doc splitting experiment draft
authorDoug Thayer <dothayer@mozilla.com>
Fri, 28 Sep 2018 08:19:17 -0700
changeset 1756585 02c1618e5bed
parent 1753802 5e7636ec12c5
child 1756586 390809808290
push id314301
push userdothayer@mozilla.com
push dateMon, 12 Nov 2018 17:35:42 +0000
treeherdertry@cb5cab7b27e6 [default view] [failures only]
milestone65.0a1
doc splitting experiment
browser/base/content/browser.xul
browser/themes/windows/browser-aero.css
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/APZUpdater.cpp
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
gfx/layers/ipc/PWebRenderBridge.ipdl
gfx/layers/ipc/SharedSurfacesChild.cpp
gfx/layers/ipc/SharedSurfacesChild.h
gfx/layers/wr/AsyncImagePipelineManager.cpp
gfx/layers/wr/AsyncImagePipelineManager.h
gfx/layers/wr/IpcResourceUpdateQueue.cpp
gfx/layers/wr/IpcResourceUpdateQueue.h
gfx/layers/wr/StackingContextHelper.cpp
gfx/layers/wr/StackingContextHelper.h
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeChild.h
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
gfx/layers/wr/WebRenderCanvasRenderer.cpp
gfx/layers/wr/WebRenderCanvasRenderer.h
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/layers/wr/WebRenderCommandBuilder.h
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/layers/wr/WebRenderLayerManager.h
gfx/layers/wr/WebRenderUserData.cpp
gfx/layers/wr/WebRenderUserData.h
gfx/thebes/gfxFontMissingGlyphs.cpp
gfx/thebes/gfxPlatform.cpp
gfx/webrender/src/display_list_flattener.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/gpu_cache.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/scene_builder.rs
gfx/webrender/src/texture_cache.rs
gfx/webrender_api/src/api.rs
gfx/webrender_bindings/RenderThread.cpp
gfx/webrender_bindings/RenderThread.h
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
ipc/glue/ByteBuf.h
layout/generic/TextDrawTarget.h
layout/generic/nsHTMLCanvasFrame.cpp
layout/generic/nsSubDocumentFrame.cpp
layout/ipc/RenderFrame.cpp
layout/painting/nsDisplayItemTypesList.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/tools/reftest/reftest.xul
layout/xul/nsBoxFrame.cpp
xpcom/ds/StaticAtoms.py
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1298,17 +1298,17 @@ xmlns="http://www.w3.org/1999/xhtml"
     </toolbarpalette>
     <box id="library-animatable-box" class="toolbarbutton-animatable-box">
       <image class="toolbarbutton-animatable-image"/>
     </box>
   </toolbox>
 
   <hbox id="fullscr-toggler" hidden="true"/>
 
-  <deck id="content-deck" flex="1">
+  <deck id="content-deck" flex="1" webrendercontent="true">
     <hbox flex="1" id="browser">
       <vbox id="browser-border-start" hidden="true" layer="true"/>
       <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
         <sidebarheader id="sidebar-header" align="center">
           <toolbarbutton id="sidebar-switcher-target" flex="1" class="tabbable">
             <image id="sidebar-icon" consumeanchor="sidebar-switcher-target"/>
             <label id="sidebar-title" crop="end" flex="1" control="sidebar"/>
             <image id="sidebar-switcher-arrow"/>
@@ -1367,10 +1367,9 @@ xmlns="http://www.w3.org/1999/xhtml"
     <html:div class="pointerlockfswarning-generic-text">
       &pointerlockWarning.generic.label;
     </html:div>
   </html:div>
 
   <vbox id="browser-bottombox" layer="true">
     <notificationbox id="global-notificationbox" notificationside="bottom"/>
   </vbox>
-
 </window>
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -55,17 +55,17 @@
 
         @media (-moz-windows-accent-color-in-titlebar: 0) {
           :root[sizemode=normal][tabsintitlebar] {
             border-top: 1px solid rgba(0,0,0,.7);
           }
           :root[sizemode=normal][tabsintitlebar][always-use-accent-color-for-window-border]:not(:-moz-window-inactive) {
             border-top-color: -moz-win-accentcolor;
           }
-          :root[tabsintitlebar]:not(:-moz-lwtheme) {
+          :root[tabsintitlebar]:not(:-moz-lwtheme) > #navigator-toolbox {
             background-color: hsl(235,33%,19%);
             color: hsl(240,9%,98%);
           }
         }
 
         @media (-moz-windows-accent-color-in-titlebar) {
           :root[sizemode=normal][tabsintitlebar] {
             border-top: 1px solid -moz-win-accentcolor;
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -598,17 +598,16 @@ APZCTreeManager::UpdateHitTestingTree(La
 }
 
 void
 APZCTreeManager::SampleForWebRender(wr::TransactionWrapper& aTxn,
                                     const TimeStamp& aSampleTime)
 {
   AssertOnSamplerThread();
   MutexAutoLock lock(mMapLock);
-
   // Sample async transforms on scrollable layers.
   for (const auto& mapping : mApzcMap) {
     AsyncPanZoomController* apzc = mapping.second;
 
     // Apply any additional async scrolling for testing purposes (used for
     // reftest-async-scroll and reftest-async-zoom).
     AutoApplyAsyncTestAttributes testAttributeApplier(apzc);
 
@@ -2520,17 +2519,17 @@ already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetAPZCAtPointWR(const ScreenPoint& aHitTestPoint,
                                   CompositorHitTestInfo* aOutHitResult,
                                   HitTestingTreeNode** aOutScrollbarNode)
 {
   MOZ_ASSERT(aOutHitResult);
   MOZ_ASSERT(aOutScrollbarNode);
 
   RefPtr<AsyncPanZoomController> result;
-  RefPtr<wr::WebRenderAPI> wr = GetWebRenderAPI();
+  RefPtr<wr::WebRenderAPI> wr = GetWebRenderAPIAtPoint(aHitTestPoint);
   if (!wr) {
     // If WebRender isn't running, fall back to the root APZC.
     // This is mostly for the benefit of GTests which do not
     // run a WebRender instance, but gracefully falling back
     // here allows those tests which are not specifically
     // testing the hit-test algorithm to still work.
     result = FindRootApzcForLayersId(mRootLayersId);
     *aOutHitResult = CompositorHitTestFlags::eVisibleToHitTest;
@@ -3119,16 +3118,43 @@ APZCTreeManager::GetWebRenderAPI() const
     [&](LayerTreeState& aState) -> void {
       if (aState.mWrBridge) {
         api = aState.mWrBridge->GetWebRenderAPI();
       }
     });
   return api.forget();
 }
 
+already_AddRefed<wr::WebRenderAPI>
+APZCTreeManager::GetContentRectWebRenderAPI() const
+{
+  RefPtr<wr::WebRenderAPI> api;
+  CompositorBridgeParent::CallWithIndirectShadowTree(mRootLayersId,
+    [&](LayerTreeState& aState) -> void {
+      if (aState.mWrBridge) {
+        api = aState.mWrBridge->GetContentRectWebRenderAPI();
+      }
+    });
+  return api.forget();
+}
+
+already_AddRefed<wr::WebRenderAPI>
+APZCTreeManager::GetWebRenderAPIAtPoint(const ScreenPoint& aPoint) const
+{
+  RefPtr<wr::WebRenderAPI> api;
+  CompositorBridgeParent::CallWithIndirectShadowTree(mRootLayersId,
+    [&](LayerTreeState& aState) -> void {
+      if (aState.mWrBridge) {
+        IntPoint point = RoundedToInt(aPoint).ToUnknownPoint();
+        api = aState.mWrBridge->GetWebRenderAPIAtPoint(point);
+      }
+    });
+  return api.forget();
+}
+
 already_AddRefed<GeckoContentController>
 APZCTreeManager::GetContentController(LayersId aLayersId) const
 {
   RefPtr<GeckoContentController> controller;
   CompositorBridgeParent::CallWithIndirectShadowTree(aLayersId,
     [&](LayerTreeState& aState) -> void {
       controller = aState.mController;
     });
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -542,16 +542,18 @@ public:
   // Assert that the current thread is the sampler thread for this APZCTM.
   void AssertOnSamplerThread();
   // Assert that the current thread is the updater thread for this APZCTM.
   void AssertOnUpdaterThread();
 
   // Returns a pointer to the WebRenderAPI for the root layers id this APZCTreeManager
   // is for. This might be null (for example, if WebRender is not enabled).
   already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() const;
+  already_AddRefed<wr::WebRenderAPI> GetContentRectWebRenderAPI() const;
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPIAtPoint(const ScreenPoint& aPoint) const;
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~APZCTreeManager();
 
   APZSampler* GetSampler() const;
   APZUpdater* GetUpdater() const;
 
--- a/gfx/layers/apz/src/APZUpdater.cpp
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -400,18 +400,20 @@ APZUpdater::RunOnUpdaterThread(LayersId 
           sendWakeMessage = false;
           break;
         }
       }
       mUpdaterQueue.push_back(QueuedTask { aLayersId, task });
     }
     if (sendWakeMessage) {
       RefPtr<wr::WebRenderAPI> api = mApz->GetWebRenderAPI();
+      RefPtr<wr::WebRenderAPI> contentRectApi = mApz->GetContentRectWebRenderAPI();
       if (api) {
         api->WakeSceneBuilder();
+        contentRectApi->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.
         NS_WARNING("Possibly dropping task posted to updater thread");
       }
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1831,19 +1831,22 @@ CompositorBridgeParent::RecvAdoptChild(c
 
   if (scheduleComposition) {
     ScheduleComposition();
   }
 
   if (childWrBridge) {
     MOZ_ASSERT(mWrBridge);
     RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+    RefPtr<wr::WebRenderAPI> contentRectApi = mWrBridge->GetContentRectWebRenderAPI();
     api = api->Clone();
+    contentRectApi = contentRectApi->Clone();
     wr::Epoch newEpoch = childWrBridge->UpdateWebRender(mWrBridge->CompositorScheduler(),
                                    api,
+                                   contentRectApi,
                                    mWrBridge->AsyncImageManager(),
                                    GetAnimationStorage(),
                                    mWrBridge->GetTextureFactoryIdentifier());
     // Pretend we composited, since parent CompositorBridgeParent was replaced.
     TimeStamp now = TimeStamp::Now();
     NotifyPipelineRendered(childWrBridge->PipelineId(), newEpoch, now, now);
   }
 
@@ -1889,28 +1892,35 @@ CompositorBridgeParent::AllocPWebRenderB
     // 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);
+  RefPtr<wr::WebRenderAPI> contentRectApi = api->CreateDocument(aSize, 1);
   if (!api) {
     mWrBridge = WebRenderBridgeParent::CreateDestroyed(aPipelineId);
     mWrBridge.get()->AddRef(); // IPDL reference
     return mWrBridge;
   }
-  mAsyncImageManager = new AsyncImagePipelineManager(api->Clone());
+  mAsyncImageManager = new AsyncImagePipelineManager(api->Clone(),
+                                                     contentRectApi->Clone());
   RefPtr<AsyncImagePipelineManager> asyncMgr = mAsyncImageManager;
   wr::TransactionBuilder txn;
   txn.SetRootPipeline(aPipelineId);
   api->SendTransaction(txn);
+  wr::TransactionBuilder contentRectTxn;
+  contentRectTxn.SetRootPipeline(aPipelineId);
+  contentRectApi->SendTransaction(contentRectTxn);
   RefPtr<CompositorAnimationStorage> animStorage = GetAnimationStorage();
-  mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, std::move(api), std::move(asyncMgr), std::move(animStorage), mVsyncRate);
+  mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr,
+                                        std::move(api), std::move(contentRectApi),
+                                        std::move(asyncMgr), std::move(animStorage), mVsyncRate);
   mWrBridge.get()->AddRef(); // IPDL reference
 
   mCompositorScheduler = mWrBridge->CompositorScheduler();
   MOZ_ASSERT(mCompositorScheduler);
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     MOZ_ASSERT(sIndirectLayerTrees[mRootLayerTreeID].mWrBridge == nullptr);
     sIndirectLayerTrees[mRootLayerTreeID].mWrBridge = mWrBridge;
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -218,34 +218,39 @@ CrossProcessCompositorBridgeParent::Allo
     MOZ_ASSERT(sIndirectLayerTrees[layersId].mWrBridge == nullptr);
     cbp = sIndirectLayerTrees[layersId].mParent;
     if (cbp) {
       root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge;
     }
   }
 
   RefPtr<wr::WebRenderAPI> api;
+  RefPtr<wr::WebRenderAPI> contentRectAPI;
   if (root) {
     api = root->GetWebRenderAPI();
+    contentRectAPI = root->GetContentRectWebRenderAPI();
   }
 
   if (!root || !api) {
     // This could happen when this function is called after CompositorBridgeParent destruction.
     // This was observed during Tab move between different windows.
     NS_WARNING(nsPrintfCString("Created child without a matching parent? root %p", root.get()).get());
     WebRenderBridgeParent* parent = WebRenderBridgeParent::CreateDestroyed(aPipelineId);
     parent->AddRef(); // IPDL reference
     return parent;
   }
 
   api = api->Clone();
+  contentRectAPI = contentRectAPI->Clone();
   RefPtr<AsyncImagePipelineManager> holder = root->AsyncImageManager();
   RefPtr<CompositorAnimationStorage> animStorage = cbp->GetAnimationStorage();
   WebRenderBridgeParent* parent = new WebRenderBridgeParent(
-          this, aPipelineId, nullptr, root->CompositorScheduler(), std::move(api), std::move(holder), std::move(animStorage), cbp->GetVsyncInterval());
+          this, aPipelineId, nullptr, root->CompositorScheduler(),
+          std::move(api), std::move(contentRectAPI),
+          std::move(holder), std::move(animStorage), cbp->GetVsyncInterval());
   parent->AddRef(); // IPDL reference
 
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     sIndirectLayerTrees[layersId].mCrossProcessParent = this;
     sIndirectLayerTrees[layersId].mWrBridge = parent;
   }
 
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -38,28 +38,51 @@ sync protocol PWebRenderBridge
 parent:
   sync EnsureConnected()
     returns (TextureFactoryIdentifier textureFactoryIdentifier, MaybeIdNamespace maybeIdNamespace);
 
   async NewCompositable(CompositableHandle handle, TextureInfo info);
   async ReleaseCompositable(CompositableHandle compositable);
 
   async DeleteCompositorAnimations(uint64_t[] aIds);
-  async SetDisplayList(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
-                       LayoutSize aContentSize, ByteBuf aDL, BuiltDisplayListDescriptor aDLDesc,
-                       WebRenderScrollData aScrollData,
-                       OpUpdateResource[] aResourceUpdates, RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems,
-                       IdNamespace aIdNamespace, bool containsSVGGroup, TimeStamp refreshStartTime, TimeStamp txnStartTime, TimeStamp fwdTime);
+  async SetDisplayList(IntRect aChromeRect,
+                       WebRenderParentCommand[] aChromeCommands,
+                       LayoutSize aChromeContentSize,
+                       ByteBuf aChromeDL,
+                       BuiltDisplayListDescriptor aChromeDLDesc,
+                       OpUpdateResource[] aChromeResourceUpdates,
+                       RefCountedShmem[] aChromeSmallShmems,
+                       Shmem[] aChromeLargeShmems,
+                       IntRect aContentRect,
+                       WebRenderParentCommand[] aContentCommands,
+                       LayoutSize aContentContentSize,
+                       ByteBuf aContentDL,
+                       BuiltDisplayListDescriptor aContentDLDesc,
+                       OpUpdateResource[] aContentResourceUpdates,
+                       RefCountedShmem[] aContentSmallShmems,
+                       Shmem[] aContentLargeShmems,
+                       OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
+                       WebRenderScrollData aScrollData, IdNamespace aIdNamespace, bool containsSVGGroup,
+                       TimeStamp refreshStartTime, TimeStamp txnStartTime, 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,
+                         WebRenderParentCommand[] aChromeCommands,
+                         OpUpdateResource[] aChromeResourceUpdates,
+                         RefCountedShmem[] aChromeSmallShmems,
+                         Shmem[] aChromeLargeShmems,
+                         WebRenderParentCommand[] aContentCommands,
+                         OpUpdateResource[] aContentResourceUpdates,
+                         RefCountedShmem[] aContentSmallShmems,
+                         Shmem[] aContentLargeShmems,
+                         OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
                          IdNamespace aIdNamespace, TimeStamp refreshStartTime, TimeStamp txnStartTime, TimeStamp fwdTime);
   async SetFocusTarget(FocusTarget focusTarget);
-  async UpdateResources(OpUpdateResource[] aResourceUpdates, RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems, bool scheduleComposite);
-  async ParentCommands(WebRenderParentCommand[] commands);
+  async UpdateResources(OpUpdateResource[] aResourceUpdates,
+                        RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems,
+                        bool aIsForContent, bool scheduleComposite);
+  async ParentCommands(WebRenderParentCommand[] commands, bool aIsForContent);
   sync GetSnapshot(PTexture texture);
   async SetLayersObserverEpoch(LayersObserverEpoch childEpoch);
   async ClearCachedResources();
   // Schedule a composite if one isn't already scheduled.
   async ScheduleComposite();
   // Save the frame capture to disk
   async Capture();
 
--- a/gfx/layers/ipc/SharedSurfacesChild.cpp
+++ b/gfx/layers/ipc/SharedSurfacesChild.cpp
@@ -16,33 +16,37 @@
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 /* static */ UserDataKey SharedSurfacesChild::sSharedKey;
 
 SharedSurfacesChild::ImageKeyData::ImageKeyData(WebRenderLayerManager* aManager,
-                                                const wr::ImageKey& aImageKey)
+                                                const wr::ImageKey& aImageKey,
+                                                bool aIsForContentRect)
   : mManager(aManager)
   , mImageKey(aImageKey)
+  , mIsForContentRect(aIsForContentRect)
 { }
 
 SharedSurfacesChild::ImageKeyData::ImageKeyData(SharedSurfacesChild::ImageKeyData&& aOther)
   : mManager(std::move(aOther.mManager))
   , mDirtyRect(std::move(aOther.mDirtyRect))
   , mImageKey(aOther.mImageKey)
+  , mIsForContentRect(aOther.mIsForContentRect)
 { }
 
 SharedSurfacesChild::ImageKeyData&
 SharedSurfacesChild::ImageKeyData::operator=(SharedSurfacesChild::ImageKeyData&& aOther)
 {
   mManager = std::move(aOther.mManager);
   mDirtyRect = std::move(aOther.mDirtyRect);
   mImageKey = aOther.mImageKey;
+  mIsForContentRect = aOther.mIsForContentRect;
   return *this;
 }
 
 SharedSurfacesChild::ImageKeyData::~ImageKeyData()
 { }
 
 void
 SharedSurfacesChild::ImageKeyData::MergeDirtyRect(const Maybe<IntRect>& aDirtyRect)
@@ -143,17 +147,17 @@ SharedSurfacesChild::SharedUserData::Upd
       // We don't have the resource update queue for this manager, so just
       // accumulate the dirty rects until it is requested.
       entry.MergeDirtyRect(aDirtyRect);
     }
   }
 
   if (!found) {
     key = aManager->WrBridge()->GetNextImageKey();
-    ImageKeyData data(aManager, key);
+    ImageKeyData data(aManager, key, aResources.IsForContentRect());
     mKeys.AppendElement(std::move(data));
     aResources.AddExternalImage(mId, key);
   }
 
   return key;
 }
 
 /* static */ void
@@ -380,17 +384,17 @@ SharedSurfacesChild::Share(SourceSurface
 SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId,
                              bool aReleaseId,
                              nsTArray<ImageKeyData>& aKeys)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   for (const auto& entry : aKeys) {
     if (!entry.mManager->IsDestroyed()) {
-      entry.mManager->AddImageKeyForDiscard(entry.mImageKey);
+      entry.mManager->AddImageKeyForDiscard(entry.mImageKey, entry.mIsForContentRect);
     }
   }
 
   if (!aReleaseId) {
     // We don't own the external image ID itself.
     return;
   }
 
@@ -479,17 +483,19 @@ SharedSurfacesAnimation::SetCurrentFrame
       mKeys.RemoveElementAt(i);
       continue;
     }
 
     entry.MergeDirtyRect(Some(aDirtyRect));
 
     Maybe<IntRect> dirtyRect = entry.TakeDirtyRect();
     if (dirtyRect) {
-      auto& resourceUpdates = entry.mManager->AsyncResourceUpdates();
+      auto& resourceUpdates = entry.mIsForContentRect ?
+        entry.mManager->AsyncResourceUpdates().ContentRectUpdateQueue() :
+        entry.mManager->AsyncResourceUpdates();
       resourceUpdates.UpdateExternalImage(mId, entry.mImageKey,
                                           ViewAs<ImagePixel>(dirtyRect.ref()));
     }
   }
 
   return NS_OK;
 }
 
--- a/gfx/layers/ipc/SharedSurfacesChild.h
+++ b/gfx/layers/ipc/SharedSurfacesChild.h
@@ -88,17 +88,18 @@ private:
   SharedSurfacesChild() = delete;
   ~SharedSurfacesChild() = delete;
 
   friend class SharedSurfacesAnimation;
 
   class ImageKeyData final {
   public:
     ImageKeyData(WebRenderLayerManager* aManager,
-                 const wr::ImageKey& aImageKey);
+                 const wr::ImageKey& aImageKey,
+                 bool aIsForContentRect);
     ~ImageKeyData();
 
     ImageKeyData(ImageKeyData&& aOther);
     ImageKeyData& operator=(ImageKeyData&& aOther);
     ImageKeyData(const ImageKeyData&) = delete;
     ImageKeyData& operator=(const ImageKeyData&) = delete;
 
     void MergeDirtyRect(const Maybe<gfx::IntRect>& aDirtyRect);
@@ -106,16 +107,17 @@ private:
     Maybe<gfx::IntRect> TakeDirtyRect()
     {
       return std::move(mDirtyRect);
     }
 
     RefPtr<WebRenderLayerManager> mManager;
     Maybe<gfx::IntRect> mDirtyRect;
     wr::ImageKey mImageKey;
+    bool mIsForContentRect;
   };
 
   class SharedUserData {
   public:
     SharedUserData()
       : mShared(false)
     { }
 
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -23,18 +23,20 @@ namespace layers {
 AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline()
  : mInitialised(false)
  , mIsChanged(false)
  , mUseExternalImage(false)
  , mFilter(wr::ImageRendering::Auto)
  , mMixBlendMode(wr::MixBlendMode::Normal)
 {}
 
-AsyncImagePipelineManager::AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi)
+AsyncImagePipelineManager::AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi,
+                                                     already_AddRefed<wr::WebRenderAPI>&& aContentRectApi)
  : mApi(aApi)
+ , mContentRectApi(aContentRectApi)
  , mIdNamespace(mApi->GetNamespace())
  , mUseTripleBuffering(mApi->GetUseTripleBuffering())
  , mResourceId(0)
  , mAsyncImageEpoch{0}
  , mWillGenerateFrame(false)
  , mDestroyed(false)
  , mUpdatesLock("UpdatesLock")
  , mUpdatesCount(0)
@@ -47,16 +49,17 @@ AsyncImagePipelineManager::~AsyncImagePi
   MOZ_COUNT_DTOR(AsyncImagePipelineManager);
 }
 
 void
 AsyncImagePipelineManager::Destroy()
 {
   MOZ_ASSERT(!mDestroyed);
   mApi = nullptr;
+  mContentRectApi = nullptr;
   mDestroyed = true;
 }
 
 void
 AsyncImagePipelineManager::SetWillGenerateFrame()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
@@ -115,26 +118,29 @@ AsyncImagePipelineManager::RemovePipelin
   MOZ_ASSERT(holder);
   if (!holder) {
     return;
   }
   holder->mDestroyedEpoch = Some(aEpoch);
 }
 
 void
-AsyncImagePipelineManager::AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost)
+AsyncImagePipelineManager::AddAsyncImagePipeline(const wr::PipelineId& aPipelineId,
+                                                 WebRenderImageHost* aImageHost,
+                                                 bool aIsForContentRect)
 {
   if (mDestroyed) {
     return;
   }
   MOZ_ASSERT(aImageHost);
   uint64_t id = wr::AsUint64(aPipelineId);
 
   MOZ_ASSERT(!mAsyncImagePipelines.Get(id));
   AsyncImagePipeline* holder = new AsyncImagePipeline();
+  holder->mIsForContentRect = aIsForContentRect;
   holder->mImageHost = aImageHost;
   mAsyncImagePipelines.Put(id, holder);
   AddPipeline(aPipelineId);
 }
 
 void
 AsyncImagePipelineManager::RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn)
 {
@@ -324,34 +330,38 @@ AsyncImagePipelineManager::UpdateWithout
 
   dSurf->Unmap();
 
   return Some(aOp);
 }
 
 void
 AsyncImagePipelineManager::ApplyAsyncImagesOfImageBridge(wr::TransactionBuilder& aSceneBuilderTxn,
-                                                         wr::TransactionBuilder& aFastTxn)
+                                                         wr::TransactionBuilder& aFastTxn,
+                                                         wr::TransactionBuilder& aContentRectSceneBuilderTxn,
+                                                         wr::TransactionBuilder& aContentRectFastTxn)
 {
   if (mDestroyed || mAsyncImagePipelines.Count() == 0) {
     return;
   }
 
   wr::Epoch epoch = GetNextImageEpoch();
 
   // We use a pipeline with a very small display list for each video element.
   // Update each of them if needed.
   for (auto iter = mAsyncImagePipelines.Iter(); !iter.Done(); iter.Next()) {
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
     AsyncImagePipeline* pipeline = iter.Data();
     // If aync image pipeline does not use ImageBridge, do not need to apply.
     if (!pipeline->mImageHost->GetAsyncRef()) {
       continue;
     }
-    ApplyAsyncImageForPipeline(epoch, pipelineId, pipeline, aSceneBuilderTxn, aFastTxn);
+    ApplyAsyncImageForPipeline(epoch, pipelineId, pipeline,
+                               pipeline->mIsForContentRect ? aContentRectSceneBuilderTxn : aSceneBuilderTxn,
+                               pipeline->mIsForContentRect ? aContentRectFastTxn : aFastTxn);
   }
 }
 
 void
 AsyncImagePipelineManager::ApplyAsyncImageForPipeline(const wr::Epoch& aEpoch,
                                                       const wr::PipelineId& aPipelineId,
                                                       AsyncImagePipeline* aPipeline,
                                                       wr::TransactionBuilder& aSceneBuilderTxn,
@@ -407,16 +417,17 @@ AsyncImagePipelineManager::ApplyAsyncIma
     if (aPipeline->mUseExternalImage) {
       MOZ_ASSERT(aPipeline->mCurrentTexture->AsWebRenderTextureHost());
       Range<wr::ImageKey> range_keys(&keys[0], keys.Length());
       aPipeline->mCurrentTexture->PushDisplayItems(builder,
                                                   wr::ToRoundedLayoutRect(rect),
                                                   wr::ToRoundedLayoutRect(rect),
                                                   aPipeline->mFilter,
                                                   range_keys);
+
       HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture->AsWebRenderTextureHost());
     } else {
       MOZ_ASSERT(keys.Length() == 1);
       builder.PushImage(wr::ToRoundedLayoutRect(rect),
                         wr::ToRoundedLayoutRect(rect),
                         true,
                         aPipeline->mFilter,
                         keys[0]);
@@ -434,24 +445,26 @@ AsyncImagePipelineManager::ApplyAsyncIma
     LayerSize(aPipeline->mScBounds.Width(), aPipeline->mScBounds.Height()),
     aPipelineId, builderContentSize,
     dl.dl_desc, dl.dl);
 }
 
 void
 AsyncImagePipelineManager::ApplyAsyncImageForPipeline(const wr::PipelineId& aPipelineId,
                                                       wr::TransactionBuilder& aTxn,
-                                                      wr::TransactionBuilder& aTxnForImageBridge)
+                                                      wr::TransactionBuilder& aTxnForImageBridge,
+                                                      bool aIsForContentRect)
 {
   AsyncImagePipeline* pipeline = mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
   if (!pipeline) {
     return;
   }
+  wr::WebRenderAPI* api = pipeline->mIsForContentRect ? mContentRectApi : mApi;
   wr::TransactionBuilder fastTxn(/* aUseSceneBuilderThread */ false);
-  wr::AutoTransactionSender sender(mApi, &fastTxn);
+  wr::AutoTransactionSender sender(api, &fastTxn);
 
   // Transaction for async image pipeline that uses ImageBridge always need to be non low priority.
   auto& sceneBuilderTxn = pipeline->mImageHost->GetAsyncRef() ? aTxnForImageBridge : aTxn;
 
   // Use transaction of using non scene builder thread when ImageHost uses ImageBridge.
   // ApplyAsyncImagesOfImageBridge() handles transaction of adding and updating
   // wr::ImageKeys of ImageHosts that uses ImageBridge. Then AsyncImagePipelineManager
   // always needs to use non scene builder thread transaction for adding and updating
@@ -656,16 +669,17 @@ AsyncImagePipelineManager::ProcessPipeli
         break;
       }
       holder->mTextureHostWrappers.pop();
     }
     while (!holder->mExternalImages.empty()) {
       if (aEpoch <= holder->mExternalImages.front().mEpoch) {
         break;
       }
+
       DebugOnly<bool> released =
         SharedSurfacesParent::Release(holder->mExternalImages.front().mImageId);
       MOZ_ASSERT(released);
       holder->mExternalImages.pop();
     }
   }
 }
 
--- a/gfx/layers/wr/AsyncImagePipelineManager.h
+++ b/gfx/layers/wr/AsyncImagePipelineManager.h
@@ -33,17 +33,18 @@ class WebRenderImageHost;
 class WebRenderTextureHost;
 class WebRenderTextureHostWrapper;
 
 class AsyncImagePipelineManager final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncImagePipelineManager)
 
-  explicit AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi);
+  explicit AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi,
+                                     already_AddRefed<wr::WebRenderAPI>&& aContentRectApi);
 
 protected:
   ~AsyncImagePipelineManager();
 
 public:
   void Destroy();
 
   void AddPipeline(const wr::PipelineId& aPipelineId);
@@ -77,28 +78,35 @@ public:
         mCompositeUntilTime < aTimeStamp) {
       mCompositeUntilTime = aTimeStamp;
     }
   }
   TimeStamp GetCompositeUntilTime() const {
     return mCompositeUntilTime;
   }
 
-  void AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost);
+  void AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost,
+                             bool aIsForContentRect);
   void RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId,
                                 wr::TransactionBuilder& aTxn);
 
   void UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
                                 const LayoutDeviceRect& aScBounds,
                                 const gfx::Matrix4x4& aScTransform,
                                 const gfx::MaybeIntSize& aScaleToSize,
                                 const wr::ImageRendering& aFilter,
                                 const wr::MixBlendMode& aMixBlendMode);
-  void ApplyAsyncImagesOfImageBridge(wr::TransactionBuilder& aSceneBuilderTxn, wr::TransactionBuilder& aFastTxn);
-  void ApplyAsyncImageForPipeline(const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn, wr::TransactionBuilder& aTxnForImageBridge);
+  void ApplyAsyncImagesOfImageBridge(wr::TransactionBuilder& aSceneBuilderTxn,
+                                     wr::TransactionBuilder& aFastTxn,
+                                     wr::TransactionBuilder& aContentRectSceneBuilderTxn,
+                                     wr::TransactionBuilder& aContentRectFastTxn);
+  void ApplyAsyncImageForPipeline(const wr::PipelineId& aPipelineId,
+                                  wr::TransactionBuilder& aTxn,
+                                  wr::TransactionBuilder& aTxnForImageBridge,
+                                  bool aIsForContentRect);
 
   void SetEmptyDisplayList(const wr::PipelineId& aPipelineId,
                            wr::TransactionBuilder& aTxn,
                            wr::TransactionBuilder& aTxnForImageBridge);
 
   void AppendImageCompositeNotification(const ImageCompositeNotificationInfo& aNotification)
   {
     mImageCompositeNotifications.AppendElement(aNotification);
@@ -115,17 +123,20 @@ public:
   wr::ExternalImageId GetNextExternalImageId();
 
 private:
   void ProcessPipelineRendered(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, const uint64_t aUpdatesCount);
   void ProcessPipelineRemoved(const wr::PipelineId& aPipelineId, const uint64_t aUpdatesCount);
 
   wr::Epoch GetNextImageEpoch();
   uint32_t GetNextResourceId() { return ++mResourceId; }
-  wr::IdNamespace GetNamespace() { return mIdNamespace; }
+  wr::IdNamespace GetNamespace()
+  {
+    return mIdNamespace;
+  }
   wr::ImageKey GenerateImageKey()
   {
     wr::ImageKey key;
     key.mNamespace = GetNamespace();
     key.mHandle = GetNextResourceId();
     return key;
   }
 
@@ -180,16 +191,17 @@ private:
       mScBounds = aScBounds;
       mScTransform = aScTransform;
       mScaleToSize = aScaleToSize;
       mFilter = aFilter;
       mMixBlendMode = aMixBlendMode;
     }
 
     bool mInitialised;
+    bool mIsForContentRect;
     bool mIsChanged;
     bool mUseExternalImage;
     LayoutDeviceRect mScBounds;
     gfx::Matrix4x4 mScTransform;
     gfx::MaybeIntSize mScaleToSize;
     wr::ImageRendering mFilter;
     wr::MixBlendMode mMixBlendMode;
     RefPtr<WebRenderImageHost> mImageHost;
@@ -216,16 +228,17 @@ private:
                              TextureHost::ResourceUpdateOp,
                              wr::TransactionBuilder& aTxn);
 
   // If texture is direct binding texture, keep it until it is not used by GPU.
   void HoldUntilNotUsedByGPU(const CompositableTextureHostRef& aTextureHost, uint64_t aUpdatesCount);
   void CheckForTextureHostsNotUsedByGPU();
 
   RefPtr<wr::WebRenderAPI> mApi;
+  RefPtr<wr::WebRenderAPI> mContentRectApi;
   const wr::IdNamespace mIdNamespace;
   const bool mUseTripleBuffering;
   uint32_t mResourceId;
 
   nsClassHashtable<nsUint64HashKey, PipelineTexturesHolder> mPipelineTexturesHolders;
   nsClassHashtable<nsUint64HashKey, AsyncImagePipeline> mAsyncImagePipelines;
   wr::Epoch mAsyncImageEpoch;
   bool mWillGenerateFrame;
--- a/gfx/layers/wr/IpcResourceUpdateQueue.cpp
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.cpp
@@ -266,16 +266,17 @@ ShmSegmentsReader::Read(const layers::Of
   }
 
   return aInto.Length() - initialLength == aRange.length();
 }
 
 IpcResourceUpdateQueue::IpcResourceUpdateQueue(layers::WebRenderBridgeChild* aAllocator,
                                                size_t aChunkSize)
 : mWriter(aAllocator, aChunkSize)
+, mIsForContentRect(false)
 {}
 
 IpcResourceUpdateQueue::IpcResourceUpdateQueue(IpcResourceUpdateQueue&& aOther) noexcept
   : mWriter(std::move(aOther.mWriter))
   , mUpdates(std::move(aOther.mUpdates))
 { }
 
 IpcResourceUpdateQueue&
--- a/gfx/layers/wr/IpcResourceUpdateQueue.h
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.h
@@ -39,16 +39,17 @@ public:
   }
 
   void Flush(nsTArray<layers::RefCountedShmem>& aSmallAllocs, nsTArray<mozilla::ipc::Shmem>& aLargeAllocs);
 
   void Clear();
   bool IsEmpty() const;
 
   layers::WebRenderBridgeChild* WrBridge() const { return mShmAllocator; }
+  size_t ChunkSize() const { return mChunkSize; }
 
 protected:
   bool AllocChunk();
   layers::OffsetRange AllocLargeChunk(size_t aSize);
 
   nsTArray<layers::RefCountedShmem> mSmallAllocs;
   nsTArray<mozilla::ipc::Shmem> mLargeAllocs;
   layers::WebRenderBridgeChild* mShmAllocator;
@@ -75,16 +76,33 @@ 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);
+  IpcResourceUpdateQueue& ContentRectUpdateQueue() {
+    MOZ_ASSERT(!mIsForContentRect);
+    if (!mContentRectUpdateQueue) {
+      mContentRectUpdateQueue = MakeUnique<IpcResourceUpdateQueue>(mWriter.WrBridge(),
+                                                                   mWriter.ChunkSize());
+      mContentRectUpdateQueue->mIsForContentRect = true;
+    }
+    return *mContentRectUpdateQueue;
+  }
+
+  bool HasContentRectUpdateQueue() {
+    return !!mContentRectUpdateQueue;
+  }
+
+  bool IsForContentRect() {
+    return mIsForContentRect;
+  }
 
   IpcResourceUpdateQueue(IpcResourceUpdateQueue&& aOther) noexcept;
   IpcResourceUpdateQueue& operator=(IpcResourceUpdateQueue&& aOther) noexcept;
 
   IpcResourceUpdateQueue(const IpcResourceUpdateQueue& aOther) = delete;
   IpcResourceUpdateQueue& operator=(const IpcResourceUpdateQueue& aOther) = delete;
 
   bool AddImage(wr::ImageKey aKey,
@@ -142,14 +160,16 @@ public:
 
   bool IsEmpty() const;
 
   static void ReleaseShmems(mozilla::ipc::IProtocol*, nsTArray<layers::RefCountedShmem>& aShms);
   static void ReleaseShmems(mozilla::ipc::IProtocol*, nsTArray<mozilla::ipc::Shmem>& aShms);
 protected:
   ShmSegmentsWriter mWriter;
   nsTArray<layers::OpUpdateResource> mUpdates;
+  UniquePtr<IpcResourceUpdateQueue> mContentRectUpdateQueue;
+  bool mIsForContentRect;
 };
 
 } // namespace
 } // namespace
 
 #endif
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -94,16 +94,35 @@ StackingContextHelper::StackingContextHe
       mDeferredAncestorTransform = aParentSC.GetDeferredTransformMatrix();
     } else {
       // We are not deferring another transform, so we can just inherit the
       // parent stacking context's deferred data without any modification.
       mDeferredTransformItem = aParentSC.mDeferredTransformItem;
       mDeferredAncestorTransform = aParentSC.mDeferredAncestorTransform;
     }
   }
+
+  if (aParentSC.mContentRectHelper) {
+    mContentRectHelper = MakeUnique<StackingContextHelper>(*aParentSC.mContentRectHelper,
+                                                           aAsr,
+                                                           *aParentSC.mContentRectHelper->mBuilder,
+                                                           aFilters,
+                                                           aBounds,
+                                                           aBoundTransform,
+                                                           aAnimation,
+                                                           aOpacityPtr,
+                                                           aTransformPtr,
+                                                           aPerspectivePtr,
+                                                           aMixBlendMode,
+                                                           aBackfaceVisible,
+                                                           aIsPreserve3D,
+                                                           aDeferredTransformItem,
+                                                           aClipNodeId,
+                                                           aAnimated);
+  }
 }
 
 StackingContextHelper::~StackingContextHelper()
 {
   if (mBuilder) {
     mBuilder->PopStackingContext(mReferenceFrameId.isSome());
   }
 }
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -20,17 +20,17 @@ namespace mozilla {
 struct ActiveScrolledRoot;
 
 namespace layers {
 
 /**
  * This is a helper class that pushes/pops a stacking context, and manages
  * some of the coordinate space transformations needed.
  */
-class MOZ_RAII StackingContextHelper
+class StackingContextHelper
 {
 public:
   StackingContextHelper(const StackingContextHelper& aParentSC,
                         const ActiveScrolledRoot* aAsr,
                         wr::DisplayListBuilder& aBuilder,
                         const nsTArray<wr::WrFilterOp>& aFilters = nsTArray<wr::WrFilterOp>(),
                         const LayoutDeviceRect& aBounds = LayoutDeviceRect(),
                         const gfx::Matrix4x4* aBoundTransform = nullptr,
@@ -51,16 +51,31 @@ public:
   StackingContextHelper();
 
   // Pops the stacking context, if one was pushed during the constructor.
   ~StackingContextHelper();
 
   // Export the inherited scale
   gfx::Size GetInheritedScale() const { return mScale; }
 
+  void CreateContentRectHelper(const StackingContextHelper& aParentSC,
+                               const nsTArray<wr::WrFilterOp>& aFilters)
+  {
+    MOZ_ASSERT(!mContentRectHelper);
+    mContentRectHelper = MakeUnique<StackingContextHelper>(aParentSC,
+                                                           nullptr,
+                                                           mBuilder->ContentRectBuilder(),
+                                                           aFilters);
+  }
+
+  const StackingContextHelper& ContentRectHelper() const
+  {
+    return *mContentRectHelper;
+  }
+
   const gfx::Matrix& GetInheritedTransform() const
   {
     return mInheritedTransform;
   }
 
   const gfx::Matrix& GetSnappingSurfaceTransform() const
   {
     return mSnappingSurfaceTransform;
@@ -123,14 +138,15 @@ private:
   // in the face of async scrolling). This behaviour of forcing a
   // WebRenderLayerScrollData item to be generated when the ASR changes is
   // implemented in WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList.
   Maybe<nsDisplayTransform*> mDeferredTransformItem;
   Maybe<gfx::Matrix4x4> mDeferredAncestorTransform;
 
   bool mIsPreserve3D;
   bool mRasterizeLocally;
+  UniquePtr<StackingContextHelper> mContentRectHelper;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_STACKINGCONTEXTHELPER_H */
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -76,32 +76,38 @@ WebRenderBridgeChild::DoDestroy()
   // When this function is called from CompositorBridgeChild::Destroy().
   // mActiveResourceTracker is not cleared here, since it is
   // used by PersistentBufferProviderShared.
   mDestroyed = true;
   mManager = nullptr;
 }
 
 void
-WebRenderBridgeChild::AddWebRenderParentCommand(const WebRenderParentCommand& aCmd)
+WebRenderBridgeChild::AddWebRenderParentCommand(const WebRenderParentCommand& aCmd,
+                                                bool aIsForContentRect)
 {
-  mParentCommands.AppendElement(aCmd);
+  if (aIsForContentRect) {
+    mContentRectParentCommands.AppendElement(aCmd);
+  } else {
+    mParentCommands.AppendElement(aCmd);
+  }
 }
 
 void
 WebRenderBridgeChild::BeginTransaction()
 {
   MOZ_ASSERT(!mDestroyed);
 
   UpdateFwdTransactionId();
   mIsInTransaction = true;
 }
 
 void
 WebRenderBridgeChild::UpdateResources(wr::IpcResourceUpdateQueue& aResources,
+                                      bool aIsForContentRect,
                                       bool aScheduleComposite /* = false */)
 {
   if (!IPCOpen()) {
     aResources.Clear();
     return;
   }
 
   if (aResources.IsEmpty()) {
@@ -109,141 +115,198 @@ WebRenderBridgeChild::UpdateResources(wr
   }
 
   nsTArray<OpUpdateResource> resourceUpdates;
   nsTArray<RefCountedShmem> smallShmems;
   nsTArray<ipc::Shmem> largeShmems;
   aResources.Flush(resourceUpdates, smallShmems, largeShmems);
 
   this->SendUpdateResources(resourceUpdates, smallShmems,
-                            largeShmems, aScheduleComposite);
+                            largeShmems, aIsForContentRect, aScheduleComposite);
 }
 
 void
-WebRenderBridgeChild::EndTransaction(const wr::LayoutSize& aContentSize,
-                                     wr::BuiltDisplayList& aDL,
-                                     wr::IpcResourceUpdateQueue& aResources,
-                                     const gfx::IntSize& aSize,
+WebRenderBridgeChild::EndTransaction(const wr::LayoutSize& aChromeContentSize,
+                                     wr::BuiltDisplayList& aChromeDL,
+                                     wr::IpcResourceUpdateQueue& aChromeResources,
+                                     const gfx::IntRect& aChromeRect,
+                                     const wr::LayoutSize& aContentContentSize,
+                                     wr::BuiltDisplayList& aContentDL,
+                                     wr::IpcResourceUpdateQueue& aContentResources,
+                                     const gfx::IntRect& aContentRect,
                                      TransactionId aTransactionId,
                                      const WebRenderScrollData& aScrollData,
                                      bool aContainsSVGGroup,
                                      const mozilla::TimeStamp& aRefreshStartTime,
                                      const mozilla::TimeStamp& aTxnStartTime)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(mIsInTransaction);
 
-  ByteBuf dlData(aDL.dl.inner.data, aDL.dl.inner.length, aDL.dl.inner.capacity);
-  aDL.dl.inner.capacity = 0;
-  aDL.dl.inner.data = nullptr;
+  ByteBuf chromeDLData(aChromeDL.dl.inner.data, aChromeDL.dl.inner.length, aChromeDL.dl.inner.capacity);
+  aChromeDL.dl.inner.capacity = 0;
+  aChromeDL.dl.inner.data = nullptr;
+  ByteBuf contentDLData(aContentDL.dl.inner.data, aContentDL.dl.inner.length, aContentDL.dl.inner.capacity);
+  aContentDL.dl.inner.capacity = 0;
+  aContentDL.dl.inner.data = nullptr;
 
   TimeStamp fwdTime;
 #if defined(ENABLE_FRAME_LATENCY_LOG)
   fwdTime = TimeStamp::Now();
 #endif
 
-  nsTArray<OpUpdateResource> resourceUpdates;
-  nsTArray<RefCountedShmem> smallShmems;
-  nsTArray<ipc::Shmem> largeShmems;
-  aResources.Flush(resourceUpdates, smallShmems, largeShmems);
+  nsTArray<OpUpdateResource> chromeResourceUpdates;
+  nsTArray<RefCountedShmem> chromeSmallShmems;
+  nsTArray<ipc::Shmem> chromeLargeShmems;
+  aChromeResources.Flush(chromeResourceUpdates, chromeSmallShmems, chromeLargeShmems);
+  nsTArray<OpUpdateResource> contentResourceUpdates;
+  nsTArray<RefCountedShmem> contentSmallShmems;
+  nsTArray<ipc::Shmem> contentLargeShmems;
+  aContentResources.Flush(contentResourceUpdates, contentSmallShmems, contentLargeShmems);
 
-  this->SendSetDisplayList(aSize, mParentCommands, mDestroyedActors,
-                           GetFwdTransactionId(), aTransactionId,
-                           aContentSize, dlData, aDL.dl_desc, aScrollData,
-                           resourceUpdates, smallShmems, largeShmems,
-                           mIdNamespace, aContainsSVGGroup, aRefreshStartTime, aTxnStartTime, fwdTime);
+  this->SendSetDisplayList(aChromeRect,
+                           mParentCommands,
+                           aChromeContentSize,
+                           chromeDLData,
+                           aChromeDL.dl_desc,
+                           chromeResourceUpdates,
+                           chromeSmallShmems,
+                           chromeLargeShmems,
+                           aContentRect,
+                           mContentRectParentCommands,
+                           aContentContentSize,
+                           contentDLData,
+                           aContentDL.dl_desc,
+                           contentResourceUpdates,
+                           contentSmallShmems,
+                           contentLargeShmems,
+                           mDestroyedActors,
+                           GetFwdTransactionId(),
+                           aTransactionId,
+                           aScrollData,
+                           mIdNamespace,
+                           aContainsSVGGroup,
+                           aRefreshStartTime,
+                           aTxnStartTime,
+                           fwdTime);
 
   mParentCommands.Clear();
+  mContentRectParentCommands.Clear();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
 void
 WebRenderBridgeChild::EndEmptyTransaction(const FocusTarget& aFocusTarget,
                                           const ScrollUpdatesMap& aUpdates,
-                                          Maybe<wr::IpcResourceUpdateQueue>& aResources,
+                                          Maybe<wr::IpcResourceUpdateQueue>& aChromeResources,
                                           uint32_t aPaintSequenceNumber,
                                           TransactionId aTransactionId,
                                           const mozilla::TimeStamp& aRefreshStartTime,
                                           const mozilla::TimeStamp& aTxnStartTime)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(mIsInTransaction);
 
   TimeStamp fwdTime;
 #if defined(ENABLE_FRAME_LATENCY_LOG)
   fwdTime = TimeStamp::Now();
 #endif
 
-  nsTArray<OpUpdateResource> resourceUpdates;
-  nsTArray<RefCountedShmem> smallShmems;
-  nsTArray<ipc::Shmem> largeShmems;
-  if (aResources) {
-    aResources->Flush(resourceUpdates, smallShmems, largeShmems);
-    aResources.reset();
+  nsTArray<OpUpdateResource> chromeResourceUpdates;
+  nsTArray<RefCountedShmem> chromeSmallShmems;
+  nsTArray<ipc::Shmem> chromeLargeShmems;
+  if (aChromeResources) {
+    aChromeResources->Flush(chromeResourceUpdates, chromeSmallShmems, chromeLargeShmems);
+  }
+
+  nsTArray<OpUpdateResource> contentResourceUpdates;
+  nsTArray<RefCountedShmem> contentSmallShmems;
+  nsTArray<ipc::Shmem> contentLargeShmems;
+  if (aChromeResources && aChromeResources->HasContentRectUpdateQueue()) {
+    aChromeResources->ContentRectUpdateQueue().Flush(contentResourceUpdates, contentSmallShmems, contentLargeShmems);
+  }
+
+  if (aChromeResources) {
+    aChromeResources.reset();
   }
 
   this->SendEmptyTransaction(aFocusTarget, aUpdates, aPaintSequenceNumber,
-                             mParentCommands, mDestroyedActors,
+                             mParentCommands,
+                             chromeResourceUpdates, chromeSmallShmems, chromeLargeShmems,
+                             mContentRectParentCommands,
+                             contentResourceUpdates, contentSmallShmems, contentLargeShmems,
+                             mDestroyedActors,
                              GetFwdTransactionId(), aTransactionId,
-                             resourceUpdates, smallShmems, largeShmems,
                              mIdNamespace, aRefreshStartTime, aTxnStartTime, fwdTime);
   mParentCommands.Clear();
+  mContentRectParentCommands.Clear();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
 void
 WebRenderBridgeChild::ProcessWebRenderParentCommands()
 {
   MOZ_ASSERT(!mDestroyed);
 
-  if (mParentCommands.IsEmpty()) {
-    return;
+  if (!mParentCommands.IsEmpty()) {
+    this->SendParentCommands(mParentCommands, false);
+    mParentCommands.Clear();
   }
-  this->SendParentCommands(mParentCommands);
-  mParentCommands.Clear();
+
+  if (!mContentRectParentCommands.IsEmpty()) {
+    this->SendParentCommands(mContentRectParentCommands, true);
+    mContentRectParentCommands.Clear();
+  }
 }
 
 void
 WebRenderBridgeChild::AddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId,
-                                                        const CompositableHandle& aHandle)
+                                                        const CompositableHandle& aHandle,
+                                                        bool aIsForContentRect)
 {
   AddWebRenderParentCommand(
-    OpAddPipelineIdForCompositable(aPipelineId, aHandle, /* isAsync */ true));
+    OpAddPipelineIdForCompositable(aPipelineId, aHandle, /* isAsync */ true),
+    aIsForContentRect);
 }
 
 void
 WebRenderBridgeChild::AddPipelineIdForCompositable(const wr::PipelineId& aPipelineId,
-                                                   const CompositableHandle& aHandle)
+                                                   const CompositableHandle& aHandle,
+                                                   bool aIsForContentRect)
 {
   AddWebRenderParentCommand(
-    OpAddPipelineIdForCompositable(aPipelineId, aHandle, /* isAsync */ false));
+    OpAddPipelineIdForCompositable(aPipelineId, aHandle, /* isAsync */ false),
+    aIsForContentRect);
 }
 
 void
-WebRenderBridgeChild::RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId)
+WebRenderBridgeChild::RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId,
+                                                      bool aIsForContentRect)
 {
   AddWebRenderParentCommand(
-    OpRemovePipelineIdForCompositable(aPipelineId));
+    OpRemovePipelineIdForCompositable(aPipelineId),
+    aIsForContentRect);
 }
 
 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));
+    OpReleaseTextureOfImage(aKey),
+    false);
 }
 
 struct FontFileDataSink
 {
   wr::FontKey* mFontKey;
   WebRenderBridgeChild* mWrBridge;
   wr::IpcResourceUpdateQueue* mResources;
 };
@@ -273,73 +336,76 @@ WriteFontDescriptor(const uint8_t* aData
 void
 WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, Range<const wr::GlyphInstance> aGlyphs,
                                  gfx::ScaledFont* aFont, const wr::ColorF& aColor, const StackingContextHelper& aSc,
                                  const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aBackfaceVisible,
                                  const wr::GlyphOptions* aGlyphOptions)
 {
   MOZ_ASSERT(aFont);
 
-  wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont);
+  wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont, aBuilder.IsContentRectBuilder());
   MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle);
 
   aBuilder.PushText(aBounds,
                     aClip,
                     aBackfaceVisible,
                     aColor,
                     key,
                     aGlyphs,
                     aGlyphOptions);
 }
 
 wr::FontInstanceKey
 WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont,
+                                              bool aIsForContentRect,
                                               wr::IpcResourceUpdateQueue* aResources)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(aScaledFont);
   MOZ_ASSERT(aScaledFont->CanSerialize());
 
   wr::FontInstanceKey instanceKey = { wr::IdNamespace { 0 }, 0 };
   if (mFontInstanceKeys.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(),
+                                                  aIsForContentRect, aResources);
   wr::FontKey nullKey = { wr::IdNamespace { 0 }, 0};
   if (fontKey == nullKey) {
     return instanceKey;
   }
 
   instanceKey = GetNextFontInstanceKey();
 
   Maybe<wr::FontInstanceOptions> options;
   Maybe<wr::FontInstancePlatformOptions> platformOptions;
   std::vector<FontVariation> variations;
   aScaledFont->GetWRFontInstanceOptions(&options, &platformOptions, &variations);
 
   aResources->AddFontInstance(instanceKey, fontKey, aScaledFont->GetSize(),
                               options.ptrOr(nullptr), platformOptions.ptrOr(nullptr),
                               Range<const FontVariation>(variations.data(), variations.size()));
   if (resources.isSome()) {
-    UpdateResources(resources.ref());
+    UpdateResources(resources.ref(), aIsForContentRect);
   }
 
   mFontInstanceKeys.Put(aScaledFont, instanceKey);
 
   return instanceKey;
 
 }
 
 wr::FontKey
 WebRenderBridgeChild::GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaled,
+                                                bool aIsForContentRect,
                                                 wr::IpcResourceUpdateQueue* aResources)
 {
   MOZ_ASSERT(!mDestroyed);
 
   wr::FontKey fontKey = { wr::IdNamespace { 0 }, 0};
   if (!mFontKeys.Get(aUnscaled, &fontKey)) {
     Maybe<wr::IpcResourceUpdateQueue> resources =
       aResources ? Nothing() : Some(wr::IpcResourceUpdateQueue(this));
@@ -350,17 +416,17 @@ WebRenderBridgeChild::GetFontKeyForUnsca
     // 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(), aIsForContentRect);
     }
 
     mFontKeys.Put(aUnscaled, fontKey);
   }
 
   return fontKey;
 }
 
@@ -496,17 +562,18 @@ WebRenderBridgeChild::RemoveTextureFromC
   if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) {
     // We don't have an actor anymore, don't try to use it!
     return;
   }
 
   AddWebRenderParentCommand(
     CompositableOperation(
       aCompositable->GetIPCHandle(),
-      OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
+      OpRemoveTexture(nullptr, aTexture->GetIPDLActor())),
+    false);
 }
 
 void
 WebRenderBridgeChild::UseTextures(CompositableClient* aCompositable,
                                   const nsTArray<TimedTextureClient>& aTextures)
 {
   MOZ_ASSERT(aCompositable);
 
@@ -524,17 +591,18 @@ WebRenderBridgeChild::UseTextures(Compos
 
     textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
                                         t.mTimeStamp, t.mPictureRect,
                                         t.mFrameID, t.mProducerID,
                                         readLocked));
     GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
   }
   AddWebRenderParentCommand(CompositableOperation(aCompositable->GetIPCHandle(),
-                                            OpUseTexture(textures)));
+                                                  OpUseTexture(textures)),
+                            false);
 }
 
 void
 WebRenderBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable,
                                                 TextureClient* aClientOnBlack,
                                                 TextureClient* aClientOnWhite)
 {
 
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -59,25 +59,31 @@ typedef ThreadSafeWeakPtrHashKey<gfx::Sc
 class WebRenderBridgeChild final : public PWebRenderBridgeChild
                                  , public CompositableForwarder
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderBridgeChild, override)
 
 public:
   explicit WebRenderBridgeChild(const wr::PipelineId& aPipelineId);
 
-  void AddWebRenderParentCommand(const WebRenderParentCommand& aCmd);
+  void AddWebRenderParentCommand(const WebRenderParentCommand& aCmd,
+                                 bool aIsForContentRect);
 
   void UpdateResources(wr::IpcResourceUpdateQueue& aResources,
+                       bool aIsForContentRect,
                        bool aScheduleComposite = false);
   void BeginTransaction();
-  void EndTransaction(const wr::LayoutSize& aContentSize,
-                      wr::BuiltDisplayList& dl,
-                      wr::IpcResourceUpdateQueue& aResources,
-                      const gfx::IntSize& aSize,
+  void EndTransaction(const wr::LayoutSize& aChromeContentSize,
+                      wr::BuiltDisplayList& aChromeDL,
+                      wr::IpcResourceUpdateQueue& aChromeResources,
+                      const gfx::IntRect& aChromeRect,
+                      const wr::LayoutSize& aContentContentSize,
+                      wr::BuiltDisplayList& aContentDL,
+                      wr::IpcResourceUpdateQueue& aContentResources,
+                      const gfx::IntRect& aContentRect,
                       TransactionId aTransactionId,
                       const WebRenderScrollData& aScrollData,
                       bool aContainsSVGroup,
                       const mozilla::TimeStamp& aRefreshStartTime,
                       const mozilla::TimeStamp& aTxnStartTime);
   void EndEmptyTransaction(const FocusTarget& aFocusTarget,
                            const ScrollUpdatesMap& aUpdates,
                            Maybe<wr::IpcResourceUpdateQueue>& aResources,
@@ -93,20 +99,23 @@ public:
 
   // KnowsCompositor
   TextureForwarder* GetTextureForwarder() override;
   LayersIPCActor* GetLayersIPCActor() override;
   void SyncWithCompositor() override;
   ActiveResourceTracker* GetActiveResourceTracker() override { return mActiveResourceTracker.get(); }
 
   void AddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId,
-                                         const CompositableHandle& aHandlee);
+                                         const CompositableHandle& aHandlee,
+                                         bool aIsForContentRect);
   void AddPipelineIdForCompositable(const wr::PipelineId& aPipelineId,
-                                    const CompositableHandle& aHandlee);
-  void RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId);
+                                    const CompositableHandle& aHandlee,
+                                    bool aIsForContentRect);
+  void RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId,
+                                       bool aIsForContentRect);
 
   /// Release TextureClient that is bounded to ImageKey.
   /// It is used for recycling TextureClient.
   void ReleaseTextureOfImage(const wr::ImageKey& aKey);
 
   /**
    * Clean this up, finishing with SendShutDown() which will cause __delete__
    * to be sent from the parent side.
@@ -140,18 +149,20 @@ public:
   void PushGlyphs(wr::DisplayListBuilder& aBuilder, Range<const wr::GlyphInstance> aGlyphs,
                   gfx::ScaledFont* aFont, const wr::ColorF& aColor,
                   const StackingContextHelper& aSc,
                   const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
                   bool aBackfaceVisible,
                   const wr::GlyphOptions* aGlyphOptions = nullptr);
 
   wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont,
+                                              bool aIsForContentRect,
                                               wr::IpcResourceUpdateQueue* aResources = nullptr);
   wr::FontKey GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaledFont,
+                                        bool aIsForContentRect,
                                         wr::IpcResourceUpdateQueue* aResources = nullptr);
   void RemoveExpiredFontKeys(wr::IpcResourceUpdateQueue& aResources);
 
   void BeginClearCachedResources();
   void EndClearCachedResources();
 
   void SetWebRenderLayerManager(WebRenderLayerManager* aManager);
 
@@ -219,16 +230,17 @@ private:
     MOZ_ASSERT(mIPCOpen == true);
     mIPCOpen = false;
     Release();
   }
 
   bool AddOpDestroy(const OpDestroy& aOp);
 
   nsTArray<WebRenderParentCommand> mParentCommands;
+  nsTArray<WebRenderParentCommand> mContentRectParentCommands;
   nsTArray<OpDestroy> mDestroyedActors;
   nsDataHashtable<nsUint64HashKey, CompositableClient*> mCompositables;
   bool mIsInTransaction;
   bool mIsInClearCachedResources;
   wr::IdNamespace mIdNamespace;
   uint32_t mResourceId;
   wr::PipelineId mPipelineId;
   WebRenderLayerManager* mManager;
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -233,31 +233,34 @@ private:
   InfallibleTArray<OpDestroy>* mActorsToDestroy;
 };
 
 WebRenderBridgeParent::WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
                                              const wr::PipelineId& aPipelineId,
                                              widget::CompositorWidget* aWidget,
                                              CompositorVsyncScheduler* aScheduler,
                                              RefPtr<wr::WebRenderAPI>&& aApi,
+                                             RefPtr<wr::WebRenderAPI>&& aContentRectApi,
                                              RefPtr<AsyncImagePipelineManager>&& aImageMgr,
                                              RefPtr<CompositorAnimationStorage>&& aAnimStorage,
                                              TimeDuration aVsyncRate)
   : mCompositorBridge(aCompositorBridge)
   , mPipelineId(aPipelineId)
   , mWidget(aWidget)
   , mApi(aApi)
+  , mContentRectApi(aContentRectApi)
   , mAsyncImageManager(aImageMgr)
   , mCompositorScheduler(aScheduler)
   , mAnimStorage(aAnimStorage)
   , mVsyncRate(aVsyncRate)
   , mChildLayersObserverEpoch{0}
   , mParentLayersObserverEpoch{0}
   , mWrEpoch{0}
   , mIdNamespace(aApi->GetNamespace())
+  , mContentRectMutex("WebRenderBridgeParent::mContentRectMutex")
   , mPaused(false)
   , mDestroyed(false)
   , mReceivedDisplayList(false)
   , mIsFirstPaint(true)
 {
   MOZ_ASSERT(mAsyncImageManager);
   MOZ_ASSERT(mAnimStorage);
   mAsyncImageManager->AddPipeline(mPipelineId);
@@ -269,16 +272,17 @@ WebRenderBridgeParent::WebRenderBridgePa
 
 WebRenderBridgeParent::WebRenderBridgeParent(const wr::PipelineId& aPipelineId)
   : mCompositorBridge(nullptr)
   , mPipelineId(aPipelineId)
   , mChildLayersObserverEpoch{0}
   , mParentLayersObserverEpoch{0}
   , mWrEpoch{0}
   , mIdNamespace{0}
+  , mContentRectMutex(nullptr)
   , mPaused(false)
   , mDestroyed(true)
   , mReceivedDisplayList(false)
   , mIsFirstPaint(false)
 {
 }
 
 /* static */ WebRenderBridgeParent*
@@ -657,16 +661,17 @@ WebRenderBridgeParent::UpdateExternalIma
   aResources.UpdateImageBuffer(keys[0], descriptor, data);
   return true;
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvUpdateResources(nsTArray<OpUpdateResource>&& aResourceUpdates,
                                            nsTArray<RefCountedShmem>&& aSmallShmems,
                                            nsTArray<ipc::Shmem>&& aLargeShmems,
+                                           const bool& aIsForContentRect,
                                            const bool& aScheduleComposite)
 {
   if (mDestroyed) {
     wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
     wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
     return IPC_OK();
   }
 
@@ -682,17 +687,21 @@ WebRenderBridgeParent::RecvUpdateResourc
     return IPC_FAIL(this, "Invalid WebRender resource data shmem or address.");
   }
 
   if (aScheduleComposite) {
     txn.InvalidateRenderedFrame();
     ScheduleGenerateFrame();
   }
 
-  mApi->SendTransaction(txn);
+  if (aIsForContentRect) {
+    mContentRectApi->SendTransaction(txn);
+  } else {
+    mApi->SendTransaction(txn);
+  }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds)
 {
   if (mDestroyed) {
@@ -820,28 +829,36 @@ WebRenderBridgeParent::SetAPZSampleTime(
     if (frameInterval != TimeDuration::Forever()) {
       animationTime += frameInterval;
     }
     apz->SetSampleTime(animationTime);
   }
 }
 
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvSetDisplayList(const gfx::IntSize& aSize,
-                                          InfallibleTArray<WebRenderParentCommand>&& aCommands,
+WebRenderBridgeParent::RecvSetDisplayList(const gfx::IntRect& aChromeRect,
+                                          InfallibleTArray<WebRenderParentCommand>&& aChromeCommands,
+                                          const wr::LayoutSize& aChromeContentSize,
+                                          ipc::ByteBuf&& chromeDL,
+                                          const wr::BuiltDisplayListDescriptor& chromeDLDesc,
+                                          nsTArray<OpUpdateResource>&& aChromeResourceUpdates,
+                                          nsTArray<RefCountedShmem>&& aChromeSmallShmems,
+                                          nsTArray<ipc::Shmem>&& aChromeLargeShmems,
+                                          const gfx::IntRect& aContentRect,
+                                          InfallibleTArray<WebRenderParentCommand>&& aContentCommands,
+                                          const wr::LayoutSize& aContentContentSize,
+                                          ipc::ByteBuf&& contentDL,
+                                          const wr::BuiltDisplayListDescriptor& contentDLDesc,
+                                          nsTArray<OpUpdateResource>&& aContentResourceUpdates,
+                                          nsTArray<RefCountedShmem>&& aContentSmallShmems,
+                                          nsTArray<ipc::Shmem>&& aContentLargeShmems,
                                           InfallibleTArray<OpDestroy>&& aToDestroy,
                                           const uint64_t& aFwdTransactionId,
                                           const TransactionId& aTransactionId,
-                                          const wr::LayoutSize& aContentSize,
-                                          ipc::ByteBuf&& dl,
-                                          const wr::BuiltDisplayListDescriptor& dlDesc,
                                           const WebRenderScrollData& aScrollData,
-                                          nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                          nsTArray<RefCountedShmem>&& aSmallShmems,
-                                          nsTArray<ipc::Shmem>&& aLargeShmems,
                                           const wr::IdNamespace& aIdNamespace,
                                           const bool& aContainsSVGGroup,
                                           const TimeStamp& aRefreshStartTime,
                                           const TimeStamp& aTxnStartTime,
                                           const TimeStamp& aFwdTime)
 {
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
@@ -866,22 +883,36 @@ WebRenderBridgeParent::RecvSetDisplayLis
   // 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);
   }
+  wr::TransactionBuilder contentTxn;
+  contentTxn.SetLowPriority(!IsRootWebRenderBridgeParent());
+  Maybe<wr::AutoTransactionSender> contentSender;
+  if (validTransaction && !aContentRect.IsEmpty()) {
+    contentSender.emplace(mContentRectApi, &contentTxn);
+  }
 
-  if (!ProcessWebRenderParentCommands(aCommands, txn)) {
+  if (!ProcessWebRenderParentCommands(aChromeCommands, txn, false)) {
     return IPC_FAIL(this, "Invalid parent command found");
   }
 
-  if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn)) {
+  if (!ProcessWebRenderParentCommands(aContentCommands, contentTxn, true)) {
+    return IPC_FAIL(this, "Invalid parent command found");
+  }
+
+  if (!UpdateResources(aChromeResourceUpdates, aChromeSmallShmems, aChromeLargeShmems, txn)) {
+    return IPC_FAIL(this, "Failed to deserialize resource updates");
+  }
+
+  if (!UpdateResources(aContentResourceUpdates, aContentSmallShmems, aContentLargeShmems, contentTxn)) {
     return IPC_FAIL(this, "Failed to deserialize resource updates");
   }
 
   mReceivedDisplayList = true;
 
   if (aScrollData.IsFirstPaint()) {
     mIsFirstPaint = true;
   }
@@ -890,44 +921,74 @@ WebRenderBridgeParent::RecvSetDisplayLis
   // 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));
+  wr::Vec<uint8_t> dlData(std::move(chromeDL));
+  wr::Vec<uint8_t> contentDLData(std::move(contentDL));
 
   bool observeLayersUpdate = ShouldParentObserveEpoch();
 
   if (validTransaction) {
     if (IsRootWebRenderBridgeParent()) {
       LayoutDeviceIntSize widgetSize = mWidget->GetClientSize();
-      LayoutDeviceIntRect docRect(LayoutDeviceIntPoint(), widgetSize);
-      txn.SetWindowParameters(widgetSize, docRect);
+      LayoutDeviceIntRect mainRect = LayoutDeviceIntRect::FromUnknownRect(aChromeRect);
+      mainRect.SetWidth(std::max(0, std::min(widgetSize.width - mainRect.X(), mainRect.Width())));
+      mainRect.SetHeight(std::max(0, std::min(widgetSize.height - mainRect.Y(), mainRect.Height())));
+      txn.SetWindowParameters(widgetSize, mainRect);
+      if (!aContentRect.IsEmpty()) {
+        {
+          MutexAutoLock lock(mContentRectMutex);
+          mContentRect = aContentRect;
+        }
+        LayoutDeviceIntRect contentRect = LayoutDeviceIntRect::FromUnknownRect(aContentRect);
+        contentRect.SetWidth(std::max(0, std::min(widgetSize.width - contentRect.X(), contentRect.Width())));
+        contentRect.SetHeight(std::max(0, std::min(widgetSize.height - contentRect.Y(), contentRect.Height())));
+        contentTxn.SetWindowParameters(widgetSize, contentRect);
+      }
     }
     gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
-    txn.SetDisplayList(clearColor, wrEpoch, LayerSize(aSize.width, aSize.height),
-                       mPipelineId, aContentSize,
-                       dlDesc, dlData);
+    txn.SetDisplayList(clearColor, wrEpoch, LayerSize(aChromeRect.width, aChromeRect.height),
+                       mPipelineId, aChromeContentSize,
+                       chromeDLDesc, dlData);
+    contentTxn.SetDisplayList(clearColor, wrEpoch, LayerSize(aContentRect.width, aContentRect.height),
+                        mPipelineId, aContentContentSize,
+                        contentDLDesc, contentDLData);
 
     if (observeLayersUpdate) {
       txn.Notify(
         wr::Checkpoint::SceneBuilt,
         MakeUnique<ScheduleObserveLayersUpdate>(
           mCompositorBridge,
           GetLayersId(),
           mChildLayersObserverEpoch,
           true
         )
       );
+      contentTxn.Notify(
+        wr::Checkpoint::SceneBuilt,
+        MakeUnique<ScheduleObserveLayersUpdate>(
+          mCompositorBridge,
+          GetLayersId(),
+          mChildLayersObserverEpoch,
+          true
+        )
+      );
     }
 
-    mApi->SendTransaction(txn);
+    if (!aChromeRect.IsEmpty()) {
+      mApi->SendTransaction(txn);
+    }
+    if (!aContentRect.IsEmpty()) {
+      mContentRectApi->SendTransaction(contentTxn);
+    }
 
     // We will schedule generating a frame after the scene
     // build is done, so we don't need to do it here.
   } else if (observeLayersUpdate) {
     mCompositorBridge->ObserveLayersUpdate(GetLayersId(), mChildLayersObserverEpoch, true);
   }
 
   HoldPendingTransactionId(wrEpoch, aTransactionId, aContainsSVGGroup,
@@ -938,32 +999,38 @@ WebRenderBridgeParent::RecvSetDisplayLis
     // Pretend we composited since someone is wating for this event,
     // though DisplayList was not pushed to webrender.
     if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
       TimeStamp now = TimeStamp::Now();
       cbp->NotifyPipelineRendered(mPipelineId, wrEpoch, now, now);
     }
   }
 
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aChromeSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aChromeLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aChromeSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aChromeLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvEmptyTransaction(const FocusTarget& aFocusTarget,
                                             const ScrollUpdatesMap& aUpdates,
                                             const uint32_t& aPaintSequenceNumber,
-                                            InfallibleTArray<WebRenderParentCommand>&& aCommands,
+                                            InfallibleTArray<WebRenderParentCommand>&& aChromeCommands,
+                                            nsTArray<OpUpdateResource>&& aChromeResourceUpdates,
+                                            nsTArray<RefCountedShmem>&& aChromeSmallShmems,
+                                            nsTArray<ipc::Shmem>&& aChromeLargeShmems,
+                                            InfallibleTArray<WebRenderParentCommand>&& aContentCommands,
+                                            nsTArray<OpUpdateResource>&& aContentResourceUpdates,
+                                            nsTArray<RefCountedShmem>&& aContentSmallShmems,
+                                            nsTArray<ipc::Shmem>&& aContentLargeShmems,
                                             InfallibleTArray<OpDestroy>&& aToDestroy,
                                             const uint64_t& aFwdTransactionId,
                                             const TransactionId& aTransactionId,
-                                            nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                            nsTArray<RefCountedShmem>&& aSmallShmems,
-                                            nsTArray<ipc::Shmem>&& aLargeShmems,
                                             const wr::IdNamespace& aIdNamespace,
                                             const TimeStamp& aRefreshStartTime,
                                             const TimeStamp& aTxnStartTime,
                                             const TimeStamp& aFwdTime)
 {
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
       DestroyActor(op);
@@ -987,29 +1054,42 @@ WebRenderBridgeParent::RecvEmptyTransact
     // const so that we can move this structure all the way to the desired
     // destination.
     UpdateAPZScrollOffsets(std::move(const_cast<ScrollUpdatesMap&>(aUpdates)), aPaintSequenceNumber);
     scheduleComposite = true;
   }
 
   wr::TransactionBuilder txn;
   txn.SetLowPriority(!IsRootWebRenderBridgeParent());
-  if (!aResourceUpdates.IsEmpty()) {
+  if (!aChromeResourceUpdates.IsEmpty()) {
     scheduleComposite = true;
   }
 
-  if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn)) {
+  if (!UpdateResources(aChromeResourceUpdates, aChromeSmallShmems, aChromeLargeShmems, txn)) {
     return IPC_FAIL(this, "Failed to deserialize resource updates");
   }
 
-  if (!aCommands.IsEmpty()) {
+  wr::TransactionBuilder contentTxn;
+  contentTxn.SetLowPriority(!IsRootWebRenderBridgeParent());
+  if (!aContentResourceUpdates.IsEmpty()) {
+    scheduleComposite = true;
+  }
+
+  if (!UpdateResources(aContentResourceUpdates, aContentSmallShmems, aContentLargeShmems, contentTxn)) {
+    return IPC_FAIL(this, "Failed to deserialize resource updates");
+  }
+
+  if (!aChromeCommands.IsEmpty() || !aContentCommands.IsEmpty()) {
     mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
-    wr::Epoch wrEpoch = GetNextWrEpoch();
-    txn.UpdateEpoch(mPipelineId, wrEpoch);
-    if (!ProcessWebRenderParentCommands(aCommands, txn)) {
+    Unused << GetNextWrEpoch();    
+  }
+
+  if (!aChromeCommands.IsEmpty()) {
+    txn.UpdateEpoch(mPipelineId, mWrEpoch);
+    if (!ProcessWebRenderParentCommands(aChromeCommands, txn, false)) {
       return IPC_FAIL(this, "Invalid parent command found");
     }
     if (ShouldParentObserveEpoch()) {
       txn.Notify(
         wr::Checkpoint::SceneBuilt,
         MakeUnique<ScheduleObserveLayersUpdate>(
           mCompositorBridge,
           GetLayersId(),
@@ -1017,18 +1097,44 @@ WebRenderBridgeParent::RecvEmptyTransact
           true
         )
       );
     }
 
     scheduleComposite = true;
   }
 
+  if (!aContentCommands.IsEmpty()) {
+    contentTxn.UpdateEpoch(mPipelineId, mWrEpoch);
+    if (!ProcessWebRenderParentCommands(aContentCommands, contentTxn, true)) {
+      return IPC_FAIL(this, "Invalid parent command found");
+    }
+    if (ShouldParentObserveEpoch()) {
+      contentTxn.Notify(
+        wr::Checkpoint::SceneBuilt,
+        MakeUnique<ScheduleObserveLayersUpdate>(
+          mCompositorBridge,
+          GetLayersId(),
+          mChildLayersObserverEpoch,
+          true
+        )
+      );
+    }
+
+    scheduleComposite = true;
+  }
+
   if (!txn.IsEmpty()) {
     mApi->SendTransaction(txn);
+    scheduleComposite = true;
+  }
+
+  if (!contentTxn.IsEmpty()) {
+    mContentRectApi->SendTransaction(contentTxn);
+    scheduleComposite = true;
   }
 
   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.
@@ -1056,87 +1162,102 @@ WebRenderBridgeParent::RecvEmptyTransact
     // we just added, and now we're going to pretend we rendered it
     MOZ_ASSERT(mPendingTransactionIds.size() == 1);
     if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
       TimeStamp now = TimeStamp::Now();
       cbp->NotifyPipelineRendered(mPipelineId, mWrEpoch, now, now);
     }
   }
 
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aChromeSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aChromeLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aContentSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aContentLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetFocusTarget(const FocusTarget& aFocusTarget)
 {
   UpdateAPZFocusState(aFocusTarget);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvParentCommands(nsTArray<WebRenderParentCommand>&& aCommands)
+WebRenderBridgeParent::RecvParentCommands(nsTArray<WebRenderParentCommand>&& aCommands,
+                                          const bool& aIsForContentRect)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   wr::TransactionBuilder txn;
   txn.SetLowPriority(!IsRootWebRenderBridgeParent());
-  if (!ProcessWebRenderParentCommands(aCommands, txn)) {
+  if (!ProcessWebRenderParentCommands(aCommands, txn, aIsForContentRect)) {
     return IPC_FAIL(this, "Invalid parent command found");
   }
-  mApi->SendTransaction(txn);
+  if (aIsForContentRect) {
+    mContentRectApi->SendTransaction(txn);
+  } else {
+    mApi->SendTransaction(txn);
+  }
   return IPC_OK();
 }
 
 bool
 WebRenderBridgeParent::ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands,
-                                                      wr::TransactionBuilder& aTxn)
+                                                      wr::TransactionBuilder& aTxn,
+                                                      bool aIsForContentRect)
 {
   // 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(aIsForContentRect ? mContentRectApi : mApi, &txnForImageBridge);
 
   for (InfallibleTArray<WebRenderParentCommand>::index_type i = 0; i < aCommands.Length(); ++i) {
     const WebRenderParentCommand& cmd = aCommands[i];
     switch (cmd.type()) {
       case WebRenderParentCommand::TOpAddPipelineIdForCompositable: {
         const OpAddPipelineIdForCompositable& op = cmd.get_OpAddPipelineIdForCompositable();
         AddPipelineIdForCompositable(op.pipelineId(),
                                      op.handle(),
                                      op.isAsync(),
                                      aTxn,
-                                     txnForImageBridge);
+                                     txnForImageBridge,
+                                     aIsForContentRect);
         break;
       }
       case WebRenderParentCommand::TOpRemovePipelineIdForCompositable: {
         const OpRemovePipelineIdForCompositable& op = cmd.get_OpRemovePipelineIdForCompositable();
-        RemovePipelineIdForCompositable(op.pipelineId(), aTxn);
+        RemovePipelineIdForCompositable(op.pipelineId(), aTxn, aIsForContentRect);
         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,
+                                                       aIsForContentRect);
         break;
       }
       case WebRenderParentCommand::TOpUpdatedAsyncImagePipeline: {
         const OpUpdatedAsyncImagePipeline& op = cmd.get_OpUpdatedAsyncImagePipeline();
-        mAsyncImageManager->ApplyAsyncImageForPipeline(op.pipelineId(), aTxn, txnForImageBridge);
+        mAsyncImageManager->ApplyAsyncImageForPipeline(op.pipelineId(),
+                                                       aTxn,
+                                                       txnForImageBridge,
+                                                       aIsForContentRect);
         break;
       }
       case WebRenderParentCommand::TCompositableOperation: {
         if (!ReceiveCompositableUpdate(cmd.get_CompositableOperation())) {
           NS_ERROR("ReceiveCompositableUpdate failed");
         }
         break;
       }
@@ -1168,16 +1289,18 @@ 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();
+  mContentRectApi->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
@@ -1211,16 +1334,17 @@ WebRenderBridgeParent::FlushFramePresent
   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();
+  mContentRectApi->WaitFlushed();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvGetSnapshot(PTextureParent* aTexture)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
@@ -1272,17 +1396,18 @@ WebRenderBridgeParent::RecvGetSnapshot(P
   return IPC_OK();
 }
 
 void
 WebRenderBridgeParent::AddPipelineIdForCompositable(const wr::PipelineId& aPipelineId,
                                                     const CompositableHandle& aHandle,
                                                     const bool& aAsync,
                                                     wr::TransactionBuilder& aTxn,
-                                                    wr::TransactionBuilder& aTxnForImageBridge)
+                                                    wr::TransactionBuilder& aTxnForImageBridge,
+                                                    const bool& aIsForContentRect)
 {
   if (mDestroyed) {
     return;
   }
 
   MOZ_ASSERT(mAsyncCompositables.find(wr::AsUint64(aPipelineId)) == mAsyncCompositables.end());
 
   RefPtr<CompositableHost> host;
@@ -1307,46 +1432,50 @@ WebRenderBridgeParent::AddPipelineIdForC
 
   if (!wrHost) {
     return;
   }
 
   wrHost->SetWrBridge(this);
   wrHost->EnableUseAsyncImagePipeline();
   mAsyncCompositables.emplace(wr::AsUint64(aPipelineId), wrHost);
-  mAsyncImageManager->AddAsyncImagePipeline(aPipelineId, wrHost);
+  mAsyncImageManager->AddAsyncImagePipeline(aPipelineId, wrHost, aIsForContentRect);
 
   // If this is being called from WebRenderBridgeParent::RecvSetDisplayList,
   // then aTxn might contain a display list that references pipelines that
   // we just added to the async image manager.
   // If we send the display list alone then WR will not yet have the content for
   // the pipelines and so it will emit errors; the SetEmptyDisplayList call
   // below ensure that we provide its content to WR as part of the same transaction.
   mAsyncImageManager->SetEmptyDisplayList(aPipelineId, aTxn, aTxnForImageBridge);
   return;
 }
 
 void
 WebRenderBridgeParent::RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId,
-                                                       wr::TransactionBuilder& aTxn)
+                                                       wr::TransactionBuilder& aTxn,
+                                                       bool aIsForContentRect)
 {
   if (mDestroyed) {
     return;
   }
 
-  auto it = mAsyncCompositables.find(wr::AsUint64(aPipelineId));
-  if (it == mAsyncCompositables.end()) {
+  auto asyncCompositables = aIsForContentRect ?
+    mContentRectAsyncCompositables : mAsyncCompositables;
+
+  auto it = asyncCompositables.find(wr::AsUint64(aPipelineId));
+  if (it == asyncCompositables.end()) {
     return;
   }
   RefPtr<WebRenderImageHost>& wrHost = it->second;
 
   wrHost->ClearWrBridge();
   mAsyncImageManager->RemoveAsyncImagePipeline(aPipelineId, aTxn);
   aTxn.RemovePipeline(aPipelineId);
-  mAsyncCompositables.erase(wr::AsUint64(aPipelineId));
+  asyncCompositables.erase(wr::AsUint64(aPipelineId));
   return;
 }
 
 void
 WebRenderBridgeParent::DeleteImage(const ImageKey& aKey,
                                    wr::TransactionBuilder& aUpdates)
 {
   if (mDestroyed) {
@@ -1410,30 +1539,46 @@ WebRenderBridgeParent::RecvClearCachedRe
       mCompositorBridge,
       GetLayersId(),
       mChildLayersObserverEpoch,
       false
     )
   );
 
   mApi->SendTransaction(txn);
+
+  wr::TransactionBuilder contentTxn;
+  contentTxn.SetLowPriority(true);
+  contentTxn.ClearDisplayList(GetNextWrEpoch(), mPipelineId);
+  contentTxn.Notify(
+    wr::Checkpoint::SceneBuilt,
+    MakeUnique<ScheduleObserveLayersUpdate>(
+      mCompositorBridge,
+      GetLayersId(),
+      mChildLayersObserverEpoch,
+      false
+    )
+  );
+
+  mContentRectApi->SendTransaction(contentTxn);
   // Schedule generate frame to clean up Pipeline
   ScheduleGenerateFrame();
   // Remove animations.
   for (const auto& id : mActiveAnimations) {
     mAnimStorage->ClearById(id);
   }
   mActiveAnimations.clear();
   std::queue<CompositorAnimationIdsForEpoch>().swap(mCompositorAnimationsToDelete); // clear queue
   return IPC_OK();
 }
 
 wr::Epoch
 WebRenderBridgeParent::UpdateWebRender(CompositorVsyncScheduler* aScheduler,
                                        wr::WebRenderAPI* aApi,
+                                       wr::WebRenderAPI* aContentRectApi,
                                        AsyncImagePipelineManager* aImageMgr,
                                        CompositorAnimationStorage* aAnimStorage,
                                        const TextureFactoryIdentifier& aTextureFactoryIdentifier)
 {
   MOZ_ASSERT(!IsRootWebRenderBridgeParent());
   MOZ_ASSERT(aScheduler);
   MOZ_ASSERT(aApi);
   MOZ_ASSERT(aImageMgr);
@@ -1456,16 +1601,17 @@ WebRenderBridgeParent::UpdateWebRender(C
   // before new layer/webrender keys allocation. In future, we could address the problem.
   Unused << SendWrUpdated(mIdNamespace, aTextureFactoryIdentifier);
   CompositorBridgeParentBase* cBridge = mCompositorBridge;
   // XXX Stop to clear resources if webreder supports resources sharing between different webrender instances.
   ClearResources();
   mCompositorBridge = cBridge;
   mCompositorScheduler = aScheduler;
   mApi = aApi;
+  mContentRectApi = aContentRectApi;
   mAsyncImageManager = aImageMgr;
   mAnimStorage = aAnimStorage;
 
   // Register pipeline to updated AsyncImageManager.
   mAsyncImageManager->AddPipeline(mPipelineId);
 
   return GetNextWrEpoch(); // Update webrender epoch
 }
@@ -1482,25 +1628,29 @@ WebRenderBridgeParent::ScheduleForcedGen
 {
   if (mDestroyed) {
     return;
   }
 
   wr::TransactionBuilder fastTxn(/* aUseSceneBuilderThread */ false);
   fastTxn.InvalidateRenderedFrame();
   mApi->SendTransaction(fastTxn);
+  wr::TransactionBuilder contentFastTxn(/* aUseSceneBuilderThread */ false);
+  contentFastTxn.InvalidateRenderedFrame();
+  mContentRectApi->SendTransaction(contentFastTxn);
 
   ScheduleGenerateFrame();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvCapture()
 {
   if (!mDestroyed) {
     mApi->Capture();
+    mContentRectApi->Capture();
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSyncWithCompositor()
 {
   FlushSceneBuilds();
@@ -1710,36 +1860,47 @@ WebRenderBridgeParent::MaybeGenerateFram
   TimeStamp start = TimeStamp::Now();
   mAsyncImageManager->SetCompositionTime(start);
 
   // Ensure GenerateFrame is handled on the render backend thread rather
   // than going through the scene builder thread. That way we continue generating
   // frames with the old scene even during slow scene builds.
   bool useSceneBuilderThread = false;
   wr::TransactionBuilder fastTxn(useSceneBuilderThread);
+  wr::TransactionBuilder contentFastTxn(useSceneBuilderThread);
 
   // Handle transaction that is related to DisplayList.
   wr::TransactionBuilder sceneBuilderTxn;
   wr::AutoTransactionSender sender(mApi, &sceneBuilderTxn);
+  wr::TransactionBuilder contentSceneBuilderTxn;
+  wr::AutoTransactionSender contentSender(mContentRectApi, &contentSceneBuilderTxn);
 
   // Adding and updating wr::ImageKeys of ImageHosts that uses ImageBridge are
   // done without using transaction of scene builder thread. With it, updating of
   // video frame becomes faster.
-  mAsyncImageManager->ApplyAsyncImagesOfImageBridge(sceneBuilderTxn, fastTxn);
+  mAsyncImageManager->ApplyAsyncImagesOfImageBridge(sceneBuilderTxn,
+                                                    fastTxn,
+                                                    contentSceneBuilderTxn,
+                                                    contentFastTxn);
 
   if (!mAsyncImageManager->GetCompositeUntilTime().IsNull()) {
     // Trigger another CompositeToTarget() call because there might be another
     // frame that we want to generate after this one.
     // It will check if we actually want to generate the frame or not.
     mCompositorScheduler->ScheduleComposition();
   }
 
-  if (!mAsyncImageManager->GetAndResetWillGenerateFrame() &&
-      fastTxn.IsEmpty() &&
-      !aForceGenerateFrame) {
+  bool generateChromeFrame = mAsyncImageManager->GetAndResetWillGenerateFrame() ||
+    !fastTxn.IsEmpty() ||
+    aForceGenerateFrame;
+  bool generateContentFrame = !mAsyncImageManager->GetAndResetWillGenerateFrame() ||
+    !contentFastTxn.IsEmpty() ||
+    aForceGenerateFrame;
+
+  if (!generateChromeFrame && !generateContentFrame) {
     // Could skip generating frame now.
     mPreviousFrameTimeStamp = TimeStamp();
     return;
   }
 
   nsTArray<wr::WrOpacityProperty> opacityArray;
   nsTArray<wr::WrTransformProperty> transformArray;
 
@@ -1754,19 +1915,24 @@ WebRenderBridgeParent::MaybeGenerateFram
 
   wr::RenderThread::Get()->IncPendingFrameCount(mApi->GetId(), start);
 
 #if defined(ENABLE_FRAME_LATENCY_LOG)
   auto startTime = TimeStamp::Now();
   mApi->SetFrameStartTime(startTime);
 #endif
 
-  fastTxn.GenerateFrame();
-
-  mApi->SendTransaction(fastTxn);
+  if (generateContentFrame) {
+    contentFastTxn.GenerateFrame(/* aSkipFrameNotification: */ generateChromeFrame);
+    mContentRectApi->SendTransaction(contentFastTxn);
+  }
+  if (generateChromeFrame) {
+    fastTxn.GenerateFrame(/* aSkipFrameNotification: */ false);
+    mApi->SendTransaction(fastTxn);
+  }
 }
 
 void
 WebRenderBridgeParent::HoldPendingTransactionId(const wr::Epoch& aWrEpoch,
                                                 TransactionId aTransactionId,
                                                 bool aContainsSVGGroup,
                                                 const TimeStamp& aRefreshStartTime,
                                                 const TimeStamp& aTxnStartTime,
@@ -1780,16 +1946,27 @@ WebRenderBridgeParent::HoldPendingTransa
                                                    aContainsSVGGroup,
                                                    aRefreshStartTime,
                                                    aTxnStartTime,
                                                    aFwdTime,
                                                    aIsFirstPaint,
                                                    aUseForTelemetry));
 }
 
+already_AddRefed<wr::WebRenderAPI>
+WebRenderBridgeParent::GetWebRenderAPIAtPoint(const gfx::IntPoint& aPoint)
+{
+  MutexAutoLock lock(mContentRectMutex);
+  if (mContentRect.Contains(aPoint)) {
+    return do_AddRef(mContentRectApi);
+  } else {
+    return do_AddRef(mApi);
+  }
+}
+
 TransactionId
 WebRenderBridgeParent::LastPendingTransactionId()
 {
   TransactionId id{0};
   if (!mPendingTransactionIds.empty()) {
     id = mPendingTransactionIds.back().mId;
   }
   return id;
@@ -1893,32 +2070,36 @@ void
 WebRenderBridgeParent::Pause()
 {
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
 #ifdef MOZ_WIDGET_ANDROID
   if (!IsRootWebRenderBridgeParent() || mDestroyed) {
     return;
   }
   mApi->Pause();
+  mContentRectApi->Pause();
 #endif
   mPaused = true;
 }
 
 bool
 WebRenderBridgeParent::Resume()
 {
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
 #ifdef MOZ_WIDGET_ANDROID
   if (!IsRootWebRenderBridgeParent() || mDestroyed) {
     return false;
   }
 
   if (!mApi->Resume()) {
     return false;
   }
+  if (!mContentRectApi->Resume()) {
+    return false;
+  }
 #endif
   mPaused = false;
   return true;
 }
 
 void
 WebRenderBridgeParent::ClearResources()
 {
@@ -1926,16 +2107,19 @@ WebRenderBridgeParent::ClearResources()
     return;
   }
 
   wr::Epoch wrEpoch = GetNextWrEpoch();
 
   wr::TransactionBuilder txn;
   txn.SetLowPriority(true);
   txn.ClearDisplayList(wrEpoch, mPipelineId);
+  wr::TransactionBuilder contentTxn;
+  contentTxn.SetLowPriority(true);
+  contentTxn.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);
@@ -1947,40 +2131,53 @@ WebRenderBridgeParent::ClearResources()
   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 : mContentRectAsyncCompositables) {
+    wr::PipelineId pipelineId = wr::AsPipelineId(entry.first);
+    RefPtr<WebRenderImageHost> host = entry.second;
+    host->ClearWrBridge();
+    mAsyncImageManager->RemoveAsyncImagePipeline(pipelineId, contentTxn);
+    contentTxn.RemovePipeline(pipelineId);
+  }
+  mContentRectAsyncCompositables.clear();
+
   for (const auto& entry : mSharedSurfaceIds) {
     mAsyncImageManager->HoldExternalImage(mPipelineId, mWrEpoch, entry.second);
   }
   mSharedSurfaceIds.clear();
 
   mAsyncImageManager->RemovePipeline(mPipelineId, wrEpoch);
   txn.RemovePipeline(mPipelineId);
+  contentTxn.RemovePipeline(mPipelineId);
 
   mApi->SendTransaction(txn);
+  mContentRectApi->SendTransaction(contentTxn);
 
   for (const auto& id : mActiveAnimations) {
     mAnimStorage->ClearById(id);
   }
   mActiveAnimations.clear();
   std::queue<CompositorAnimationIdsForEpoch>().swap(mCompositorAnimationsToDelete); // clear queue
 
   if (IsRootWebRenderBridgeParent()) {
     mCompositorScheduler->Destroy();
   }
 
   mAnimStorage = nullptr;
   mCompositorScheduler = nullptr;
   mAsyncImageManager = nullptr;
   mApi = nullptr;
+  mContentRectApi = nullptr;
   mCompositorBridge = nullptr;
 }
 
 bool
 WebRenderBridgeParent::ShouldParentObserveEpoch()
 {
   if (mParentLayersObserverEpoch == mChildLayersObserverEpoch) {
     return false;
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -51,74 +51,91 @@ class WebRenderBridgeParent final : publ
                                   , public layers::FrameRecorder
 {
 public:
   WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
                         const wr::PipelineId& aPipelineId,
                         widget::CompositorWidget* aWidget,
                         CompositorVsyncScheduler* aScheduler,
                         RefPtr<wr::WebRenderAPI>&& aApi,
+                        RefPtr<wr::WebRenderAPI>&& aContentRectApi,
                         RefPtr<AsyncImagePipelineManager>&& aImageMgr,
                         RefPtr<CompositorAnimationStorage>&& aAnimStorage,
                         TimeDuration aVsyncRate);
 
   static WebRenderBridgeParent* CreateDestroyed(const wr::PipelineId& aPipelineId);
 
   wr::PipelineId PipelineId() { return mPipelineId; }
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPIAtPoint(const gfx::IntPoint& aPoint);
   already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() { return do_AddRef(mApi); }
+  already_AddRefed<wr::WebRenderAPI> GetContentRectWebRenderAPI() { return do_AddRef(mContentRectApi); }
   AsyncImagePipelineManager* AsyncImageManager() { return mAsyncImageManager; }
   CompositorVsyncScheduler* CompositorScheduler() { return mCompositorScheduler.get(); }
 
   mozilla::ipc::IPCResult RecvEnsureConnected(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,
+                                              const bool& aIsForContentRect,
                                               const bool& aScheduleComposite) override;
-  mozilla::ipc::IPCResult RecvSetDisplayList(const gfx::IntSize& aSize,
-                                             InfallibleTArray<WebRenderParentCommand>&& aCommands,
+  mozilla::ipc::IPCResult RecvSetDisplayList(const gfx::IntRect& aChromeRect,
+                                             InfallibleTArray<WebRenderParentCommand>&& aChromeCommands,
+                                             const wr::LayoutSize& aChromeContentSize,
+                                             ipc::ByteBuf&& chromeDL,
+                                             const wr::BuiltDisplayListDescriptor& chromeDLDesc,
+                                             nsTArray<OpUpdateResource>&& aChromeResourceUpdates,
+                                             nsTArray<RefCountedShmem>&& aChromeSmallShmems,
+                                             nsTArray<ipc::Shmem>&& aChromeLargeShmems,
+                                             const gfx::IntRect& aContentRect,
+                                             InfallibleTArray<WebRenderParentCommand>&& aContentCommands,
+                                             const wr::LayoutSize& aContentContentSize,
+                                             ipc::ByteBuf&& contentDL,
+                                             const wr::BuiltDisplayListDescriptor& contentDLDesc,
+                                             nsTArray<OpUpdateResource>&& aContentResourceUpdates,
+                                             nsTArray<RefCountedShmem>&& aContentSmallShmems,
+                                             nsTArray<ipc::Shmem>&& aContentLargeShmems,
                                              InfallibleTArray<OpDestroy>&& aToDestroy,
                                              const uint64_t& aFwdTransactionId,
                                              const TransactionId& aTransactionId,
-                                             const wr::LayoutSize& aContentSize,
-                                             ipc::ByteBuf&& dl,
-                                             const wr::BuiltDisplayListDescriptor& dlDesc,
                                              const WebRenderScrollData& aScrollData,
-                                             nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                             nsTArray<RefCountedShmem>&& aSmallShmems,
-                                             nsTArray<ipc::Shmem>&& aLargeShmems,
                                              const wr::IdNamespace& aIdNamespace,
                                              const bool& aContainsSVGGroup,
                                              const TimeStamp& aRefreshStartTime,
                                              const TimeStamp& aTxnStartTime,
                                              const TimeStamp& aFwdTime) override;
   mozilla::ipc::IPCResult RecvEmptyTransaction(const FocusTarget& aFocusTarget,
                                                const ScrollUpdatesMap& aUpdates,
                                                const uint32_t& aPaintSequenceNumber,
-                                               InfallibleTArray<WebRenderParentCommand>&& aCommands,
+                                               InfallibleTArray<WebRenderParentCommand>&& aChromeCommands,
+                                               nsTArray<OpUpdateResource>&& aChromeResourceUpdates,
+                                               nsTArray<RefCountedShmem>&& aChromeSmallShmems,
+                                               nsTArray<ipc::Shmem>&& aChromeLargeShmems,
+                                               InfallibleTArray<WebRenderParentCommand>&& aContentCommands,
+                                               nsTArray<OpUpdateResource>&& aContentResourceUpdates,
+                                               nsTArray<RefCountedShmem>&& aContentSmallShmems,
+                                               nsTArray<ipc::Shmem>&& aContentLargeShmems,
                                                InfallibleTArray<OpDestroy>&& aToDestroy,
                                                const uint64_t& aFwdTransactionId,
                                                const TransactionId& aTransactionId,
-                                               nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                               nsTArray<RefCountedShmem>&& aSmallShmems,
-                                               nsTArray<ipc::Shmem>&& aLargeShmems,
                                                const wr::IdNamespace& aIdNamespace,
                                                const TimeStamp& aRefreshStartTime,
                                                const TimeStamp& aTxnStartTime,
                                                const TimeStamp& aFwdTime) override;
   mozilla::ipc::IPCResult RecvSetFocusTarget(const FocusTarget& aFocusTarget) override;
-  mozilla::ipc::IPCResult RecvParentCommands(nsTArray<WebRenderParentCommand>&& commands) override;
+  mozilla::ipc::IPCResult RecvParentCommands(nsTArray<WebRenderParentCommand>&& commands,
+                                             const bool& aIsForContentRect) 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;
@@ -201,16 +218,17 @@ public:
    *
    * WebRender could skip frame rendering if there is no update.
    * This function is used to force rendering even when there is not update.
    */
   void ScheduleForcedGenerateFrame();
 
   wr::Epoch UpdateWebRender(CompositorVsyncScheduler* aScheduler,
                             wr::WebRenderAPI* aApi,
+                            wr::WebRenderAPI* aContentRectApi,
                             AsyncImagePipelineManager* aImageMgr,
                             CompositorAnimationStorage* aAnimStorage,
                             const TextureFactoryIdentifier& aTextureFactoryIdentifier);
 
   void RemoveEpochDataPriorTo(const wr::Epoch& aRenderedEpoch);
 
   /**
    * This sets the is-first-paint flag to true for the next received
@@ -249,27 +267,30 @@ private:
                                    TextureHost* aTexture,
                                    bool aIsUpdate,
                                    wr::TransactionBuilder& aResources);
 
   void AddPipelineIdForCompositable(const wr::PipelineId& aPipelineIds,
                                     const CompositableHandle& aHandle,
                                     const bool& aAsync,
                                     wr::TransactionBuilder& aTxn,
-                                    wr::TransactionBuilder& aTxnForImageBridge);
+                                    wr::TransactionBuilder& aTxnForImageBridge,
+                                    const bool& aIsForContentRect);
   void RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId,
-                                       wr::TransactionBuilder& aTxn);
+                                       wr::TransactionBuilder& aTxn,
+                                       bool aIsForContentRect);
 
   void DeleteImage(const wr::ImageKey& aKey,
                    wr::TransactionBuilder& aUpdates);
   void ReleaseTextureOfImage(const wr::ImageKey& aKey);
 
   LayersId GetLayersId() const;
   bool ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands,
-                                      wr::TransactionBuilder& aTxn);
+                                      wr::TransactionBuilder& aTxn,
+                                      bool aIsForContentRect);
 
   void ClearResources();
   bool ShouldParentObserveEpoch();
   mozilla::ipc::IPCResult HandleShutdown();
 
   // Returns true if there is any animation (including animations in delay
   // phase).
   bool AdvanceAnimations();
@@ -332,23 +353,25 @@ private:
     wr::Epoch mEpoch;
     InfallibleTArray<uint64_t> mIds;
   };
 
   CompositorBridgeParentBase* MOZ_NON_OWNING_REF mCompositorBridge;
   wr::PipelineId mPipelineId;
   RefPtr<widget::CompositorWidget> mWidget;
   RefPtr<wr::WebRenderAPI> mApi;
+  RefPtr<wr::WebRenderAPI> mContentRectApi;
   RefPtr<AsyncImagePipelineManager> mAsyncImageManager;
   RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
   RefPtr<CompositorAnimationStorage> mAnimStorage;
   // mActiveAnimations is used to avoid leaking animations when WebRenderBridgeParent is
   // destroyed abnormally and Tab move between different windows.
   std::unordered_set<uint64_t> mActiveAnimations;
   std::unordered_map<uint64_t, RefPtr<WebRenderImageHost>> mAsyncCompositables;
+  std::unordered_map<uint64_t, RefPtr<WebRenderImageHost>> mContentRectAsyncCompositables;
   std::unordered_map<uint64_t, CompositableTextureHostRef> mTextureHosts;
   std::unordered_map<uint64_t, wr::ExternalImageId> mSharedSurfaceIds;
 
   TimeDuration mVsyncRate;
   TimeStamp mPreviousFrameTimeStamp;
   // These fields keep track of the latest layer observer epoch values in the child and the
   // parent. mChildLayersObserverEpoch is the latest epoch value received from the child.
   // mParentLayersObserverEpoch is the latest epoch value that we have told TabParent about
@@ -356,16 +379,20 @@ private:
   LayersObserverEpoch mChildLayersObserverEpoch;
   LayersObserverEpoch mParentLayersObserverEpoch;
 
   std::queue<PendingTransactionId> mPendingTransactionIds;
   std::queue<CompositorAnimationIdsForEpoch> mCompositorAnimationsToDelete;
   wr::Epoch mWrEpoch;
   wr::IdNamespace mIdNamespace;
 
+  // Kind of clunky, but I can't sort out a more elegant way of getting this to work.
+  Mutex mContentRectMutex;
+  IntRect mContentRect;
+
   bool mPaused;
   bool mDestroyed;
   bool mReceivedDisplayList;
   bool mIsFirstPaint;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderCanvasRenderer.cpp
+++ b/gfx/layers/wr/WebRenderCanvasRenderer.cpp
@@ -34,17 +34,18 @@ WebRenderCanvasRendererAsync::~WebRender
 }
 
 void
 WebRenderCanvasRendererAsync::Initialize(const CanvasInitializeData& aData)
 {
   WebRenderCanvasRenderer::Initialize(aData);
 
   if (mPipelineId.isSome()) {
-    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref(),
+                                                          mIsForContentRect);
     mPipelineId.reset();
   }
 }
 
 bool
 WebRenderCanvasRendererAsync::CreateCompositable()
 {
   if (!mCanvasClient) {
@@ -66,47 +67,51 @@ WebRenderCanvasRendererAsync::CreateComp
 
     mCanvasClient->Connect();
   }
 
   if (!mPipelineId) {
     // Alloc async image pipeline id.
     mPipelineId = Some(mManager->WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
     mManager->WrBridge()->AddPipelineIdForCompositable(mPipelineId.ref(),
-                                                       mCanvasClient->GetIPCHandle());
+                                                       mCanvasClient->GetIPCHandle(),
+                                                       mIsForContentRect);
   }
 
   return true;
 }
 
 void
 WebRenderCanvasRendererAsync::ClearCachedResources()
 {
   if (mPipelineId.isSome()) {
-    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref(),
+                                                          mIsForContentRect);
     mPipelineId.reset();
   }
 }
 
 void
 WebRenderCanvasRendererAsync::Destroy()
 {
   if (mPipelineId.isSome()) {
-    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref(),
+                                                          mIsForContentRect);
     mPipelineId.reset();
   }
 }
 
 void
 WebRenderCanvasRendererAsync::UpdateCompositableClientForEmptyTransaction()
 {
   UpdateCompositableClient();
   if (mPipelineId.isSome()) {
     // Notify an update of async image pipeline during empty transaction.
     // During non empty transaction, WebRenderBridgeParent receives OpUpdateAsyncImagePipeline message,
     // but during empty transaction, the message is not sent to WebRenderBridgeParent.
     // Then OpUpdatedAsyncImagePipeline is used to notify the update.
-    mManager->WrBridge()->AddWebRenderParentCommand(OpUpdatedAsyncImagePipeline(mPipelineId.ref()));
+    mManager->WrBridge()->AddWebRenderParentCommand(OpUpdatedAsyncImagePipeline(mPipelineId.ref()),
+                                                    mIsForContentRect);
   }
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderCanvasRenderer.h
+++ b/gfx/layers/wr/WebRenderCanvasRenderer.h
@@ -27,32 +27,35 @@ public:
 
 protected:
   WebRenderLayerManager* mManager;
 };
 
 class WebRenderCanvasRendererAsync : public WebRenderCanvasRenderer
 {
 public:
-  explicit WebRenderCanvasRendererAsync(WebRenderLayerManager* aManager)
+  explicit WebRenderCanvasRendererAsync(WebRenderLayerManager* aManager,
+                                        bool aIsForContentRect)
     : WebRenderCanvasRenderer(aManager)
+    , mIsForContentRect(aIsForContentRect)
   { }
   virtual ~WebRenderCanvasRendererAsync();
 
   WebRenderCanvasRendererAsync* AsWebRenderCanvasRendererAsync() override { return this; }
 
   void Initialize(const CanvasInitializeData& aData) override;
   bool CreateCompositable() override;
 
   void ClearCachedResources() override;
   void Destroy() override;
 
   void UpdateCompositableClientForEmptyTransaction();
 
   Maybe<wr::PipelineId> GetPipelineId() { return mPipelineId; }
 protected:
   Maybe<wr::PipelineId> mPipelineId;
+  bool mIsForContentRect;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -336,17 +336,17 @@ struct DIGroup
   int32_t mAppUnitsPerDevPixel;
   gfx::Size mScale;
   ScrollableLayerGuid::ViewID mScrollId;
   LayerPoint mResidualOffset;
   LayerIntRect mLayerBounds;
   // The current bounds of the blob image, relative to
   // the top-left of the mLayerBounds.
   IntRect mImageBounds;
-  Maybe<wr::ImageKey> mKey;
+  Maybe<mozilla::Pair<bool, wr::ImageKey>> mKey;
   std::vector<RefPtr<SourceSurface>> mExternalSurfaces;
   std::vector<RefPtr<ScaledFont>> mFonts;
 
   DIGroup()
     : mAppUnitsPerDevPixel(0)
     , mScrollId(ScrollableLayerGuid::NULL_SCROLL_ID)
   {
   }
@@ -373,17 +373,17 @@ struct DIGroup
       delete data;
     }
   }
 
   void ClearImageKey(WebRenderLayerManager* aManager, bool aForce = false)
   {
     if (mKey) {
       MOZ_RELEASE_ASSERT(aForce || mInvalidRect.IsEmpty());
-      aManager->AddImageKeyForDiscard(mKey.value());
+      aManager->AddImageKeyForDiscard(mKey.value().second(), mKey.value().first());
       mKey = Nothing();
     }
     mFonts.clear();
   }
 
   static IntRect
   ToDeviceSpace(nsRect aBounds, Matrix& aMatrix, int32_t aAppUnitsPerDevPixel, LayerIntPoint aOffset)
   {
@@ -603,33 +603,35 @@ struct DIGroup
     IntSize dtSize = layerBounds.Size().ToUnknownSize();
     LayoutDeviceRect bounds = (LayerRect(layerBounds) - mResidualOffset) / scale;
 
     if (mInvalidRect.IsEmpty()) {
       GP("Not repainting group because it's empty\n");
       GP("End EndGroup\n");
       if (mKey) {
         aResources.SetImageVisibleArea(
-          mKey.value(),
+          mKey.value().second(),
           ViewAs<ImagePixel>(mPaintRect, PixelCastJustification::LayerIsImage));
         PushImage(aBuilder, bounds);
       }
       return;
     }
 
     gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
     std::vector<RefPtr<ScaledFont>> fonts;
     RefPtr<WebRenderDrawEventRecorder> recorder =
       MakeAndAddRef<WebRenderDrawEventRecorder>(
         [&](MemStream& aStream, std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
           size_t count = aScaledFonts.size();
           aStream.write((const char*)&count, sizeof(count));
           for (auto& scaled : aScaledFonts) {
             BlobFont font = {
-              aWrManager->WrBridge()->GetFontKeyForScaledFont(scaled, &aResources),
+              aWrManager->WrBridge()->GetFontKeyForScaledFont(scaled,
+                                                              aBuilder.IsContentRectBuilder(),
+                                                              &aResources),
               scaled
             };
             aStream.write((const char*)&font, sizeof(font));
           }
           fonts = std::move(aScaledFonts);
         });
 
     RefPtr<gfx::DrawTarget> dummyDt =
@@ -663,31 +665,31 @@ struct DIGroup
         return;
       wr::ImageKey key = aWrManager->WrBridge()->GetNextImageKey();
       GP("No previous key making new one %d\n", key.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.IsContentRectBuilder(), key));
     } else {
       wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity);
       auto bottomRight = mInvalidRect.BottomRight();
       GP("check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y, dtSize.width, dtSize.height);
       MOZ_RELEASE_ASSERT(bottomRight.x <= dtSize.width && bottomRight.y <= dtSize.height);
       GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
-      if (!aResources.UpdateBlobImage(mKey.value(), descriptor, bytes, ViewAs<ImagePixel>(mInvalidRect))) {
+      if (!aResources.UpdateBlobImage(mKey.value().second(), descriptor, bytes, ViewAs<ImagePixel>(mInvalidRect))) {
         return;
       }
     }
     mFonts = std::move(fonts);
     mInvalidRect.SetEmpty();
     aResources.SetImageVisibleArea(
-      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);
@@ -701,17 +703,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),
-                       mKey.value());
+                       mKey.value().second());
     aBuilder.ClearHitTestInfo();
   }
 
   void PaintItemRange(Grouper* aGrouper,
                       nsDisplayItem* aStartItem,
                       nsDisplayItem* aEndItem,
                       gfxContext* aContext,
                       WebRenderDrawEventRecorder* aRecorder) {
@@ -939,17 +941,19 @@ Grouper::PaintContainerItem(DIGroup* aGr
       aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
       break;
   }
 }
 
 class WebRenderGroupData : public WebRenderUserData
 {
 public:
-  explicit WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  explicit WebRenderGroupData(WebRenderLayerManager* aWRManager,
+                              nsDisplayItem* aItem,
+                              bool aIsForContentRect);
   virtual ~WebRenderGroupData();
 
   virtual WebRenderGroupData* AsGroupData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eGroup; }
   static UserDataType Type() { return UserDataType::eGroup; }
 
   DIGroup mSubGroup;
   DIGroup mFollowingGroup;
@@ -1031,17 +1035,18 @@ Grouper::ConstructGroups(nsDisplayListBu
       // this function.
       bool createdWRCommands =
         item->CreateWebRenderCommands(aBuilder, aResources, aSc, aCommandBuilder->mManager,
                                       mDisplayListBuilder);
       sIndent--;
       MOZ_RELEASE_ASSERT(createdWRCommands, "active transforms should always succeed at creating WebRender commands");
 
       RefPtr<WebRenderGroupData> groupData =
-        aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>(item);
+        aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>(item,
+                                                                              aBuilder.IsContentRectBuilder());
 
       // Initialize groupData->mFollowingGroup
       // TODO: compute the group bounds post-grouping, so that they can be
       // tighter for just the sublist that made it into this group.
       // We want to ensure the tight bounds are still clipped by area
       // that we're building the display list for.
       if (!groupData->mFollowingGroup.mGroupBounds.IsEqualEdges(currentGroup->mGroupBounds) ||
           groupData->mFollowingGroup.mScale != currentGroup->mScale ||
@@ -1188,23 +1193,25 @@ WebRenderCommandBuilder::DoGroupingForDi
                                                   const StackingContextHelper& aSc,
                                                   wr::DisplayListBuilder& aBuilder,
                                                   wr::IpcResourceUpdateQueue& aResources)
 {
   if (!aList->GetBottom()) {
     return;
   }
 
-  mClipManager.BeginList(aSc);
-  Grouper g(mClipManager);
+  mCurrentClipManager->BeginList(aSc);
+  Grouper g(*mCurrentClipManager);
   int32_t appUnitsPerDevPixel = aWrappingItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   GP("DoGroupingForDisplayList\n");
 
   g.mDisplayListBuilder = aDisplayListBuilder;
-  RefPtr<WebRenderGroupData> groupData = CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem);
+  RefPtr<WebRenderGroupData> groupData =
+    CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem,
+                                                         aBuilder.IsContentRectBuilder());
   bool snapped;
   nsRect groupBounds = aWrappingItem->GetBounds(aDisplayListBuilder, &snapped);
   DIGroup& group = groupData->mSubGroup;
   auto p = group.mGroupBounds;
   auto q = groupBounds;
   gfx::Size scale = aSc.GetInheritedScale();
   auto trans = ViewAs<LayerPixel>(aSc.GetSnappingSurfaceTransform().GetTranslation());
   auto snappedTrans = LayerIntPoint::Floor(trans);
@@ -1267,17 +1274,17 @@ WebRenderCommandBuilder::DoGroupingForDi
   // mLayerBounds.TopLeft() in the blob image we want to stop doing this
   // adjustment.
   group.mPaintRect = group.mPaintRect - group.mLayerBounds.TopLeft();
   g.mTransform = Matrix::Scaling(scale.width, scale.height)
                                 .PostTranslate(residualOffset.x, residualOffset.y);
   group.mScale = scale;
   group.mScrollId = scrollId;
   g.ConstructGroups(aDisplayListBuilder, this, aBuilder, aResources, &group, aList, aSc);
-  mClipManager.EndList(aSc);
+  mCurrentClipManager->EndList(aSc);
 }
 
 void
 WebRenderCommandBuilder::Destroy()
 {
   mLastCanvasDatas.Clear();
   ClearCachedResources();
 }
@@ -1302,31 +1309,42 @@ WebRenderCommandBuilder::NeedsEmptyTrans
 }
 
 void
 WebRenderCommandBuilder::BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder,
                                                 wr::IpcResourceUpdateQueue& aResourceUpdates,
                                                 nsDisplayList* aDisplayList,
                                                 nsDisplayListBuilder* aDisplayListBuilder,
                                                 WebRenderScrollData& aScrollData,
-                                                wr::LayoutSize& aContentSize,
                                                 const nsTArray<wr::WrFilterOp>& aFilters)
 {
   StackingContextHelper sc;
+  StackingContextHelper sc2;
+
   aScrollData = WebRenderScrollData(mManager);
   MOZ_ASSERT(mLayerScrollData.empty());
   mLastCanvasDatas.Clear();
   mLastAsr = nullptr;
   mBuilderDumpIndex = 0;
   mContainsSVGGroup = false;
   MOZ_ASSERT(mDumpIndent == 0);
-  mClipManager.BeginBuild(mManager, aBuilder);
+  if (aBuilder.IsContentRectBuilder()) {
+    mContentRectClipManager.BeginBuild(mManager, aBuilder);
+  } else if (aBuilder.HasContentRectBuilder()) {
+    mClipManager.BeginBuild(mManager, aBuilder);
+    mContentRectClipManager.BeginBuild(mManager, aBuilder.ContentRectBuilder());
+  } else {
+    mClipManager.BeginBuild(mManager, aBuilder);
+  }
 
   {
     StackingContextHelper pageRootSc(sc, nullptr, aBuilder, aFilters);
+    if (aBuilder.HasContentRectBuilder()) {
+      pageRootSc.CreateContentRectHelper(sc2, aFilters);
+    }
     if (ShouldDumpDisplayList(aDisplayListBuilder)) {
       mBuilderDumpIndex = aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
     }
     CreateWebRenderCommandsFromDisplayList(aDisplayList, nullptr, aDisplayListBuilder,
                                            pageRootSc, aBuilder, aResourceUpdates);
   }
 
   // Make a "root" layer data that has everything else as descendants
@@ -1341,17 +1359,24 @@ WebRenderCommandBuilder::BuildWebRenderC
   }
   // Append the WebRenderLayerScrollData items into WebRenderScrollData
   // in reverse order, from topmost to bottommost. This is in keeping with
   // the semantics of WebRenderScrollData.
   for (auto i = mLayerScrollData.crbegin(); i != mLayerScrollData.crend(); i++) {
     aScrollData.AddLayerData(*i);
   }
   mLayerScrollData.clear();
-  mClipManager.EndBuild();
+  if (aBuilder.IsContentRectBuilder()) {
+    mContentRectClipManager.EndBuild();
+  } else if (aBuilder.HasContentRectBuilder()) {
+    mClipManager.EndBuild();
+    mContentRectClipManager.EndBuild();
+  } else {
+    mClipManager.EndBuild();
+  }
 
   // Remove the user data those are not displayed on the screen and
   // also reset the data to unused for next transaction.
   RemoveUnusedAndResetWebRenderUserData();
 }
 
 bool
 WebRenderCommandBuilder::ShouldDumpDisplayList(nsDisplayListBuilder* aBuilder)
@@ -1364,32 +1389,39 @@ WebRenderCommandBuilder::ShouldDumpDispl
 void
 WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(nsDisplayList* aDisplayList,
                                                                 nsDisplayItem* aWrappingItem,
                                                                 nsDisplayListBuilder* aDisplayListBuilder,
                                                                 const StackingContextHelper& aSc,
                                                                 wr::DisplayListBuilder& aBuilder,
                                                                 wr::IpcResourceUpdateQueue& aResources)
 {
+  ClipManager* previousClipManager = mCurrentClipManager;
+  if (aBuilder.IsContentRectBuilder()) {
+    mCurrentClipManager = &mContentRectClipManager;
+  } else {
+    mCurrentClipManager = &mClipManager;
+  }
+
   if (mDoGrouping) {
     MOZ_RELEASE_ASSERT(aWrappingItem, "Only the root list should have a null wrapping item, and mDoGrouping should never be true for the root list.");
     GP("actually entering the grouping code\n");
     DoGroupingForDisplayList(aDisplayList, aWrappingItem, aDisplayListBuilder, aSc, aBuilder, aResources);
     return;
   }
 
   bool dumpEnabled = ShouldDumpDisplayList(aDisplayListBuilder);
   if (dumpEnabled) {
     // If we're inside a nested display list, print the WR DL items from the
     // wrapper item before we start processing the nested items.
     mBuilderDumpIndex = aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
   }
 
   mDumpIndent++;
-  mClipManager.BeginList(aSc);
+  mCurrentClipManager->BeginList(aSc);
 
   bool apzEnabled = mManager->AsyncPanZoomEnabled();
 
   FlattenedDisplayItemIterator iter(aDisplayListBuilder, aDisplayList);
   while (nsDisplayItem* i = iter.GetNext()) {
     nsDisplayItem* item = i;
     DisplayItemType itemType = item->GetType();
 
@@ -1448,17 +1480,17 @@ WebRenderCommandBuilder::CreateWebRender
       // If we're going to create a new layer data for this item, stash the
       // ASR so that if we recurse into a sublist they will know where to stop
       // walking up their ASR chain when building scroll metadata.
       if (forceNewLayerData) {
         mAsrStack.push_back(asr);
       }
     }
 
-    mClipManager.BeginItem(item, aSc);
+    mCurrentClipManager->BeginItem(item, aSc);
 
     { // scope restoreDoGrouping
       AutoRestore<bool> restoreDoGrouping(mDoGrouping);
       if (itemType == DisplayItemType::TYPE_SVG_WRAPPER) {
         // Inside an <svg>, all display items that are not LAYER_ACTIVE wrapper
         // display items (like animated transforms / opacity) share the same
         // animated geometry root, so we can combine subsequent items of that
         // type into the same image.
@@ -1542,43 +1574,46 @@ WebRenderCommandBuilder::CreateWebRender
           mLayerScrollData.back().Initialize(mManager->GetScrollData(), item,
               descendants, stopAtAsr, aSc.GetDeferredTransformMatrix());
         }
       }
     }
   }
 
   mDumpIndent--;
-  mClipManager.EndList(aSc);
+  mCurrentClipManager->EndList(aSc);
+  mCurrentClipManager = previousClipManager;
 }
 
 void
 WebRenderCommandBuilder::PushOverrideForASR(const ActiveScrolledRoot* aASR,
                                             const Maybe<wr::WrClipId>& aClipId)
 {
-  mClipManager.PushOverrideForASR(aASR, aClipId);
+  mCurrentClipManager->PushOverrideForASR(aASR, aClipId);
 }
 
 void
 WebRenderCommandBuilder::PopOverrideForASR(const ActiveScrolledRoot* aASR)
 {
-  mClipManager.PopOverrideForASR(aASR);
+  mCurrentClipManager->PopOverrideForASR(aASR);
 }
 
 Maybe<wr::ImageKey>
 WebRenderCommandBuilder::CreateImageKey(nsDisplayItem* aItem,
                                         ImageContainer* aContainer,
                                         mozilla::wr::DisplayListBuilder& aBuilder,
                                         mozilla::wr::IpcResourceUpdateQueue& aResources,
                                         mozilla::wr::ImageRendering aRendering,
                                         const StackingContextHelper& aSc,
                                         gfx::IntSize& aSize,
                                         const Maybe<LayoutDeviceRect>& aAsyncImageBounds)
 {
-  RefPtr<WebRenderImageData> imageData = CreateOrRecycleWebRenderUserData<WebRenderImageData>(aItem);
+  RefPtr<WebRenderImageData> imageData =
+    CreateOrRecycleWebRenderUserData<WebRenderImageData>(aItem,
+                                                         aBuilder.IsContentRectBuilder());
   MOZ_ASSERT(imageData);
 
   if (aContainer->IsAsync()) {
     MOZ_ASSERT(aAsyncImageBounds);
 
     LayoutDeviceRect rect = aAsyncImageBounds.value();
     LayoutDeviceRect scBounds(LayoutDevicePoint(0, 0), rect.Size());
     gfx::MaybeIntSize scaleToSize;
@@ -1827,17 +1862,19 @@ WebRenderCommandBuilder::GenerateFallbac
 {
   bool useBlobImage = gfxPrefs::WebRenderBlobImages() && !aItem->MustPaintOnContentSide();
   Maybe<gfx::Color> highlight = Nothing();
   if (gfxPrefs::WebRenderHighlightPaintedLayers()) {
     highlight = Some(useBlobImage ? gfx::Color(1.0, 0.0, 0.0, 0.5)
                                   : gfx::Color(1.0, 1.0, 0.0, 0.5));
   }
 
-  RefPtr<WebRenderFallbackData> fallbackData = CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem);
+  RefPtr<WebRenderFallbackData> fallbackData =
+    CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem,
+                                                            aBuilder.IsContentRectBuilder());
 
   bool snap;
   nsRect itemBounds = aItem->GetBounds(aDisplayListBuilder, &snap);
 
   // Blob images will only draw the visible area of the blob so we don't need to clip
   // them here and can just rely on the webrender clipping.
   // TODO We also don't clip native themed widget to avoid over-invalidation during scrolling.
   // it would be better to support a sort of straming/tiling scheme for large ones but the hope
@@ -1932,17 +1969,19 @@ WebRenderCommandBuilder::GenerateFallbac
       std::vector<RefPtr<ScaledFont>> fonts;
 
       RefPtr<WebRenderDrawEventRecorder> recorder =
         MakeAndAddRef<WebRenderDrawEventRecorder>([&] (MemStream &aStream, std::vector<RefPtr<ScaledFont>> &aScaledFonts) {
           size_t count = aScaledFonts.size();
           aStream.write((const char*)&count, sizeof(count));
           for (auto& scaled : aScaledFonts) {
             BlobFont font = {
-              mManager->WrBridge()->GetFontKeyForScaledFont(scaled, &aResources),
+              mManager->WrBridge()->GetFontKeyForScaledFont(scaled,
+                                                            aBuilder.IsContentRectBuilder(),
+                                                            &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);
@@ -2086,17 +2125,18 @@ WebRenderCommandBuilder::RemoveUnusedAnd
 
       MOZ_ASSERT(frame->HasProperty(WebRenderUserDataProperty::Key()));
 
       WebRenderUserDataTable* userDataTable =
         frame->GetProperty(WebRenderUserDataProperty::Key());
 
       MOZ_ASSERT(userDataTable->Count());
 
-      userDataTable->Remove(WebRenderUserDataKey(data->GetDisplayItemKey(), data->GetType()));
+      userDataTable->Remove(WebRenderUserDataKey(data->GetDisplayItemKey(),
+                                                 data->GetType()));
 
       if (!userDataTable->Count()) {
         frame->RemoveProperty(WebRenderUserDataProperty::Key());
         delete userDataTable;
       }
 
       if (data->GetType() == WebRenderUserData::UserDataType::eCanvas) {
         mLastCanvasDatas.RemoveEntry(data->AsCanvasData());
@@ -2116,18 +2156,19 @@ WebRenderCommandBuilder::ClearCachedReso
   RemoveUnusedAndResetWebRenderUserData();
   // UserDatas should only be in the used state during a call to WebRenderCommandBuilder::BuildWebRenderCommands
   // The should always be false upon return from BuildWebRenderCommands().
   MOZ_RELEASE_ASSERT(mWebRenderUserDatas.Count() == 0);
 }
 
 
 
-WebRenderGroupData::WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
-  : WebRenderUserData(aWRManager, aItem)
+WebRenderGroupData::WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                       bool aIsForContentRect)
+  : WebRenderUserData(aWRManager, aItem, aIsForContentRect)
 {
   MOZ_COUNT_CTOR(WebRenderGroupData);
 }
 
 WebRenderGroupData::~WebRenderGroupData()
 {
   MOZ_COUNT_DTOR(WebRenderGroupData);
   GP("Group data destruct\n");
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -32,16 +32,17 @@ class WebRenderUserData;
 
 class WebRenderCommandBuilder {
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderUserData>> WebRenderUserDataRefTable;
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderCanvasData>> CanvasDataSet;
 
 public:
   explicit WebRenderCommandBuilder(WebRenderLayerManager* aManager)
   : mManager(aManager)
+  , mCurrentClipManager(nullptr)
   , mLastAsr(nullptr)
   , mBuilderDumpIndex(0)
   , mDumpIndent(0)
   , mDoGrouping(false)
   , mContainsSVGGroup(false)
   {}
 
   void Destroy();
@@ -50,17 +51,16 @@ public:
 
   bool NeedsEmptyTransaction();
 
   void BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder,
                               wr::IpcResourceUpdateQueue& aResourceUpdates,
                               nsDisplayList* aDisplayList,
                               nsDisplayListBuilder* aDisplayListBuilder,
                               WebRenderScrollData& aScrollData,
-                              wr::LayoutSize& aContentSize,
                               const nsTArray<wr::WrFilterOp>& aFilters);
 
   void PushOverrideForASR(const ActiveScrolledRoot* aASR,
                           const Maybe<wr::WrClipId>& aClipId);
   void PopOverrideForASR(const ActiveScrolledRoot* aASR);
 
   Maybe<wr::ImageKey> CreateImageKey(nsDisplayItem* aItem,
                                      ImageContainer* aContainer,
@@ -124,16 +124,17 @@ public:
   bool GetContainsSVGGroup() { return mContainsSVGGroup; }
 
   // Those are data that we kept between transactions. We used to cache some
   // data in the layer. But in layers free mode, we don't have layer which
   // means we need some other place to cached the data between transaction.
   // We store the data in frame's property.
   template<class T> already_AddRefed<T>
   CreateOrRecycleWebRenderUserData(nsDisplayItem* aItem,
+                                   bool aIsForContentRect,
                                    bool* aOutIsRecycled = nullptr)
   {
     MOZ_ASSERT(aItem);
     nsIFrame* frame = aItem->Frame();
     if (aOutIsRecycled) {
       *aOutIsRecycled = true;
     }
 
@@ -146,40 +147,43 @@ public:
     }
 
     RefPtr<WebRenderUserData>& data = userDataTable->GetOrInsert(WebRenderUserDataKey(aItem->GetPerFrameKey(), T::Type()));
     if (!data) {
       // To recreate a new user data, we should remove the data from the table first.
       if (data) {
         data->RemoveFromTable();
       }
-      data = new T(mManager, aItem);
+      data = new T(mManager, aItem, aIsForContentRect);
       mWebRenderUserDatas.PutEntry(data);
       if (aOutIsRecycled) {
         *aOutIsRecycled = false;
       }
     }
 
     MOZ_ASSERT(data);
     MOZ_ASSERT(data->GetType() == T::Type());
+    MOZ_ASSERT(data->GetIsForContentRect() == aIsForContentRect);
 
     // Mark the data as being used. We will remove unused user data in the end of EndTransaction.
     data->SetUsed(true);
 
     if (T::Type() == WebRenderUserData::UserDataType::eCanvas) {
       mLastCanvasDatas.PutEntry(data->AsCanvasData());
     }
     RefPtr<T> res = static_cast<T*>(data.get());
     return res.forget();
   }
 
   WebRenderLayerManager* mManager;
 
 private:
   ClipManager mClipManager;
+  ClipManager mContentRectClipManager;
+  ClipManager* mCurrentClipManager;
 
   // We use this as a temporary data structure while building the mScrollData
   // inside a layers-free transaction.
   std::vector<WebRenderLayerScrollData> mLayerScrollData;
   // We use this as a temporary data structure to track the current display
   // item's ASR as we recurse in CreateWebRenderCommandsFromDisplayList. We
   // need this so that WebRenderLayerScrollData items that deeper in the
   // tree don't duplicate scroll metadata that their ancestors already have.
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -36,16 +36,17 @@ WebRenderLayerManager::WebRenderLayerMan
   , mLatestTransactionId{0}
   , mWindowOverlayChanged(false)
   , mNeedsComposite(false)
   , mIsFirstPaint(false)
   , mTarget(nullptr)
   , mPaintSequenceNumber(0)
   , mWebRenderCommandBuilder(this)
   , mLastDisplayListSize(0)
+  , mLastContentDisplayListSize(0)
 {
   MOZ_COUNT_CTOR(WebRenderLayerManager);
 }
 
 KnowsCompositor*
 WebRenderLayerManager::AsKnowsCompositor()
 {
   return mWrChild;
@@ -104,16 +105,17 @@ WebRenderLayerManager::DoDestroy(bool aI
     return;
   }
 
   LayerManager::Destroy();
 
   if (WrBridge()) {
     // Just clear ImageKeys, they are deleted during WebRenderAPI destruction.
     mImageKeysToDelete.Clear();
+    mContentRectImageKeysToDelete.Clear();
     // CompositorAnimations are cleared by WebRenderBridgeParent.
     mDiscardedCompositorAnimationsIds.Clear();
     WrBridge()->Destroy(aIsSync);
   }
 
   // Clear this before calling RemoveUnusedAndResetWebRenderUserData(),
   // otherwise that function might destroy some WebRenderAnimationData instances
   // which will put stuff back into mDiscardedCompositorAnimationsIds. If
@@ -276,58 +278,86 @@ WebRenderLayerManager::EndTransactionWit
   AUTO_PROFILER_TRACING("Paint", "RenderLayers");
 
   // Since we don't do repeat transactions right now, just set the time
   mAnimationReadyTime = TimeStamp::Now();
 
   WrBridge()->BeginTransaction();
 
   LayoutDeviceIntSize size = mWidget->GetClientSize();
-  wr::LayoutSize contentSize { (float)size.width, (float)size.height };
-  wr::DisplayListBuilder builder(WrBridge()->GetPipeline(), contentSize, mLastDisplayListSize);
+
+  LayoutDeviceRect contentRect = aDisplayListBuilder ?
+    aDisplayListBuilder->GetContentRenderRootRect() :
+    LayoutDeviceRect();
+  if (contentRect.IsEmpty() && XRE_IsContentProcess()) {
+    contentRect = LayoutDeviceRect(0, 0, size.width, size.height);
+  }
+
+  LayoutDeviceRect chromeRect;
+  if (!contentRect.IsEmpty() || XRE_IsContentProcess()) {
+    chromeRect = LayoutDeviceRect(0, 0,
+                                          size.width,
+                                          contentRect.y);
+  } else {
+    chromeRect = LayoutDeviceRect(0, 0,
+                                          size.width,
+                                          size.height);
+  }
+  wr::LayoutRect chromeLayoutRect = wr::ToRoundedLayoutRect(chromeRect);
+  wr::DisplayListBuilder builder(WrBridge()->GetPipeline(),
+                                 chromeLayoutRect.size,
+                                 mLastDisplayListSize);
+  wr::LayoutRect contentLayoutRect = wr::ToRoundedLayoutRect(contentRect);
+  if (!contentRect.IsEmpty() || XRE_IsContentProcess()) {
+    builder.CreateContentRectBuilder(contentLayoutRect.size, mLastContentDisplayListSize);
+  }
+
   wr::IpcResourceUpdateQueue resourceUpdates(WrBridge());
   wr::usize builderDumpIndex = 0;
   bool containsSVGGroup = false;
   bool dumpEnabled = mWebRenderCommandBuilder.ShouldDumpDisplayList(aDisplayListBuilder);
   if (dumpEnabled) {
     printf_stderr("-- WebRender display list build --\n");
   }
 
+  wr::DisplayListBuilder& startingBuilder =
+    XRE_IsContentProcess() ? builder.ContentRectBuilder() : builder;
+  wr::IpcResourceUpdateQueue& startingResources =
+    XRE_IsContentProcess() ? resourceUpdates.ContentRectUpdateQueue() : resourceUpdates;
   if (aDisplayList) {
     MOZ_ASSERT(aDisplayListBuilder && !aBackground);
     // Record the time spent "layerizing". WR doesn't actually layerize but
     // generating the WR display list is the closest equivalent
     PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Layerization);
 
-    mWebRenderCommandBuilder.BuildWebRenderCommands(builder,
-                                                    resourceUpdates,
+    mWebRenderCommandBuilder.BuildWebRenderCommands(startingBuilder,
+                                                    startingResources,
                                                     aDisplayList,
                                                     aDisplayListBuilder,
                                                     mScrollData,
-                                                    contentSize,
                                                     aFilters);
     builderDumpIndex = mWebRenderCommandBuilder.GetBuilderDumpIndex();
     containsSVGGroup = mWebRenderCommandBuilder.GetContainsSVGGroup();
   } else {
     // ViewToPaint does not have frame yet, then render only background clolor.
     MOZ_ASSERT(!aDisplayListBuilder && aBackground);
-    aBackground->AddWebRenderCommands(builder);
+    aBackground->AddWebRenderCommands(startingBuilder);
     if (dumpEnabled) {
       printf_stderr("(no display list; background only)\n");
-      builderDumpIndex = builder.Dump(/*indent*/ 1, Some(builderDumpIndex), Nothing());
+      builderDumpIndex = startingBuilder.Dump(/*indent*/ 1, Some(builderDumpIndex), Nothing());
     }
   }
 
   DiscardCompositorAnimations();
 
-  mWidget->AddWindowOverlayWebRenderCommands(WrBridge(), builder, resourceUpdates);
+  mWidget->AddWindowOverlayWebRenderCommands(WrBridge(), startingBuilder, startingResources);
   mWindowOverlayChanged = false;
   if (dumpEnabled) {
     printf_stderr("(window overlay)\n");
-    Unused << builder.Dump(/*indent*/ 1, Some(builderDumpIndex), Nothing());
+    Unused << startingBuilder.Dump(/*indent*/ 1, Some(builderDumpIndex), Nothing());
   }
 
   if (AsyncPanZoomEnabled()) {
     mScrollData.SetFocusTarget(mFocusTarget);
     mFocusTarget = FocusTarget();
 
     if (mIsFirstPaint) {
       mScrollData.SetIsFirstPaint();
@@ -339,51 +369,80 @@ WebRenderLayerManager::EndTransactionWit
   // offsets, and we can throw away the pending scroll updates we had kept for
   // an empty transaction.
   ClearPendingScrollInfoUpdate();
 
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(/*aThrottle*/ true);
   TimeStamp refreshStart = mTransactionIdAllocator->GetTransactionStart();
 
   if (mAsyncResourceUpdates) {
-    if (resourceUpdates.IsEmpty()) {
+    if (resourceUpdates.IsEmpty() && !resourceUpdates.HasContentRectUpdateQueue()) {
       resourceUpdates = std::move(mAsyncResourceUpdates.ref());
     } else {
       // If we can't just swap the queue, we need to take the slow path and
       // send the update as a separate message. We don't need to schedule a
       // composite however because that will happen with EndTransaction.
-      WrBridge()->UpdateResources(mAsyncResourceUpdates.ref(),
-                                  /* aScheduleComposite */ false);
+      if (mAsyncResourceUpdates->HasContentRectUpdateQueue()) {
+        WrBridge()->UpdateResources(mAsyncResourceUpdates->ContentRectUpdateQueue(),
+                                    /* aIsForContentRect */ true,
+                                    /* aScheduleComposite */ false);
+      }
+
+      if (!mAsyncResourceUpdates->IsEmpty()) {
+        WrBridge()->UpdateResources(mAsyncResourceUpdates.ref(),
+                                    /* aIsForContentRect */ false,
+                                    /* aScheduleComposite */ false);
+      }
     }
     mAsyncResourceUpdates.reset();
   }
 
   for (const auto& key : mImageKeysToDelete) {
     resourceUpdates.DeleteImage(key);
   }
   mImageKeysToDelete.Clear();
+  for (const auto& key : mContentRectImageKeysToDelete) {
+    resourceUpdates.ContentRectUpdateQueue().DeleteImage(key);
+  }
+  mContentRectImageKeysToDelete.Clear();
 
   WrBridge()->RemoveExpiredFontKeys(resourceUpdates);
+  if (startingResources.HasContentRectUpdateQueue()) {
+    WrBridge()->RemoveExpiredFontKeys(resourceUpdates.ContentRectUpdateQueue());
+  }
 
   // Skip the synchronization for buffer since we also skip the painting during
   // device-reset status.
   if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
     if (WrBridge()->GetSyncObject() &&
         WrBridge()->GetSyncObject()->IsSyncObjectValid()) {
       WrBridge()->GetSyncObject()->Synchronize();
     }
   }
 
   wr::BuiltDisplayList dl;
-  builder.Finalize(contentSize, dl);
+  wr::LayoutSize chromeContentSize;
+  wr::BuiltDisplayList contentDL = {};
+  wr::LayoutSize contentContentSize = {};
+  builder.Finalize(chromeContentSize, dl);
+  if (builder.GetSendContentRectDisplayList() || XRE_IsContentProcess()) {
+    builder.ContentRectBuilder().Finalize(contentContentSize, contentDL);
+  } else {
+    contentRect = LayoutDeviceRect(0,0,0,0);
+  }
+
   mLastDisplayListSize = dl.dl.inner.capacity;
+  mLastContentDisplayListSize = contentDL.dl.inner.capacity;
 
   {
     AUTO_PROFILER_TRACING("Paint", "ForwardDPTransaction");
-    WrBridge()->EndTransaction(contentSize, dl, resourceUpdates, size.ToUnknownSize(),
+    WrBridge()->EndTransaction(chromeContentSize, dl, resourceUpdates,
+                               RoundedToInt(chromeRect).ToUnknownRect(),
+                               contentContentSize, contentDL, resourceUpdates.ContentRectUpdateQueue(),
+                               RoundedToInt(contentRect).ToUnknownRect(),
                                mLatestTransactionId, mScrollData, containsSVGGroup,
                                refreshStart, mTransactionStart);
   }
 
   mTransactionStart = TimeStamp();
 
   MakeSnapshotIfRequired(size);
   mNeedsComposite = false;
@@ -459,30 +518,39 @@ WebRenderLayerManager::MakeSnapshotIfReq
   DrawTarget* dt = mTarget->GetDrawTarget();
   MOZ_RELEASE_ASSERT(dt);
   dt->FillRect(dst, pattern);
 
   mTarget = nullptr;
 }
 
 void
-WebRenderLayerManager::AddImageKeyForDiscard(wr::ImageKey key)
+WebRenderLayerManager::AddImageKeyForDiscard(wr::ImageKey key, bool aIsForContentRect)
 {
-  mImageKeysToDelete.AppendElement(key);
+  if (aIsForContentRect) {
+    mContentRectImageKeysToDelete.AppendElement(key);
+  } else {
+    mImageKeysToDelete.AppendElement(key);
+  }
 }
 
 void
 WebRenderLayerManager::DiscardImages()
 {
   wr::IpcResourceUpdateQueue resources(WrBridge());
   for (const auto& key : mImageKeysToDelete) {
     resources.DeleteImage(key);
   }
   mImageKeysToDelete.Clear();
-  WrBridge()->UpdateResources(resources);
+  for (const auto& key : mContentRectImageKeysToDelete) {
+    resources.ContentRectUpdateQueue().DeleteImage(key);
+  }
+  mContentRectImageKeysToDelete.Clear();
+  WrBridge()->UpdateResources(resources, false);
+  WrBridge()->UpdateResources(resources.ContentRectUpdateQueue(), true);
 }
 
 void
 WebRenderLayerManager::AddActiveCompositorAnimationId(uint64_t aId)
 {
   // In layers-free mode we track the active compositor animation ids on the
   // client side so that we don't try to discard the same animation id multiple
   // times. We could just ignore the multiple-discard on the parent side, but
@@ -514,16 +582,17 @@ WebRenderLayerManager::DiscardCompositor
 
 void
 WebRenderLayerManager::DiscardLocalImages()
 {
   // Removes images but doesn't tell the parent side about them
   // This is useful in empty / failed transactions where we created
   // image keys but didn't tell the parent about them yet.
   mImageKeysToDelete.Clear();
+  mContentRectImageKeysToDelete.Clear();
 }
 
 void
 WebRenderLayerManager::SetLayersObserverEpoch(LayersObserverEpoch aEpoch)
 {
   if (WrBridge()->IPCOpen()) {
     WrBridge()->SendSetLayersObserverEpoch(aEpoch);
   }
@@ -741,17 +810,25 @@ WebRenderLayerManager::FlushAsyncResourc
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mAsyncResourceUpdates) {
     return;
   }
 
   if (!IsDestroyed() && WrBridge()) {
-    WrBridge()->UpdateResources(mAsyncResourceUpdates.ref(),
-                                /* aScheduleComposite */ true);
+    if (!mAsyncResourceUpdates->IsEmpty()) {
+      WrBridge()->UpdateResources(mAsyncResourceUpdates.ref(),
+                                  /* aIsForContentRect */ false,
+                                  /* aScheduleComposite */ true); 
+    }
+    if (mAsyncResourceUpdates->HasContentRectUpdateQueue()) {
+      WrBridge()->UpdateResources(mAsyncResourceUpdates->ContentRectUpdateQueue(),
+                                  /* aIsForContentRect */ true,
+                                  /* aScheduleComposite */ true);
+    }
   }
 
   mAsyncResourceUpdates.reset();
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -126,30 +126,38 @@ public:
 
   virtual already_AddRefed<PersistentBufferProvider>
   CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
 
   bool AsyncPanZoomEnabled() const override;
 
   // adds an imagekey to a list of keys that will be discarded on the next
   // transaction or destruction
-  void AddImageKeyForDiscard(wr::ImageKey);
+  void AddImageKeyForDiscard(wr::ImageKey, bool aIsForContentRect);
   void DiscardImages();
   void DiscardLocalImages();
 
   wr::IpcResourceUpdateQueue& AsyncResourceUpdates();
   void FlushAsyncResourceUpdates();
 
   // Methods to manage the compositor animation ids. Active animations are still
   // going, and when they end we discard them and remove them from the active
   // list.
   void AddActiveCompositorAnimationId(uint64_t aId);
   void AddCompositorAnimationsIdForDiscard(uint64_t aId);
   void DiscardCompositorAnimations();
 
+  void SetNeedsContentRectDisplayList(bool aNeedsContentRectDisplayList) {
+    mNeedsContentRectDisplayList = aNeedsContentRectDisplayList;
+  }
+
+  bool GetNeedsContentRectDisplayList() {
+    return mNeedsContentRectDisplayList;
+  }
+
   WebRenderBridgeChild* WrBridge() const { return mWrChild; }
 
   // See equivalent function in ClientLayerManager
   void LogTestDataForCurrentPaint(ScrollableLayerGuid::ViewID aScrollId,
                                   const std::string& aKey,
                                   const std::string& aValue) {
     MOZ_ASSERT(gfxPrefs::APZTestLoggingEnabled(), "don't call me");
     mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
@@ -177,16 +185,17 @@ private:
    * Take a snapshot of the parent context, and copy
    * it into mTarget.
    */
   void MakeSnapshotIfRequired(LayoutDeviceIntSize aSize);
 
 private:
   nsIWidget* MOZ_NON_OWNING_REF mWidget;
   nsTArray<wr::ImageKey> mImageKeysToDelete;
+  nsTArray<wr::ImageKey> mContentRectImageKeysToDelete;
 
   // 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;
 
@@ -221,14 +230,18 @@ private:
   APZTestData mApzTestData;
 
   TimeStamp mTransactionStart;
   WebRenderCommandBuilder mWebRenderCommandBuilder;
 
   size_t mLastDisplayListSize;
 
   Maybe<wr::IpcResourceUpdateQueue> mAsyncResourceUpdates;
+
+  size_t mLastContentDisplayListSize;
+
+  bool mNeedsContentRectDisplayList;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_WEBRENDERLAYERMANAGER_H */
--- a/gfx/layers/wr/WebRenderUserData.cpp
+++ b/gfx/layers/wr/WebRenderUserData.cpp
@@ -66,22 +66,24 @@ WebRenderUserData::ProcessInvalidateForI
   if (image && image->IsAsyncAnimatedImage()) {
     return true;
   }
 
   aFrame->SchedulePaint();
   return false;
 }
 
-WebRenderUserData::WebRenderUserData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
+WebRenderUserData::WebRenderUserData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                     bool aIsForContentRect)
   : mWRManager(aWRManager)
   , mFrame(aItem->Frame())
   , mDisplayItemKey(aItem->GetPerFrameKey())
   , mTable(aWRManager->GetWebRenderUserDataTable())
   , mUsed(false)
+  , mIsForContentRect(aIsForContentRect)
 {
 }
 
 WebRenderUserData::~WebRenderUserData()
 {
 }
 
 void
@@ -91,45 +93,46 @@ WebRenderUserData::RemoveFromTable()
 }
 
 WebRenderBridgeChild*
 WebRenderUserData::WrBridge() const
 {
   return mWRManager->WrBridge();
 }
 
-WebRenderImageData::WebRenderImageData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
-  : WebRenderUserData(aWRManager, aItem)
+WebRenderImageData::WebRenderImageData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                       bool aIsForContentRect)
+  : WebRenderUserData(aWRManager, aItem, aIsForContentRect)
   , mOwnsKey(false)
 {
 }
 
 WebRenderImageData::~WebRenderImageData()
 {
   ClearImageKey();
 
   if (mPipelineId) {
-    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref(), mIsForContentRect);
   }
 }
 
 bool
 WebRenderImageData::IsAsyncAnimatedImage() const
 {
   return mContainer && mContainer->GetSharedSurfacesAnimation();
 }
 
 void
 WebRenderImageData::ClearImageKey()
 {
   if (mKey) {
     // If we don't own the key, then the owner is responsible for discarding the
     // key when appropriate.
     if (mOwnsKey) {
-      mWRManager->AddImageKeyForDiscard(mKey.value());
+      mWRManager->AddImageKeyForDiscard(mKey.value(), mIsForContentRect);
       if (mTextureOfImage) {
         WrBridge()->ReleaseTextureOfImage(mKey.value());
         mTextureOfImage = nullptr;
       }
     }
     mKey.reset();
   }
   mOwnsKey = false;
@@ -241,29 +244,31 @@ WebRenderImageData::CreateAsyncImageWebR
                                                       const LayoutDeviceRect& aSCBounds,
                                                       const gfx::Matrix4x4& aSCTransform,
                                                       const gfx::MaybeIntSize& aScaleToSize,
                                                       const wr::ImageRendering& aFilter,
                                                       const wr::MixBlendMode& aMixBlendMode,
                                                       bool aIsBackfaceVisible)
 {
   MOZ_ASSERT(aContainer->IsAsync());
+  MOZ_ASSERT(mIsForContentRect == aBuilder.IsContentRectBuilder());
 
   if (mPipelineId.isSome() && mContainer != aContainer) {
     // In this case, we need to remove the existed pipeline and create new one
     // because the ImageContainer is changed.
-    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref(), mIsForContentRect);
     mPipelineId.reset();
   }
 
   if (!mPipelineId) {
     // Alloc async image pipeline id.
     mPipelineId = Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
     WrBridge()->AddPipelineIdForAsyncCompositable(mPipelineId.ref(),
-                                                  aContainer->GetAsyncContainerHandle());
+                                                  aContainer->GetAsyncContainerHandle(),
+                                                  mIsForContentRect);
     mContainer = aContainer;
   }
   MOZ_ASSERT(!mImageClient);
 
   // Push IFrame for async image pipeline.
   //
   // We don't push a stacking context for this async image pipeline here.
   // Instead, we do it inside the iframe that hosts the image. As a result,
@@ -274,17 +279,18 @@ WebRenderImageData::CreateAsyncImageWebR
   wr::LayoutRect r = wr::ToRoundedLayoutRect(aBounds);
   aBuilder.PushIFrame(r, aIsBackfaceVisible, mPipelineId.ref(), /*ignoreMissingPipelines*/ false);
 
   WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(mPipelineId.value(),
                                                                    aSCBounds,
                                                                    aSCTransform,
                                                                    aScaleToSize,
                                                                    aFilter,
-                                                                   aMixBlendMode));
+                                                                   aMixBlendMode),
+                                        mIsForContentRect);
 }
 
 void
 WebRenderImageData::CreateImageClientIfNeeded()
 {
   if (!mImageClient) {
     mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE,
                                                   WrBridge(),
@@ -292,18 +298,19 @@ WebRenderImageData::CreateImageClientIfN
     if (!mImageClient) {
       return;
     }
 
     mImageClient->Connect();
   }
 }
 
-WebRenderFallbackData::WebRenderFallbackData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
-  : WebRenderImageData(aWRManager, aItem)
+WebRenderFallbackData::WebRenderFallbackData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                             bool aIsForContentRect)
+  : WebRenderImageData(aWRManager, aItem, aIsForContentRect)
   , mInvalid(false)
 {
 }
 
 WebRenderFallbackData::~WebRenderFallbackData()
 {
 }
 
@@ -314,35 +321,37 @@ WebRenderFallbackData::GetGeometry()
 }
 
 void
 WebRenderFallbackData::SetGeometry(nsAutoPtr<nsDisplayItemGeometry> aGeometry)
 {
   mGeometry = aGeometry;
 }
 
-WebRenderAnimationData::WebRenderAnimationData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
-  : WebRenderUserData(aWRManager, aItem)
+WebRenderAnimationData::WebRenderAnimationData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                               bool aIsForContentRect)
+  : WebRenderUserData(aWRManager, aItem, aIsForContentRect)
 {
 }
 
 WebRenderAnimationData::~WebRenderAnimationData()
 {
   // It may be the case that nsDisplayItem that created this WebRenderUserData
   // gets destroyed without getting a chance to discard the compositor animation
   // id, so we should do it as part of cleanup here.
   uint64_t animationId = mAnimationInfo.GetCompositorAnimationsId();
   // animationId might be 0 if mAnimationInfo never held any active animations.
   if (animationId) {
     mWRManager->AddCompositorAnimationsIdForDiscard(animationId);
   }
 }
 
-WebRenderCanvasData::WebRenderCanvasData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
-  : WebRenderUserData(aWRManager, aItem)
+WebRenderCanvasData::WebRenderCanvasData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                         bool aIsForContentRect)
+  : WebRenderUserData(aWRManager, aItem, aIsForContentRect)
 {
 }
 
 WebRenderCanvasData::~WebRenderCanvasData()
 {
   if (mCanvasRenderer) {
     mCanvasRenderer->ClearCachedResources();
   }
@@ -358,17 +367,18 @@ WebRenderCanvasRendererAsync*
 WebRenderCanvasData::GetCanvasRenderer()
 {
   return mCanvasRenderer.get();
 }
 
 WebRenderCanvasRendererAsync*
 WebRenderCanvasData::CreateCanvasRenderer()
 {
-  mCanvasRenderer = MakeUnique<WebRenderCanvasRendererAsync>(mWRManager);
+  mCanvasRenderer = MakeUnique<WebRenderCanvasRendererAsync>(mWRManager,
+                                                             mIsForContentRect);
   return mCanvasRenderer.get();
 }
 
 void
 DestroyWebRenderUserDataTable(WebRenderUserDataTable* aTable)
 {
   for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
     iter.UserData()->RemoveFromTable();
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -56,17 +56,18 @@ public:
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderUserData> > WebRenderUserDataRefTable;
 
   static bool SupportsAsyncUpdate(nsIFrame* aFrame);
 
   static bool ProcessInvalidateForImage(nsIFrame* aFrame, DisplayItemType aType);
 
   NS_INLINE_DECL_REFCOUNTING(WebRenderUserData)
 
-  WebRenderUserData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  WebRenderUserData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                    bool aIsForContentRect);
 
   virtual WebRenderImageData* AsImageData() { return nullptr; }
   virtual WebRenderFallbackData* AsFallbackData() { return nullptr; }
   virtual WebRenderCanvasData* AsCanvasData() { return nullptr; }
   virtual WebRenderGroupData* AsGroupData() { return nullptr; }
 
   enum class UserDataType {
     eImage,
@@ -76,28 +77,30 @@ public:
     eGroup,
   };
 
   virtual UserDataType GetType() = 0;
   bool IsUsed() { return mUsed; }
   void SetUsed(bool aUsed) { mUsed = aUsed; }
   nsIFrame* GetFrame() { return mFrame; }
   uint32_t GetDisplayItemKey() { return mDisplayItemKey; }
+  bool GetIsForContentRect() { return mIsForContentRect; }
   void RemoveFromTable();
   virtual nsDisplayItemGeometry* GetGeometry() { return nullptr; }
 protected:
   virtual ~WebRenderUserData();
 
   WebRenderBridgeChild* WrBridge() const;
 
   RefPtr<WebRenderLayerManager> mWRManager;
   nsIFrame* mFrame;
   uint32_t mDisplayItemKey;
   WebRenderUserDataRefTable* mTable;
   bool mUsed;
+  bool mIsForContentRect;
 };
 
 struct WebRenderUserDataKey {
   WebRenderUserDataKey(uint32_t aFrameKey, WebRenderUserData::UserDataType aType)
     : mFrameKey(aFrameKey)
     , mType(aType)
   { }
 
@@ -114,17 +117,18 @@ struct WebRenderUserDataKey {
   WebRenderUserData::UserDataType mType;
 };
 
 typedef nsRefPtrHashtable<nsGenericHashKey<mozilla::layers::WebRenderUserDataKey>, WebRenderUserData> WebRenderUserDataTable;
 
 class WebRenderImageData : public WebRenderUserData
 {
 public:
-  WebRenderImageData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  WebRenderImageData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                     bool aIsForContentRect);
   virtual ~WebRenderImageData();
 
   virtual WebRenderImageData* AsImageData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eImage; }
   static UserDataType Type() { return UserDataType::eImage; }
   Maybe<wr::ImageKey> GetKey() { return mKey; }
   void SetKey(const wr::ImageKey& aKey);
   already_AddRefed<ImageClient> GetImageClient();
@@ -162,17 +166,18 @@ protected:
   Maybe<wr::PipelineId> mPipelineId;
   RefPtr<ImageContainer> mContainer;
   bool mOwnsKey;
 };
 
 class WebRenderFallbackData : public WebRenderImageData
 {
 public:
-  WebRenderFallbackData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  WebRenderFallbackData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                        bool aIsForContentRect);
   virtual ~WebRenderFallbackData();
 
   virtual WebRenderFallbackData* AsFallbackData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eFallback; }
   static UserDataType Type() { return UserDataType::eFallback; }
   nsDisplayItemGeometry* GetGeometry() override;
   void SetGeometry(nsAutoPtr<nsDisplayItemGeometry> aGeometry);
   nsRect GetBounds() { return mBounds; }
@@ -191,42 +196,43 @@ protected:
   bool mInvalid;
   gfx::Size mScale;
   std::vector<RefPtr<gfx::ScaledFont>> mFonts;
 };
 
 class WebRenderAnimationData : public WebRenderUserData
 {
 public:
-  WebRenderAnimationData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  WebRenderAnimationData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                         bool aIsForContentRect);
   virtual ~WebRenderAnimationData();
 
   virtual UserDataType GetType() override { return UserDataType::eAnimation; }
   static UserDataType Type() { return UserDataType::eAnimation; }
   AnimationInfo& GetAnimationInfo() { return mAnimationInfo; }
 
 protected:
   AnimationInfo mAnimationInfo;
 };
 
 class WebRenderCanvasData : public WebRenderUserData
 {
 public:
-  WebRenderCanvasData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  WebRenderCanvasData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                      bool aIsForContentRect);
   virtual ~WebRenderCanvasData();
 
   virtual WebRenderCanvasData* AsCanvasData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eCanvas; }
   static UserDataType Type() { return UserDataType::eCanvas; }
 
   void ClearCanvasRenderer();
   WebRenderCanvasRendererAsync* GetCanvasRenderer();
   WebRenderCanvasRendererAsync* CreateCanvasRenderer();
 protected:
-
   UniquePtr<WebRenderCanvasRendererAsync> mCanvasRenderer;
 };
 
 extern void DestroyWebRenderUserDataTable(WebRenderUserDataTable* aTable);
 
 struct WebRenderUserDataProperty {
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(Key, WebRenderUserDataTable, DestroyWebRenderUserDataTable)
 };
--- a/gfx/thebes/gfxFontMissingGlyphs.cpp
+++ b/gfx/thebes/gfxFontMissingGlyphs.cpp
@@ -160,17 +160,19 @@ public:
         mManager->RemoveUserData(&sWRUserDataKey);
     }
 
     layers::WebRenderLayerManager* mManager;
 
     static UserDataKey sWRUserDataKey;
 };
 
-static RefPtr<SourceSurface> gWRGlyphAtlas[8];
+static const int CONTENT_RECT_GLYPH_ATLAS = 8;
+
+static RefPtr<SourceSurface> gWRGlyphAtlas[16];
 static LinkedList<WRUserData> gWRUsers;
 UserDataKey WRUserData::sWRUserDataKey;
 
 /**
  * Generates a transformed WebRender mini-font atlas for a given orientation.
  */
 static already_AddRefed<SourceSurface>
 MakeWRGlyphAtlas(const Matrix* aMat)
@@ -215,51 +217,52 @@ MakeWRGlyphAtlas(const Matrix* aMat)
 static void
 PurgeWRGlyphAtlas()
 {
     // For each WR layer manager, we need go through each atlas orientation
     // and see if it has a stashed image key. If it does, remove the image
     // from the layer manager.
     for (WRUserData* user : gWRUsers) {
         auto* manager = user->mManager;
-        for (size_t i = 0; i < 8; i++) {
+        for (size_t i = 0; i < 16; i++) {
             if (gWRGlyphAtlas[i]) {
                 uint32_t handle =
                     (uint32_t)(uintptr_t)gWRGlyphAtlas[i]->GetUserData(
                         reinterpret_cast<UserDataKey*>(manager));
                 if (handle) {
                     manager->AddImageKeyForDiscard(
-                        wr::ImageKey{manager->WrBridge()->GetNamespace(), handle});
+                        wr::ImageKey{manager->WrBridge()->GetNamespace(), handle},
+                        i & CONTENT_RECT_GLYPH_ATLAS);
                 }
             }
         }
     }
     // Remove the layer managers' destroy notifications only after processing
     // so as not to mess up gWRUsers iteration.
     while (!gWRUsers.isEmpty()) {
         gWRUsers.popFirst()->Remove();
     }
     // Finally, clear out the atlases.
-    for (size_t i = 0; i < 8; i++) {
+    for (size_t i = 0; i < 16; i++) {
         gWRGlyphAtlas[i] = nullptr;
     }
 }
 
 WRUserData::WRUserData(layers::WebRenderLayerManager* aManager)
     : mManager(aManager)
 {
     gWRUsers.insertFront(this);
 }
 
 WRUserData::~WRUserData()
 {
     // When the layer manager is destroyed, we need go through each
     // atlas and remove any assigned image keys.
     if (isInList()) {
-        for (size_t i = 0; i < 8; i++) {
+        for (size_t i = 0; i < 16; i++) {
             if (gWRGlyphAtlas[i]) {
                 gWRGlyphAtlas[i]->RemoveUserData(reinterpret_cast<UserDataKey*>(mManager));
             }
         }
     }
 }
 
 static already_AddRefed<SourceSurface>
@@ -269,25 +272,30 @@ GetWRGlyphAtlas(DrawTarget& aDrawTarget,
     // Encode orientation in the key.
     if (aMat) {
         if (aMat->_11 == 0) {
             key |= 4 | (aMat->_12 < 0 ? 1 : 0) | (aMat->_21 < 0 ? 2 : 0);
         } else {
             key |= (aMat->_11 < 0 ? 1 : 0) | (aMat->_22 < 0 ? 2 : 0);
         }
     }
+    // The atlas may exist, but an image key may not be assigned for it to
+    // the given layer manager.
+    auto* tdt = static_cast<layout::TextDrawTarget*>(&aDrawTarget);
+    if (tdt->IsForContentRect()) {
+        key |= CONTENT_RECT_GLYPH_ATLAS;
+    }
+
     // Check if an atlas was already created, or create one if necessary.
     RefPtr<SourceSurface> atlas = gWRGlyphAtlas[key];
     if (!atlas) {
         atlas = MakeWRGlyphAtlas(aMat);
         gWRGlyphAtlas[key] = atlas;
     }
-    // The atlas may exist, but an image key may not be assigned for it to
-    // the given layer manager.
-    auto* tdt = static_cast<layout::TextDrawTarget*>(&aDrawTarget);
+
     auto* manager = tdt->WrLayerManager();
     if (!atlas->GetUserData(reinterpret_cast<UserDataKey*>(manager))) {
         // No image key, so we need to map the atlas' data for transfer to WR.
         RefPtr<DataSourceSurface> dataSurface = atlas->GetDataSurface();
         if (!dataSurface) {
             return nullptr;
         }
         DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ);
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2797,24 +2797,26 @@ gfxPlatform::InitWebRenderConfig()
   if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
     featureWebRender.ForceDisable(
       FeatureStatus::Unavailable,
       "Hardware compositing is disabled",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBRENDER_NEED_HWCOMP"));
   }
 
   // WebRender relies on the GPU process when on Windows
-#ifdef XP_WIN
-  if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
-    featureWebRender.ForceDisable(
-      FeatureStatus::Unavailable,
-      "GPU Process is disabled",
-      NS_LITERAL_CSTRING("FEATURE_FAILURE_GPU_PROCESS_DISABLED"));
-  }
-#endif
+// TODO (Woops if this made it into a review)!!!!
+//
+// #ifdef XP_WIN
+//   if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
+//     featureWebRender.ForceDisable(
+//       FeatureStatus::Unavailable,
+//       "GPU Process is disabled",
+//       NS_LITERAL_CSTRING("FEATURE_FAILURE_GPU_PROCESS_DISABLED"));
+//   }
+// #endif
 
   if (InSafeMode()) {
     featureWebRender.ForceDisable(
       FeatureStatus::Unavailable,
       "Safe-mode is enabled",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_SAFE_MODE"));
   }
 
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -1,16 +1,16 @@
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter, ClipAndScrollInfo};
 use api::{ClipId, ColorF, ComplexClipRegion, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use api::{DisplayItemRef, ExtendMode, ExternalScrollId};
+use api::{DisplayItemRef, DocumentId, ExtendMode, ExternalScrollId};
 use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, RasterSpace, GradientStop};
 use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint, ColorDepth};
 use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
 use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId};
 use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity};
 use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
 use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData};
 use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore, ClipItemSceneData};
@@ -105,16 +105,19 @@ impl ClipIdToIndexMapper {
         }
     }
 }
 
 /// A structure that converts a serialized display list into a form that WebRender
 /// can use to later build a frame. This structure produces a FrameBuilder. Public
 /// members are typically those that are destructured into the FrameBuilder.
 pub struct DisplayListFlattener<'a> {
+    /// The document ID for the scene that we are flattening.
+    document_id: DocumentId,
+
     /// The scene that we are currently flattening.
     scene: &'a Scene,
 
     /// The ClipScrollTree that we are currently building during flattening.
     clip_scroll_tree: &'a mut ClipScrollTree,
 
     /// A counter for generating unique picture ids.
     picture_id_generator: &'a mut PictureIdGenerator,
@@ -161,16 +164,17 @@ pub struct DisplayListFlattener<'a> {
 
     /// The root picture index for this flattener. This is the picture
     /// to start the culling phase from.
     pub root_pic_index: PictureIndex,
 }
 
 impl<'a> DisplayListFlattener<'a> {
     pub fn create_frame_builder(
+        document_id: DocumentId,
         scene: &Scene,
         clip_scroll_tree: &mut ClipScrollTree,
         font_instances: FontInstanceMap,
         view: &DocumentView,
         output_pipelines: &FastHashSet<PipelineId>,
         frame_builder_config: &FrameBuilderConfig,
         new_scene: &mut Scene,
         picture_id_generator: &mut PictureIdGenerator,
@@ -180,16 +184,17 @@ impl<'a> DisplayListFlattener<'a> {
         let root_pipeline_id = scene.root_pipeline_id.unwrap();
         let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
 
         let background_color = root_pipeline
             .background_color
             .and_then(|color| if color.a > 0.0 { Some(color) } else { None });
 
         let mut flattener = DisplayListFlattener {
+            document_id,
             scene,
             clip_scroll_tree,
             font_instances,
             config: *frame_builder_config,
             output_pipelines,
             id_to_index_mapper: ClipIdToIndexMapper::default(),
             hit_testing_runs: Vec::new(),
             pending_shadow_items: VecDeque::new(),
@@ -460,16 +465,18 @@ impl<'a> DisplayListFlattener<'a> {
         info: &IframeDisplayItem,
         clip_and_scroll_ids: &ClipAndScrollInfo,
         reference_frame_relative_offset: &LayoutVector2D,
     ) {
         let iframe_pipeline_id = info.pipeline_id;
         let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
             Some(pipeline) => pipeline,
             None => {
+                println!("Missing pipeline - {:?} {:?}", info,
+                         iframe_pipeline_id);
                 debug_assert!(info.ignore_missing_pipeline);
                 return
             },
         };
 
         //TODO: use or assert on `clip_and_scroll_ids.clip_node_id` ?
         let clip_chain_index = self.add_clip_node(
             info.clip_id,
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -426,17 +426,17 @@ impl FrameBuilder {
                 has_texture_cache_tasks |= !texture_cache.is_empty();
             }
         }
 
         let gpu_cache_frame_id = gpu_cache.end_frame(gpu_cache_profile);
 
         render_tasks.write_task_data(device_pixel_scale);
 
-        resource_cache.end_frame();
+        resource_cache.end_frame(texture_cache_profile);
 
         Frame {
             window_size: self.window_size,
             inner_rect: self.screen_rect,
             device_pixel_ratio: device_pixel_scale.0,
             background_color: self.background_color,
             layer,
             profile_counters,
--- a/gfx/webrender/src/gpu_cache.rs
+++ b/gfx/webrender/src/gpu_cache.rs
@@ -19,19 +19,20 @@
 //! data is not in the cache, the user provided closure
 //! will be invoked to build the data.
 //!
 //! After ```end_frame``` has occurred, callers can
 //! use the ```get_address``` API to get the allocated
 //! address in the GPU cache of a given resource slot
 //! for this frame.
 
-use api::{PremultipliedColorF, TexelRect};
+use api::{DocumentId, PremultipliedColorF, TexelRect};
 use api::{VoidPtrToSizeFn};
 use euclid::TypedRect;
+use internal_types::{FastHashMap};
 use profiler::GpuCacheProfileCounters;
 use render_backend::FrameId;
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use std::{mem, u16, u32};
 use std::ops::Add;
 use std::os::raw::c_void;
 
 
@@ -323,17 +324,17 @@ struct Texture {
     rows: Vec<Row>,
     // Free lists of available blocks for each supported
     // block size in the texture. These are intrusive
     // linked lists.
     free_lists: FreeBlockLists,
     // Linked list of currently occupied blocks. This
     // makes it faster to iterate blocks looking for
     // candidates to be evicted from the cache.
-    occupied_list_head: Option<BlockIndex>,
+    occupied_list_heads: FastHashMap<DocumentId, BlockIndex>,
     // Pending blocks that have been written this frame
     // and will need to be sent to the GPU.
     pending_blocks: Vec<GpuBlockData>,
     // Pending update commands.
     updates: Vec<GpuCacheUpdate>,
     // Profile stats
     allocated_block_count: usize,
 }
@@ -342,17 +343,17 @@ impl Texture {
     fn new() -> Self {
         Texture {
             height: GPU_CACHE_INITIAL_HEIGHT,
             blocks: Vec::new(),
             rows: Vec::new(),
             free_lists: FreeBlockLists::new(),
             pending_blocks: Vec::new(),
             updates: Vec::new(),
-            occupied_list_head: None,
+            occupied_list_heads: FastHashMap::default(),
             allocated_block_count: 0,
         }
     }
 
     // Reports the CPU heap usage of this Texture struct.
     fn malloc_size_of(&self, op: VoidPtrToSizeFn) -> usize {
         let mut size = 0;
         unsafe {
@@ -406,19 +407,19 @@ impl Texture {
         // Given the code above, it's now guaranteed that there is a block
         // available in the appropriate free-list. Pull a block from the
         // head of the list.
         let free_block_index = free_list.take().unwrap();
         let block = &mut self.blocks[free_block_index.0 as usize];
         *free_list = block.next;
 
         // Add the block to the occupied linked list.
-        block.next = self.occupied_list_head;
+        block.next = self.occupied_list_heads.get(&frame_id.document_id).map(|x| *x);
         block.last_access_time = frame_id;
-        self.occupied_list_head = Some(free_block_index);
+        self.occupied_list_heads.insert(frame_id.document_id, free_block_index);
         self.allocated_block_count += alloc_size;
 
         if let Some(pending_block_index) = pending_block_index {
             // Add this update to the pending list of blocks that need
             // to be updated on the GPU.
             self.updates.push(GpuCacheUpdate::Copy {
                 block_index: pending_block_index,
                 block_count,
@@ -427,36 +428,65 @@ impl Texture {
         }
 
         CacheLocation {
             block_index: free_block_index,
             epoch: block.epoch,
         }
     }
 
+    // Evict everything associated with the given document.
+    pub fn cleanup_for_document(&mut self, document_id: DocumentId) {
+        let mut current_block = self.occupied_list_heads.get(&document_id).map(|x| *x);
+
+        while let Some(index) = current_block {
+            let block = &mut self.blocks[index.0 as usize];
+            let next_block = block.next;
+
+            // Get the row metadata from the address.
+            let row = &mut self.rows[block.address.v as usize];
+
+            // Use the row metadata to determine which free-list
+            // this block belongs to.
+            let (_, free_list) = self.free_lists
+                .get_actual_block_count_and_free_list(row.block_count_per_item);
+
+            block.epoch.next();
+            block.next = *free_list;
+            *free_list = Some(index);
+
+            self.allocated_block_count -= row.block_count_per_item;
+
+            current_block = next_block;
+        }
+
+        self.occupied_list_heads.remove(&document_id);
+    }
+
     // Run through the list of occupied cache blocks and evict
     // any old blocks that haven't been referenced for a while.
     fn evict_old_blocks(&mut self, frame_id: FrameId) {
         // Prune any old items from the list to make room.
         // Traverse the occupied linked list and see
         // which items have not been used for a long time.
-        let mut current_block = self.occupied_list_head;
+        let mut current_block = self.occupied_list_heads.get(&frame_id.document_id).map(|x| *x);
         let mut prev_block: Option<BlockIndex> = None;
+        let frame_index = frame_id.frame_index;
 
         while let Some(index) = current_block {
             let (next_block, should_unlink) = {
                 let block = &mut self.blocks[index.0 as usize];
 
                 let next_block = block.next;
                 let mut should_unlink = false;
 
                 // If this resource has not been used in the last
                 // few frames, free it from the texture and mark
                 // as empty.
-                if block.last_access_time + FRAMES_BEFORE_EVICTION < frame_id {
+                if block.last_access_time.frame_index + FRAMES_BEFORE_EVICTION < frame_index {
                     should_unlink = true;
 
                     // Get the row metadata from the address.
                     let row = &mut self.rows[block.address.v as usize];
 
                     // Use the row metadata to determine which free-list
                     // this block belongs to.
                     let (_, free_list) = self.free_lists
@@ -475,17 +505,24 @@ impl Texture {
             // If the block was released, we will need to remove it
             // from the occupied linked list.
             if should_unlink {
                 match prev_block {
                     Some(prev_block) => {
                         self.blocks[prev_block.0 as usize].next = next_block;
                     }
                     None => {
-                        self.occupied_list_head = next_block;
+                        match next_block {
+                            Some(next_block) => {
+                                self.occupied_list_heads.insert(frame_id.document_id, next_block);
+                            }
+                            None => {
+                                self.occupied_list_heads.remove(&frame_id.document_id);
+                            }
+                        }
                     }
                 }
             } else {
                 prev_block = current_block;
             }
 
             current_block = next_block;
         }
@@ -551,16 +588,21 @@ impl GpuCache {
         GpuCache {
             frame_id: FrameId::invalid(),
             texture: Texture::new(),
             saved_block_count: 0,
             in_debug: false,
         }
     }
 
+    /// Evict all the items for the given document
+    pub fn cleanup_for_document(&mut self, document_id: DocumentId) {
+        self.texture.cleanup_for_document(document_id);
+    }
+
     /// Begin a new frame.
     pub fn begin_frame(&mut self, frame_id: FrameId) {
         debug_assert!(self.texture.pending_blocks.is_empty());
         self.frame_id = frame_id;
         self.texture.evict_old_blocks(self.frame_id);
         self.saved_block_count = 0;
     }
 
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -75,62 +75,56 @@ impl DocumentView {
         DevicePixelScale::new(
             self.device_pixel_ratio *
             self.page_zoom_factor *
             self.pinch_zoom_factor
         )
     }
 }
 
-#[derive(Copy, Clone, Hash, PartialEq, PartialOrd, Debug, Eq, Ord)]
+#[derive(Copy, Clone, Hash, Debug, Eq, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct FrameId(usize);
+pub struct FrameId {
+    pub frame_index: usize,
+    pub document_id: DocumentId,
+}
 
 impl FrameId {
     /// Returns an invalid sentinel FrameId, which will always compare less than
     /// any valid FrameId.
     pub fn invalid() -> Self {
-        FrameId(0)
+        FrameId {
+            frame_index: 0,
+            document_id: DocumentId(IdNamespace(0), 0),
+        }
     }
 
     /// Returns a FrameId corresponding to the first frame.
     ///
     /// Note that we use 0 as the internal id here because the current code
     /// increments the frame id at the beginning of the frame, rather than
     /// at the end, and we want the first frame to be 1. It would probably
     /// be sensible to move the advance() call to after frame-building, and
     /// then make this method return FrameId(1).
-    pub fn first() -> Self {
-        FrameId(0)
+    pub fn first(document_id: DocumentId) -> Self {
+        FrameId {
+            frame_index: 0,
+            document_id,
+        }
     }
 
-    /// Returns the backing usize for this FrameId.
-    pub fn as_usize(&self) -> usize {
-        self.0
-    }
+    // /// Returns the backing usize for this FrameId.
+    // pub fn as_usize(&self) -> usize {
+    //     self.0
+    // }
 
     /// Advances this FrameId to the next frame.
     fn advance(&mut self) {
-        self.0 += 1;
-    }
-}
-
-impl ::std::ops::Add<usize> for FrameId {
-    type Output = Self;
-    fn add(self, other: usize) -> FrameId {
-        FrameId(self.0 + other)
-    }
-}
-
-impl ::std::ops::Sub<usize> for FrameId {
-    type Output = Self;
-    fn sub(self, other: usize) -> FrameId {
-        assert!(self.0 >= other, "Underflow subtracting FrameIds");
-        FrameId(self.0 - other)
+        self.frame_index += 1;
     }
 }
 
 // A collection of resources that are shared by clips, primitives
 // between display lists.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct FrameResources {
@@ -194,16 +188,17 @@ struct Document {
     // renderer.
     has_built_scene: bool,
 
     resources: FrameResources,
 }
 
 impl Document {
     pub fn new(
+        id: DocumentId,
         window_size: DeviceUintSize,
         layer: DocumentLayer,
         default_device_pixel_ratio: f32,
     ) -> Self {
         Document {
             scene: Scene::new(),
             removed_pipelines: Vec::new(),
             view: DocumentView {
@@ -211,17 +206,17 @@ impl Document {
                 inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), window_size),
                 layer,
                 pan: DeviceIntPoint::zero(),
                 page_zoom_factor: 1.0,
                 pinch_zoom_factor: 1.0,
                 device_pixel_ratio: default_device_pixel_ratio,
             },
             clip_scroll_tree: ClipScrollTree::new(),
-            frame_id: FrameId::first(),
+            frame_id: FrameId::first(id),
             frame_builder: None,
             output_pipelines: FastHashSet::default(),
             hit_tester: None,
             dynamic_properties: SceneProperties::new(),
             frame_is_valid: false,
             hit_tester_is_valid: false,
             rendered_frame_is_valid: false,
             has_built_scene: false,
@@ -706,16 +701,17 @@ impl RenderBackend {
                         self.update_document(
                             txn.document_id,
                             replace(&mut txn.resource_updates, Vec::new()),
                             txn.doc_resource_updates.take(),
                             replace(&mut txn.frame_ops, Vec::new()),
                             replace(&mut txn.notifications, Vec::new()),
                             txn.render_frame,
                             txn.invalidate_rendered_frame,
+                            txn.skip_frame_notification,
                             &mut frame_counter,
                             &mut profile_counters,
                             has_built_scene,
                         );
                     },
                     SceneBuilderResult::FlushComplete(tx) => {
                         tx.send(()).ok();
                     }
@@ -815,16 +811,17 @@ impl RenderBackend {
                 sender.send(self.next_namespace_id()).unwrap();
             }
             ApiMsg::CloneApiByClient(namespace_id) => {
                 assert!(self.namespace_alloc_by_client);
                 debug_assert!(!self.documents.iter().any(|(did, _doc)| did.0 == namespace_id));
             }
             ApiMsg::AddDocument(document_id, initial_size, layer) => {
                 let document = Document::new(
+                    document_id,
                     initial_size,
                     layer,
                     self.default_device_pixel_ratio,
                 );
                 self.documents.insert(document_id, document);
             }
             ApiMsg::DeleteDocument(document_id) => {
                 self.documents.remove(&document_id);
@@ -987,16 +984,17 @@ impl RenderBackend {
             blob_requests: Vec::new(),
             resource_updates: transaction_msg.resource_updates,
             frame_ops: transaction_msg.frame_ops,
             rasterized_blobs: Vec::new(),
             notifications: transaction_msg.notifications,
             set_root_pipeline: None,
             render_frame: transaction_msg.generate_frame,
             invalidate_rendered_frame: transaction_msg.invalidate_rendered_frame,
+            skip_frame_notification: transaction_msg.skip_frame_notification,
         });
 
         self.resource_cache.pre_scene_building_update(
             &mut txn.resource_updates,
             &mut profile_counters.resources,
         );
 
         for scene_msg in transaction_msg.scene_ops.drain(..) {
@@ -1023,16 +1021,17 @@ impl RenderBackend {
             self.update_document(
                 txn.document_id,
                 replace(&mut txn.resource_updates, Vec::new()),
                 None,
                 replace(&mut txn.frame_ops, Vec::new()),
                 replace(&mut txn.notifications, Vec::new()),
                 txn.render_frame,
                 txn.invalidate_rendered_frame,
+                txn.skip_frame_notification,
                 frame_counter,
                 profile_counters,
                 false
             );
 
             return;
         }
 
@@ -1059,16 +1058,17 @@ impl RenderBackend {
         &mut self,
         document_id: DocumentId,
         resource_updates: Vec<ResourceUpdate>,
         doc_resource_updates: Option<DocumentResourceUpdates>,
         mut frame_ops: Vec<FrameMsg>,
         mut notifications: Vec<NotificationRequest>,
         mut render_frame: bool,
         invalidate_rendered_frame: bool,
+        skip_frame_notification: bool,
         frame_counter: &mut u32,
         profile_counters: &mut BackendProfileCounters,
         has_built_scene: bool,
     ) {
         let requested_frame = render_frame;
 
         // If we have a sampler, get more frame ops from it and add them
         // to the transaction. This is a hook to allow the WR user code to
@@ -1192,17 +1192,17 @@ impl RenderBackend {
 
         if !notifications.is_empty() {
             self.result_tx.send(ResultMsg::AppendNotificationRequests(notifications)).unwrap();
         }
 
         // Always forward the transaction to the renderer if a frame was requested,
         // otherwise gecko can get into a state where it waits (forever) for the
         // transaction to complete before sending new work.
-        if requested_frame {
+        if requested_frame && !skip_frame_notification {
             // If rendered frame is already valid, there is no need to render frame.
             if doc.rendered_frame_is_valid {
                 render_frame = false;
             } else if render_frame {
                 doc.rendered_frame_is_valid = true;
             }
             self.notifier.new_frame_ready(document_id, scroll, render_frame, frame_build_time);
         }
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -1552,17 +1552,17 @@ pub struct Renderer {
     prim_header_f_texture: VertexDataTexture,
     prim_header_i_texture: VertexDataTexture,
     transforms_texture: VertexDataTexture,
     render_task_texture: VertexDataTexture,
     gpu_cache_texture: GpuCacheTexture,
     #[cfg(feature = "debug_renderer")]
     gpu_cache_debug_chunks: Vec<GpuDebugChunk>,
 
-    gpu_cache_frame_id: FrameId,
+    frame_indices: FastHashMap<DocumentId, usize>,
     gpu_cache_overflow: bool,
 
     pipeline_info: PipelineInfo,
 
     // Manages and resolves source textures IDs to real texture IDs.
     texture_resolver: TextureResolver,
 
     // A PBO used to do asynchronous texture cache uploads.
@@ -2017,17 +2017,17 @@ impl Renderer {
             output_image_handler: None,
             size_of_op: options.size_of_op,
             output_targets: FastHashMap::default(),
             cpu_profiles: VecDeque::new(),
             gpu_profiles: VecDeque::new(),
             gpu_cache_texture,
             #[cfg(feature = "debug_renderer")]
             gpu_cache_debug_chunks: Vec::new(),
-            gpu_cache_frame_id: FrameId::invalid(),
+            frame_indices: FastHashMap::default(),
             gpu_cache_overflow: false,
             texture_cache_upload_pbo,
             texture_resolver,
             renderer_errors: Vec::new(),
             #[cfg(feature = "capture")]
             read_fbo,
             #[cfg(feature = "replay")]
             owned_external_images: FastHashMap::default(),
@@ -2596,19 +2596,19 @@ impl Renderer {
             #[cfg(feature = "replay")]
             self.texture_resolver.external_images.extend(
                 self.owned_external_images.iter().map(|(key, value)| (*key, value.clone()))
             );
 
             for &mut (_, RenderedDocument { ref mut frame, .. }) in &mut active_documents {
                 frame.profile_counters.reset_targets();
                 self.prepare_gpu_cache(frame);
-                assert!(frame.gpu_cache_frame_id <= self.gpu_cache_frame_id,
-                    "Received frame depends on a later GPU cache epoch ({:?}) than one we received last via `UpdateGpuCache` ({:?})",
-                    frame.gpu_cache_frame_id, self.gpu_cache_frame_id);
+                assert!(frame.gpu_cache_frame_id.frame_index <= *self.frame_indices.get(&frame.gpu_cache_frame_id.document_id).unwrap_or(&0),
+                     "Received frame depends on a later GPU cache epoch ({:?}) than one we received last via `UpdateGpuCache` ({:?})",
+                    frame.gpu_cache_frame_id, self.frame_indices);
 
                 self.draw_tile_frame(
                     frame,
                     framebuffer_size,
                     clear_depth_value.is_some(),
                     cpu_frame_id,
                     &mut stats
                 );
@@ -2762,18 +2762,19 @@ impl Renderer {
         self.gpu_cache_texture.prepare_for_updates(
             &mut self.device,
             updated_blocks,
             max_requested_height,
         );
 
         for update_list in self.pending_gpu_cache_updates.drain(..) {
             assert!(update_list.height <= max_requested_height);
-            if update_list.frame_id > self.gpu_cache_frame_id {
-                self.gpu_cache_frame_id = update_list.frame_id
+            let frame_index = self.frame_indices.entry(update_list.frame_id.document_id).or_insert(0);
+            if update_list.frame_id.frame_index > *frame_index {
+                *frame_index = update_list.frame_id.frame_index;
             }
             self.gpu_cache_texture
                 .update(&mut self.device, &update_list);
         }
 
         let updated_rows = self.gpu_cache_texture.flush(&mut self.device);
 
         let counters = &mut self.backend_profile_counters.resources.gpu_cache;
@@ -4705,17 +4706,17 @@ struct PlainTexture {
 }
 
 
 #[cfg(any(feature = "capture", feature = "replay"))]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct PlainRenderer {
     gpu_cache: PlainTexture,
-    gpu_cache_frame_id: FrameId,
+    frame_indices: FastHashMap<DocumentId, usize>,
     textures: FastHashMap<CacheTextureId, PlainTexture>,
     external_images: Vec<ExternalCaptureImage>
 }
 
 #[cfg(feature = "replay")]
 enum CapturedExternalImageData {
     NativeTexture(gl::GLuint),
     Buffer(Arc<Vec<u8>>),
@@ -4936,17 +4937,17 @@ impl Renderer {
 
             info!("saving GPU cache");
             self.update_gpu_cache(); // flush pending updates
             let mut plain_self = PlainRenderer {
                 gpu_cache: Self::save_texture(
                     &self.gpu_cache_texture.texture.as_ref().unwrap(),
                     "gpu", &config.root, &mut self.device,
                 ),
-                gpu_cache_frame_id: self.gpu_cache_frame_id,
+                frame_indices: self.frame_indices.clone(),
                 textures: FastHashMap::default(),
                 external_images: deferred_images,
             };
 
             info!("saving cached textures");
             for (id, texture) in &self.texture_resolver.texture_cache_map {
                 let file_name = format!("cache-{}", plain_self.textures.len() + 1);
                 info!("\t{}", file_name);
@@ -5040,17 +5041,17 @@ impl Renderer {
                     // fill up the CPU cache from the contents we just loaded
                     rows.clear();
                     cpu_blocks.clear();
                     rows.extend((0 .. dim.height).map(|_| CacheRow::new()));
                     cpu_blocks.extend_from_slice(blocks);
                 }
                 GpuCacheBus::Scatter { .. } => {}
             }
-            self.gpu_cache_frame_id = renderer.gpu_cache_frame_id;
+            self.frame_indices = renderer.frame_indices;
 
             info!("loading external texture-backed images");
             let mut native_map = FastHashMap::<String, gl::GLuint>::default();
             for ExternalCaptureImage { short_path, external, descriptor } in renderer.external_images {
                 let target = match external.image_type {
                     ExternalImageType::TextureHandle(target) => target,
                     ExternalImageType::Buffer => continue,
                 };
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{AddFont, BlobImageResources, AsyncBlobImageRasterizer, ResourceUpdate};
 use api::{BlobImageDescriptor, BlobImageHandler, BlobImageRequest, RasterizedBlobImage};
 use api::{ClearCache, ColorF, DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
-use api::{FontInstanceKey, FontKey, FontTemplate, GlyphIndex};
+use api::{DocumentId, FontInstanceKey, FontKey, FontTemplate, GlyphIndex};
 use api::{ExternalImageData, ExternalImageType, BlobImageResult, BlobImageParams};
 use api::{FontInstanceData, FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
 use api::{GlyphDimensions, IdNamespace};
 use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
 use api::{MemoryReport, VoidPtrToSizeFn};
 use api::{TileOffset, TileSize, TileRange, BlobImageData};
 use app_units::Au;
 #[cfg(feature = "capture")]
@@ -401,17 +401,17 @@ pub type GlyphDimensionsCache = FastHash
 /// managed by the OS or other parts of WebRender.
 pub struct ResourceCache {
     cached_glyphs: GlyphCache,
     cached_images: ImageCache,
     cached_render_tasks: RenderTaskCache,
 
     resources: Resources,
     state: State,
-    current_frame_id: FrameId,
+    current_frame_indices: FastHashMap<DocumentId, usize>,
 
     texture_cache: TextureCache,
 
     // TODO(gw): We should expire (parts of) this cache semi-regularly!
     cached_glyph_dimensions: GlyphDimensionsCache,
     glyph_rasterizer: GlyphRasterizer,
 
     // The set of images that aren't present or valid in the texture cache,
@@ -439,17 +439,17 @@ impl ResourceCache {
         ResourceCache {
             cached_glyphs: GlyphCache::new(),
             cached_images: ResourceClassCache::new(),
             cached_render_tasks: RenderTaskCache::new(),
             resources: Resources::default(),
             cached_glyph_dimensions: FastHashMap::default(),
             texture_cache,
             state: State::Idle,
-            current_frame_id: FrameId::invalid(),
+            current_frame_indices: FastHashMap::default(),
             pending_image_requests: FastHashSet::default(),
             glyph_rasterizer,
             blob_image_handler,
             rasterized_blob_images: FastHashMap::default(),
             blob_image_templates: FastHashMap::default(),
             missing_blob_images: Vec::new(),
             blob_image_rasterizer: None,
         }
@@ -1414,17 +1414,17 @@ impl ResourceCache {
     }
 
     pub fn begin_frame(&mut self, frame_id: FrameId) {
         debug_assert_eq!(self.state, State::Idle);
         self.state = State::AddResources;
         self.texture_cache.begin_frame(frame_id);
         self.cached_glyphs.begin_frame(&self.texture_cache, &self.cached_render_tasks, &mut self.glyph_rasterizer);
         self.cached_render_tasks.begin_frame(&mut self.texture_cache);
-        self.current_frame_id = frame_id;
+        self.current_frame_indices.insert(frame_id.document_id, frame_id.frame_index);
     }
 
     pub fn block_until_all_resources_added(
         &mut self,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskTree,
         texture_cache_profile: &mut TextureCacheProfileCounters,
     ) {
@@ -1447,17 +1447,16 @@ impl ResourceCache {
         // Apply any updates of new / updated images (incl. blobs) to the texture cache.
         self.update_texture_cache(gpu_cache);
         render_tasks.prepare_for_render();
         self.cached_render_tasks.update(
             gpu_cache,
             &mut self.texture_cache,
             render_tasks,
         );
-        self.texture_cache.end_frame(texture_cache_profile);
     }
 
     fn rasterize_missing_blob_images(&mut self) {
         if self.missing_blob_images.is_empty() {
             return;
         }
 
         self.blob_image_handler
@@ -1611,19 +1610,20 @@ impl ResourceCache {
                     None,
                     UvRectKind::Rect,
                     eviction,
                 );
             }
         }
     }
 
-    pub fn end_frame(&mut self) {
+    pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) {
         debug_assert_eq!(self.state, State::QueryResources);
         self.state = State::Idle;
+        self.texture_cache.end_frame(texture_cache_profile);
     }
 
     pub fn clear(&mut self, what: ClearCache) {
         if what.contains(ClearCache::IMAGES) {
             for (_key, mut cached) in self.cached_images.resources.drain() {
                 cached.drop_from_cache(&mut self.texture_cache);
             }
         }
@@ -1794,28 +1794,28 @@ pub struct PlainResources {
     font_templates: FastHashMap<FontKey, PlainFontTemplate>,
     font_instances: FastHashMap<FontInstanceKey, FontInstance>,
     image_templates: FastHashMap<ImageKey, PlainImageTemplate>,
 }
 
 #[cfg(feature = "capture")]
 #[derive(Serialize)]
 pub struct PlainCacheRef<'a> {
-    current_frame_id: FrameId,
+    current_frame_indices: &'a FastHashMap<DocumentId, usize>,
     glyphs: &'a GlyphCache,
     glyph_dimensions: &'a GlyphDimensionsCache,
     images: &'a ImageCache,
     render_tasks: &'a RenderTaskCache,
     textures: &'a TextureCache,
 }
 
 #[cfg(feature = "replay")]
 #[derive(Deserialize)]
 pub struct PlainCacheOwn {
-    current_frame_id: FrameId,
+    current_frame_indices: FastHashMap<DocumentId, usize>,
     glyphs: GlyphCache,
     glyph_dimensions: GlyphDimensionsCache,
     images: ImageCache,
     render_tasks: RenderTaskCache,
     textures: TextureCache,
 }
 
 #[cfg(feature = "replay")]
@@ -1992,17 +1992,17 @@ impl ResourceCache {
         };
 
         (resources, external_images)
     }
 
     #[cfg(feature = "capture")]
     pub fn save_caches(&self, _root: &PathBuf) -> PlainCacheRef {
         PlainCacheRef {
-            current_frame_id: self.current_frame_id,
+            current_frame_indices: &self.current_frame_indices,
             glyphs: &self.cached_glyphs,
             glyph_dimensions: &self.cached_glyph_dimensions,
             images: &self.cached_images,
             render_tasks: &self.cached_render_tasks,
             textures: &self.texture_cache,
         }
     }
 
@@ -2019,25 +2019,25 @@ impl ResourceCache {
         info!("loading resource cache");
         //TODO: instead of filling the local path to Arc<data> map as we process
         // each of the resource types, we could go through all of the local paths
         // and fill out the map as the first step.
         let mut raw_map = FastHashMap::<String, Arc<Vec<u8>>>::default();
 
         match caches {
             Some(cached) => {
-                self.current_frame_id = cached.current_frame_id;
+                self.current_frame_indices = cached.current_frame_indices;
                 self.cached_glyphs = cached.glyphs;
                 self.cached_glyph_dimensions = cached.glyph_dimensions;
                 self.cached_images = cached.images;
                 self.cached_render_tasks = cached.render_tasks;
                 self.texture_cache = cached.textures;
             }
             None => {
-                self.current_frame_id = FrameId::invalid();
+                self.current_frame_indices = FastHashMap::default();
                 self.cached_glyphs.clear();
                 self.cached_glyph_dimensions.clear();
                 self.cached_images.clear();
                 self.cached_render_tasks.clear();
                 let max_texture_size = self.texture_cache.max_texture_size();
                 self.texture_cache = TextureCache::new(max_texture_size);
             }
         }
--- a/gfx/webrender/src/scene_builder.rs
+++ b/gfx/webrender/src/scene_builder.rs
@@ -42,16 +42,17 @@ pub struct Transaction {
     pub blob_rasterizer: Option<Box<AsyncBlobImageRasterizer>>,
     pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>,
     pub resource_updates: Vec<ResourceUpdate>,
     pub frame_ops: Vec<FrameMsg>,
     pub notifications: Vec<NotificationRequest>,
     pub set_root_pipeline: Option<PipelineId>,
     pub render_frame: bool,
     pub invalidate_rendered_frame: bool,
+    pub skip_frame_notification: bool,
 }
 
 impl Transaction {
     pub fn can_skip_scene_builder(&self) -> bool {
         self.request_scene_build.is_none() &&
             self.display_list_updates.is_empty() &&
             self.epoch_updates.is_empty() &&
             self.removed_pipelines.is_empty() &&
@@ -76,16 +77,17 @@ pub struct BuiltTransaction {
     pub frame_ops: Vec<FrameMsg>,
     pub removed_pipelines: Vec<PipelineId>,
     pub notifications: Vec<NotificationRequest>,
     pub doc_resource_updates: Option<DocumentResourceUpdates>,
     pub scene_build_start_time: u64,
     pub scene_build_end_time: u64,
     pub render_frame: bool,
     pub invalidate_rendered_frame: bool,
+    pub skip_frame_notification: bool,
 }
 
 pub struct DisplayListUpdate {
     pub pipeline_id: PipelineId,
     pub epoch: Epoch,
     pub built_display_list: BuiltDisplayList,
     pub background: Option<ColorF>,
     pub viewport_size: LayoutSize,
@@ -316,16 +318,17 @@ impl SceneBuilder {
             let mut built_scene = None;
             let mut doc_resource_updates = None;
 
             if item.scene.has_root_pipeline() {
                 let mut clip_scroll_tree = ClipScrollTree::new();
                 let mut new_scene = Scene::new();
 
                 let frame_builder = DisplayListFlattener::create_frame_builder(
+                    item.document_id,
                     &item.scene,
                     &mut clip_scroll_tree,
                     item.font_instances,
                     &item.view,
                     &item.output_pipelines,
                     &self.config,
                     &mut new_scene,
                     &mut self.picture_id_generator,
@@ -363,16 +366,17 @@ impl SceneBuilder {
                     resources: item.doc_resources,
                 },
             );
 
             let txn = Box::new(BuiltTransaction {
                 document_id: item.document_id,
                 render_frame: item.build_frame,
                 invalidate_rendered_frame: false,
+                skip_frame_notification: false,
                 built_scene,
                 resource_updates: Vec::new(),
                 rasterized_blobs: Vec::new(),
                 blob_rasterizer: None,
                 frame_ops: Vec::new(),
                 removed_pipelines: Vec::new(),
                 notifications: Vec::new(),
                 scene_build_start_time,
@@ -420,16 +424,17 @@ impl SceneBuilder {
         let mut built_scene = None;
         let mut doc_resource_updates = None;
         if scene.has_root_pipeline() {
             if let Some(request) = txn.request_scene_build.take() {
                 let mut clip_scroll_tree = ClipScrollTree::new();
                 let mut new_scene = Scene::new();
 
                 let frame_builder = DisplayListFlattener::create_frame_builder(
+                    txn.document_id,
                     &scene,
                     &mut clip_scroll_tree,
                     request.font_instances,
                     &request.view,
                     &request.output_pipelines,
                     &self.config,
                     &mut new_scene,
                     &mut self.picture_id_generator,
@@ -479,16 +484,17 @@ impl SceneBuilder {
         if self.simulate_slow_ms > 0 {
             thread::sleep(Duration::from_millis(self.simulate_slow_ms as u64));
         }
 
         Box::new(BuiltTransaction {
             document_id: txn.document_id,
             render_frame: txn.render_frame,
             invalidate_rendered_frame: txn.invalidate_rendered_frame,
+            skip_frame_notification: txn.skip_frame_notification,
             built_scene,
             rasterized_blobs,
             resource_updates: replace(&mut txn.resource_updates, Vec::new()),
             blob_rasterizer: replace(&mut txn.blob_rasterizer, None),
             frame_ops: replace(&mut txn.frame_ops, Vec::new()),
             removed_pipelines: replace(&mut txn.removed_pipelines, Vec::new()),
             notifications: replace(&mut txn.notifications, Vec::new()),
             doc_resource_updates,
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -128,16 +128,32 @@ impl CacheEntry {
         }
     }
 
     fn evict(&self) {
         if let Some(eviction_notice) = self.eviction_notice.as_ref() {
             eviction_notice.notify();
         }
     }
+
+    fn should_evict(&self, frame_id: FrameId, frame_index_threshold: usize) -> bool {
+        if self.last_access == FrameId::invalid() {
+            return true;
+        }
+
+        if self.last_access.document_id != frame_id.document_id {
+            return false;
+        }
+
+        match self.eviction {
+            Eviction::Manual => false,
+            Eviction::Auto => self.last_access.frame_index < frame_index_threshold,
+            Eviction::Eager => self.last_access.frame_index < frame_id.frame_index,
+        }
+    }
 }
 
 
 /// A texture cache handle is a weak reference to a cache entry.
 ///
 /// If the handle has not been inserted into the cache yet, or if the entry was
 /// previously inserted and then evicted, lookup of the handle will fail, and
 /// the cache handle needs to re-upload this item to the texture cache (see
@@ -354,27 +370,30 @@ impl TextureCache {
 
         self.shared_textures.clear(&mut self.pending_updates);
     }
 
     pub fn begin_frame(&mut self, frame_id: FrameId) {
         self.frame_id = frame_id;
     }
 
-    pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) {
+    pub fn end_frame(&mut self,
+                     texture_cache_profile: &mut TextureCacheProfileCounters) {
         self.expire_old_standalone_entries();
 
         self.shared_textures.array_a8_linear
             .update_profile(&mut texture_cache_profile.pages_a8_linear);
         self.shared_textures.array_a16_linear
             .update_profile(&mut texture_cache_profile.pages_a16_linear);
         self.shared_textures.array_rgba8_linear
             .update_profile(&mut texture_cache_profile.pages_rgba8_linear);
         self.shared_textures.array_rgba8_nearest
             .update_profile(&mut texture_cache_profile.pages_rgba8_nearest);
+
+        self.frame_id = FrameId::invalid();
     }
 
     // Request an item in the texture cache. All images that will
     // be used on a frame *must* have request() called on their
     // handle, to update the last used timestamp and ensure
     // that resources are not flushed from the cache too early.
     //
     // Returns true if the image needs to be uploaded to the
@@ -601,32 +620,28 @@ impl TextureCache {
 
         // Use the pressure factor to compute the maximum number of frames that
         // a standalone texture can go unused before being evicted.
         let max_frame_age_raw =
             ((1.0 - pressure_factor) * MAX_FRAME_AGE_WITHOUT_PRESSURE) as usize;
 
         // We clamp max_frame_age to frame_id - 1 so that entries with FrameId(0)
         // always get evicted, even early in the lifetime of the Renderer.
-        let max_frame_age = max_frame_age_raw.min(self.frame_id.as_usize() - 1);
+        let max_frame_age = max_frame_age_raw.min(self.frame_id.frame_index - 1);
 
-        // Compute the oldest FrameId for which we will not evict.
-        let frame_id_threshold = self.frame_id - max_frame_age;
+        // Compute the oldest frame index for which we will not evict.
+        let frame_indexd_threshold = self.frame_id.frame_index - max_frame_age;
 
         // Iterate over the entries in reverse order, evicting the ones older than
         // the frame age threshold. Reverse order avoids iterator invalidation when
         // removing entries.
         for i in (0..self.standalone_entry_handles.len()).rev() {
             let evict = {
                 let entry = self.entries.get(&self.standalone_entry_handles[i]);
-                match entry.eviction {
-                    Eviction::Manual => false,
-                    Eviction::Auto => entry.last_access < frame_id_threshold,
-                    Eviction::Eager => entry.last_access < self.frame_id,
-                }
+                entry.should_evict(self.frame_id, frame_indexd_threshold)
             };
             if evict {
                 let handle = self.standalone_entry_handles.swap_remove(i);
                 let entry = self.entries.free(handle);
                 entry.evict();
                 self.free(entry);
             }
         }
@@ -639,27 +654,27 @@ impl TextureCache {
     fn expire_old_shared_entries(&mut self, required_alloc: &ImageDescriptor) {
         let mut eviction_candidates = Vec::new();
         let mut retained_entries = Vec::new();
 
         // Build a list of eviction candidates (which are
         // anything not used this frame).
         for handle in self.shared_entry_handles.drain(..) {
             let entry = self.entries.get(&handle);
-            if entry.eviction == Eviction::Manual || entry.last_access == self.frame_id {
+            if entry.should_evict(self.frame_id, self.frame_id.frame_index) {
+                eviction_candidates.push(handle);
+            } else {
                 retained_entries.push(handle);
-            } else {
-                eviction_candidates.push(handle);
             }
         }
 
         // Sort by access time so we remove the oldest ones first.
         eviction_candidates.sort_by_key(|handle| {
             let entry = self.entries.get(handle);
-            entry.last_access
+            entry.last_access.frame_index
         });
 
         // Doing an eviction is quite expensive, so we don't want to
         // do it all the time. To avoid this, try and evict a
         // significant number of items each cycle. However, we don't
         // want to evict everything we can, since that will result in
         // more items being uploaded than necessary.
         // Instead, we say we will keep evicting until both of these
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -57,30 +57,33 @@ pub struct Transaction {
     // If true the transaction is piped through the scene building thread, if false
     // it will be applied directly on the render backend.
     use_scene_builder_thread: bool,
 
     generate_frame: bool,
 
     invalidate_rendered_frame: bool,
 
+    skip_frame_notification: bool,
+
     low_priority: bool,
 }
 
 impl Transaction {
     pub fn new() -> Self {
         Transaction {
             scene_ops: Vec::new(),
             frame_ops: Vec::new(),
             resource_updates: Vec::new(),
             payloads: Vec::new(),
             notifications: Vec::new(),
             use_scene_builder_thread: true,
             generate_frame: false,
             invalidate_rendered_frame: false,
+            skip_frame_notification: false,
             low_priority: false,
         }
     }
 
     // TODO: better name?
     pub fn skip_scene_builder(&mut self) {
         self.use_scene_builder_thread = false;
     }
@@ -241,18 +244,19 @@ impl Transaction {
 
     /// Generate a new frame. When it's done and a RenderNotifier has been set
     /// in `webrender::Renderer`, [new_frame_ready()][notifier] gets called.
     /// Note that the notifier is called even if the frame generation was a
     /// no-op; the arguments passed to `new_frame_ready` will provide information
     /// as to when happened.
     ///
     /// [notifier]: trait.RenderNotifier.html#tymethod.new_frame_ready
-    pub fn generate_frame(&mut self) {
+    pub fn generate_frame(&mut self, skip_frame_notification: bool) {
         self.generate_frame = true;
+        self.skip_frame_notification = skip_frame_notification;
     }
 
     /// Invalidate rendered frame. It ensure that frame will be rendered during
     /// next frame generation. WebRender could skip frame rendering if there
     /// is no update.
     /// But there are cases that needs to force rendering.
     ///  - Content of image is updated by reusing same ExternalImageId.
     ///  - Platform requests it if pixels become stale (like wakeup from standby).
@@ -290,16 +294,17 @@ impl Transaction {
             TransactionMsg {
                 scene_ops: self.scene_ops,
                 frame_ops: self.frame_ops,
                 resource_updates: self.resource_updates,
                 notifications: self.notifications,
                 use_scene_builder_thread: self.use_scene_builder_thread,
                 generate_frame: self.generate_frame,
                 invalidate_rendered_frame: self.invalidate_rendered_frame,
+                skip_frame_notification: self.skip_frame_notification,
                 low_priority: self.low_priority,
             },
             self.payloads,
         )
     }
 
     pub fn add_image(
         &mut self,
@@ -401,16 +406,17 @@ impl Transaction {
 /// Represents a transaction in the format sent through the channel.
 #[derive(Clone, Deserialize, Serialize)]
 pub struct TransactionMsg {
     pub scene_ops: Vec<SceneMsg>,
     pub frame_ops: Vec<FrameMsg>,
     pub resource_updates: Vec<ResourceUpdate>,
     pub generate_frame: bool,
     pub invalidate_rendered_frame: bool,
+    pub skip_frame_notification: bool,
     pub use_scene_builder_thread: bool,
     pub low_priority: bool,
 
     #[serde(skip)]
     pub notifications: Vec<NotificationRequest>,
 }
 
 impl TransactionMsg {
@@ -427,29 +433,31 @@ impl TransactionMsg {
     pub fn frame_message(msg: FrameMsg) -> Self {
         TransactionMsg {
             scene_ops: Vec::new(),
             frame_ops: vec![msg],
             resource_updates: Vec::new(),
             notifications: Vec::new(),
             generate_frame: false,
             invalidate_rendered_frame: false,
+            skip_frame_notification: false,
             use_scene_builder_thread: false,
             low_priority: false,
         }
     }
 
     pub fn scene_message(msg: SceneMsg) -> Self {
         TransactionMsg {
             scene_ops: vec![msg],
             frame_ops: Vec::new(),
             resource_updates: Vec::new(),
             notifications: Vec::new(),
             generate_frame: false,
             invalidate_rendered_frame: false,
+            skip_frame_notification: false,
             use_scene_builder_thread: false,
             low_priority: false,
         }
     }
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub struct AddImage {
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -274,16 +274,17 @@ RenderThread::HandleFrame(wr::WindowId a
   TimeStamp startTime;
 
   bool hadSlowFrame;
   { // scope lock
     MutexAutoLock lock(mFrameCountMapLock);
     auto it = mWindowInfos.find(AsUint64(aWindowId));
     MOZ_ASSERT(it != mWindowInfos.end());
     WindowInfo* info = it->second;
+
     MOZ_ASSERT(info->mPendingCount > 0);
     startTime = info->mStartTimes.front();
     hadSlowFrame = info->mHadSlowFrame;
     info->mHadSlowFrame = false;
   }
 
   UpdateAndRender(aWindowId, startTime, aRender, /* aReadbackSize */ Nothing(), /* aReadbackBuffer */ Nothing(), hadSlowFrame);
   FrameRenderingComplete(aWindowId);
--- a/gfx/webrender_bindings/RenderThread.h
+++ b/gfx/webrender_bindings/RenderThread.h
@@ -238,16 +238,17 @@ private:
   // An optional shared GLContext to be used for all
   // windows.
   RefPtr<gl::GLContext> mSharedGL;
 
   std::map<wr::WindowId, UniquePtr<RendererOGL>> mRenderers;
 
   struct WindowInfo {
     bool mIsDestroyed = false;
+    bool mSeenFrame = false;
     int64_t mPendingCount = 0;
     int64_t mRenderingCount = 0;
     // One entry in this queue for each pending frame, so the length
     // should always equal mPendingCount
     std::queue<TimeStamp> mStartTimes;
     bool mHadSlowFrame = false;
   };
 
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -202,19 +202,19 @@ TransactionBuilder::SetDisplayList(gfx::
 
 void
 TransactionBuilder::ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipelineId)
 {
   wr_transaction_clear_display_list(mTxn, aEpoch, aPipelineId);
 }
 
 void
-TransactionBuilder::GenerateFrame()
+TransactionBuilder::GenerateFrame(bool aSkipFrameNotification)
 {
-  wr_transaction_generate_frame(mTxn);
+  wr_transaction_generate_frame(mTxn, aSkipFrameNotification);
 }
 
 void
 TransactionBuilder::InvalidateRenderedFrame()
 {
   wr_transaction_invalidate_rendered_frame(mTxn);
 }
 
@@ -801,31 +801,55 @@ WebRenderAPI::RunOnRenderThread(UniquePt
   auto event = reinterpret_cast<uintptr_t>(aEvent.release());
   wr_api_send_external_event(mDocHandle, event);
 }
 
 DisplayListBuilder::DisplayListBuilder(PipelineId aId,
                                        const wr::LayoutSize& aContentSize,
                                        size_t aCapacity)
   : mActiveFixedPosTracker(nullptr)
+  , mContentRectBuilder(nullptr)
+  , mPipelineId(aId)
+  , mContentSize(aContentSize)
+  , mIsContentRectBuilder(false)
+  , mSendContentRectDisplayList(false)
 {
   MOZ_COUNT_CTOR(DisplayListBuilder);
   mWrState = wr_state_new(aId, aContentSize, aCapacity);
 }
 
 DisplayListBuilder::~DisplayListBuilder()
 {
   MOZ_COUNT_DTOR(DisplayListBuilder);
   wr_state_delete(mWrState);
 }
 
 void DisplayListBuilder::Save() { wr_dp_save(mWrState); }
 void DisplayListBuilder::Restore() { wr_dp_restore(mWrState); }
 void DisplayListBuilder::ClearSave() { wr_dp_clear_save(mWrState); }
 
+DisplayListBuilder& DisplayListBuilder::CreateContentRectBuilder(const wr::LayoutSize& aContentSize,
+                                                                 size_t aCapacity)
+{
+  MOZ_ASSERT(!mIsContentRectBuilder);
+  mContentRectBuilder = MakeUnique<DisplayListBuilder>(mPipelineId, aContentSize, aCapacity);
+  mContentRectBuilder->mIsContentRectBuilder = true;
+  return *mContentRectBuilder;
+}
+
+DisplayListBuilder& DisplayListBuilder::ContentRectBuilder()
+{
+  return *mContentRectBuilder;
+}
+
+bool DisplayListBuilder::HasContentRectBuilder()
+{
+  return !!mContentRectBuilder;
+}
+
 usize DisplayListBuilder::Dump(usize aIndent,
                                const Maybe<usize>& aStart,
                                const Maybe<usize>& aEnd)
 {
   return wr_dump_display_list(mWrState, aIndent, aStart.ptrOr(nullptr), aEnd.ptrOr(nullptr));
 }
 
 void
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -82,17 +82,17 @@ public:
                       mozilla::LayerSize aViewportSize,
                       wr::WrPipelineId pipeline_id,
                       const wr::LayoutSize& content_size,
                       wr::BuiltDisplayListDescriptor dl_descriptor,
                       wr::Vec<uint8_t>& dl_data);
 
   void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipeline);
 
-  void GenerateFrame();
+  void GenerateFrame(bool aSkipFrameNotification);
 
   void InvalidateRenderedFrame();
 
   void UpdateDynamicProperties(const nsTArray<wr::WrOpacityProperty>& aOpacityArray,
                                const nsTArray<wr::WrTransformProperty>& aTransformArray);
 
   void SetWindowParameters(const LayoutDeviceIntSize& aWindowSize,
                            const LayoutDeviceIntRect& aDocRect);
@@ -314,16 +314,35 @@ public:
   void Save();
   void Restore();
   void ClearSave();
   usize Dump(usize aIndent, const Maybe<usize>& aStart, const Maybe<usize>& aEnd);
 
   void Finalize(wr::LayoutSize& aOutContentSize,
                 wr::BuiltDisplayList& aOutDisplayList);
 
+  void FinalizeContentRect(wr::LayoutSize& aOutContentSize,
+                           wr::BuiltDisplayList& aOutDisplayList);
+
+  bool HasContentRectBuilder();
+  bool IsContentRectBuilder() { return mIsContentRectBuilder; }
+  DisplayListBuilder& CreateContentRectBuilder(const wr::LayoutSize& aContentSize,
+                                               size_t aCapacity);
+  DisplayListBuilder& ContentRectBuilder();
+
+  bool GetSendContentRectDisplayList()
+  {
+    return mSendContentRectDisplayList;
+  }
+
+  void SetSendContentRectDisplayList()
+  {
+    mSendContentRectDisplayList = true;
+  }
+
   Maybe<wr::WrClipId> PushStackingContext(
           const wr::LayoutRect& aBounds, // TODO: We should work with strongly typed rects
           const wr::WrClipId* aClipNodeId,
           const wr::WrAnimationProperty* aAnimation,
           const float* aOpacity,
           const gfx::Matrix4x4* aTransform,
           wr::TransformStyle aTransformStyle,
           const gfx::Matrix4x4* aPerspective,
@@ -568,16 +587,23 @@ protected:
 
   // Contains the current leaf of the clip chain to be merged with the
   // display item's clip rect when pushing an item. May be set to Nothing() if
   // there is no clip rect to merge with.
   Maybe<wr::LayoutRect> mClipChainLeaf;
 
   FixedPosScrollTargetTracker* mActiveFixedPosTracker;
 
+  UniquePtr<DisplayListBuilder> mContentRectBuilder;
+  wr::PipelineId mPipelineId;
+  wr::LayoutSize mContentSize;
+
+  bool mIsContentRectBuilder;
+  bool mSendContentRectDisplayList;
+
   friend class WebRenderAPI;
 };
 
 Maybe<wr::ImageFormat>
 SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat);
 
 } // namespace wr
 } // namespace mozilla
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1104,16 +1104,18 @@ pub extern "C" fn wr_api_create_document
 ) {
     assert!(unsafe { is_in_compositor_thread() });
 
     *out_handle = Box::into_raw(Box::new(DocumentHandle::new(
         root_dh.api.clone_sender().create_api_by_client(next_namespace_id()),
         doc_size,
         layer
     )));
+
+    println!("wr_api_create_document {:?}", unsafe{(**out_handle).document_id});
 }
 
 /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
 #[no_mangle]
 pub unsafe extern "C" fn wr_api_delete_document(dh: &mut DocumentHandle) {
     dh.api.delete_document(dh.document_id);
 }
 
@@ -1276,18 +1278,20 @@ pub extern "C" fn wr_transaction_set_win
     txn.set_window_parameters(
         *window_size,
         *doc_rect,
         1.0,
     );
 }
 
 #[no_mangle]
-pub extern "C" fn wr_transaction_generate_frame(txn: &mut Transaction) {
-    txn.generate_frame();
+pub extern "C" fn wr_transaction_generate_frame(
+    txn: &mut Transaction,
+    skip_frame_notification: bool) {
+    txn.generate_frame(skip_frame_notification);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_transaction_invalidate_rendered_frame(txn: &mut Transaction) {
     txn.invalidate_rendered_frame();
 }
 
 #[no_mangle]
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1820,17 +1820,17 @@ void wr_transaction_clear_display_list(T
                                        WrPipelineId aPipelineId)
 WR_FUNC;
 
 WR_INLINE
 void wr_transaction_delete(Transaction *aTxn)
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
-void wr_transaction_generate_frame(Transaction *aTxn)
+void wr_transaction_generate_frame(Transaction *aTxn, bool aSkipFrameNotification)
 WR_FUNC;
 
 WR_INLINE
 void wr_transaction_invalidate_rendered_frame(Transaction *aTxn)
 WR_FUNC;
 
 WR_INLINE
 bool wr_transaction_is_empty(const Transaction *aTxn)
--- a/ipc/glue/ByteBuf.h
+++ b/ipc/glue/ByteBuf.h
@@ -87,18 +87,20 @@ template<>
 struct ParamTraits<mozilla::ipc::ByteBuf>
 {
   typedef mozilla::ipc::ByteBuf paramType;
 
   // this is where we transfer the memory from the ByteBuf to IPDL, avoiding a copy
   static void Write(Message* aMsg, paramType& aParam)
   {
     WriteParam(aMsg, aParam.mLen);
-    // hand over ownership of the buffer to the Message
-    aMsg->WriteBytesZeroCopy(aParam.mData, aParam.mLen, aParam.mCapacity);
+    if (aParam.mCapacity) {
+      // hand over ownership of the buffer to the Message
+      aMsg->WriteBytesZeroCopy(aParam.mData, aParam.mLen, aParam.mCapacity);
+    }
     aParam.mData = nullptr;
     aParam.mCapacity = 0;
     aParam.mLen = 0;
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     // We make a copy from the BufferList so that we get a contigous result.
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -88,16 +88,17 @@ public:
       mBuilder.Restore();
     } else {
       mBuilder.ClearSave();
     }
   }
 
   void FoundUnsupportedFeature() { mHasUnsupportedFeatures = true; }
   bool HasUnsupportedFeatures() { return mHasUnsupportedFeatures; }
+  bool IsForContentRect() { return mResources.IsForContentRect(); }
 
   wr::FontInstanceFlags GetWRGlyphFlags() const { return mWRGlyphFlags; }
   void SetWRGlyphFlags(wr::FontInstanceFlags aFlags) { mWRGlyphFlags = aFlags; }
 
   class AutoRestoreWRGlyphFlags
   {
   public:
     ~AutoRestoreWRGlyphFlags()
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -136,17 +136,19 @@ public:
 
     switch(element->GetCurrentContextType()) {
       case CanvasContextType::Canvas2D:
       case CanvasContextType::WebGL1:
       case CanvasContextType::WebGL2:
       {
         bool isRecycled;
         RefPtr<WebRenderCanvasData> canvasData =
-          aManager->CommandBuilder().CreateOrRecycleWebRenderUserData<WebRenderCanvasData>(this, &isRecycled);
+          aManager->CommandBuilder().CreateOrRecycleWebRenderUserData<WebRenderCanvasData>(this,
+                                                                                           aBuilder.IsContentRectBuilder(),
+                                                                                           &isRecycled);
         nsHTMLCanvasFrame* canvasFrame = static_cast<nsHTMLCanvasFrame*>(mFrame);
         if (!canvasFrame->UpdateWebRenderCanvasData(aDisplayListBuilder, canvasData)) {
           return true;
         }
         WebRenderCanvasRendererAsync* data =
           static_cast<WebRenderCanvasRendererAsync*>(canvasData->GetCanvasRenderer());
         MOZ_ASSERT(data);
         data->UpdateCompositableClient();
@@ -169,16 +171,20 @@ public:
         // We don't push a stacking context for this async image pipeline here.
         // Instead, we do it inside the iframe that hosts the image. As a result,
         // a bunch of the calculations normally done as part of that stacking
         // context need to be done manually and pushed over to the parent side,
         // where it will be done when we build the display list for the iframe.
         // That happens in WebRenderCompositableHolder.
 
         wr::LayoutRect r = wr::ToRoundedLayoutRect(bounds);
+
+        printf("nsHTMLCanvasFrame -- %d %d\n",
+               data->GetPipelineId()->mNamespace,
+               data->GetPipelineId()->mHandle);
         aBuilder.PushIFrame(r, !BackfaceIsHidden(), data->GetPipelineId().ref(), /*ignoreMissingPipelines*/ false);
 
         gfx::Matrix4x4 scTransform;
         gfxRect destGFXRect = mFrame->PresContext()->AppUnitsToGfxUnits(dest);
         scTransform.PreScale(destGFXRect.Width() / canvasSizeInPx.width,
                              destGFXRect.Height() / canvasSizeInPx.height, 1.0f);
         if (data->NeedsYFlip()) {
           scTransform = scTransform.PreTranslate(0, data->GetSize().height, 0).PreScale(1, -1, 1);
@@ -188,17 +194,18 @@ public:
         LayoutDeviceRect scBounds(LayoutDevicePoint(0, 0), bounds.Size());
         wr::ImageRendering filter = wr::ToImageRendering(nsLayoutUtils::GetSamplingFilterForFrame(mFrame));
         wr::MixBlendMode mixBlendMode = wr::MixBlendMode::Normal;
         aManager->WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(data->GetPipelineId().value(),
                                                                                    scBounds,
                                                                                    scTransform,
                                                                                    scaleToSize,
                                                                                    filter,
-                                                                                   mixBlendMode));
+                                                                                   mixBlendMode),
+                                                        aBuilder.IsContentRectBuilder());
         break;
       }
       case CanvasContextType::ImageBitmap:
       {
         // TODO: Support ImageBitmap
         break;
       }
       case CanvasContextType::NoContext:
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -43,16 +43,18 @@
 #include "nsServiceManagerUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/HTMLFrameElement.h"
 #include "RetainedDisplayListBuilder.h"
 
 using namespace mozilla;
 using mozilla::layout::RenderFrame;
 
+int gThingCalled = false;
+
 static bool sShowPreviousPage = true;
 
 static nsIDocument*
 GetDocumentFromView(nsView* aView)
 {
   MOZ_ASSERT(aView, "null view");
 
   nsViewManager* vm = aView->GetViewManager();
--- a/layout/ipc/RenderFrame.cpp
+++ b/layout/ipc/RenderFrame.cpp
@@ -123,16 +123,23 @@ RenderFrame::AttachLayerManager()
   }
 
   // Perhaps the document containing this frame currently has no presentation?
   if (lm && lm->GetCompositorBridgeChild() && lm != mLayerManager) {
     mLayersConnected = lm->GetCompositorBridgeChild()->SendAdoptChild(mLayersId);
     FrameLayerBuilder::InvalidateAllLayers(lm);
   }
 
+  if (lm) {
+    WebRenderLayerManager* wrlm = lm->AsWebRenderLayerManager();
+    if (wrlm) {
+      wrlm->SetNeedsContentRectDisplayList(true);
+    }
+  }
+
   mLayerManager = lm.forget();
   return mLayerManager;
 }
 
 void
 RenderFrame::OwnerContentChanged(nsIContent* aContent)
 {
   MOZ_ASSERT(!mFrameLoader || mFrameLoader->GetOwnerContent() == aContent,
@@ -306,17 +313,25 @@ nsDisplayRemote::Paint(nsDisplayListBuil
 
 bool
 nsDisplayRemote::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                          mozilla::wr::IpcResourceUpdateQueue& aResources,
                                          const StackingContextHelper& aSc,
                                          mozilla::layers::WebRenderLayerManager* aManager,
                                          nsDisplayListBuilder* aDisplayListBuilder)
 {
+<<<<<<< dest
   mOffset = GetContentRectLayerOffset(mFrame, aDisplayListBuilder);
+=======
+  if (aBuilder.IsContentRectBuilder()) {
+    aManager->SetNeedsContentRectDisplayList(false);
+  }
+
+  mOffset = mozilla::layout::GetContentRectLayerOffset(mFrame, aDisplayListBuilder);
+>>>>>>> source
 
   LayoutDeviceRect rect = LayoutDeviceRect::FromAppUnits(
     mFrame->GetContentRectRelativeToSelf(), mFrame->PresContext()->AppUnitsPerDevPixel());
   rect += mOffset;
 
   aBuilder.PushIFrame(mozilla::wr::ToRoundedLayoutRect(rect),
       !BackfaceIsHidden(),
       mozilla::wr::AsPipelineId(GetRemoteLayersId()),
--- a/layout/painting/nsDisplayItemTypesList.h
+++ b/layout/painting/nsDisplayItemTypesList.h
@@ -44,16 +44,17 @@ DECLARE_DISPLAY_ITEM_TYPE(OPACITY, TYPE_
 DECLARE_DISPLAY_ITEM_TYPE(OPTION_EVENT_GRABBER, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(OUTLINE, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(OWN_LAYER, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(PLUGIN, 0)
 DECLARE_DISPLAY_ITEM_TYPE(PLUGIN_READBACK, 0)
 DECLARE_DISPLAY_ITEM_TYPE(PRINT_PLUGIN, 0)
 DECLARE_DISPLAY_ITEM_TYPE(RANGE_FOCUS_RING, 0)
 DECLARE_DISPLAY_ITEM_TYPE(REMOTE, TYPE_RENDERS_NO_IMAGES)
+DECLARE_DISPLAY_ITEM_TYPE(RENDER_ROOT, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(RESOLUTION, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SCROLL_INFO_LAYER, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SELECTION_OVERLAY, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR_REGION, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SUBDOCUMENT, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(MASK, 0)
 DECLARE_DISPLAY_ITEM_TYPE(FILTER, TYPE_RENDERS_NO_IMAGES)
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -3475,22 +3475,47 @@ nsDisplaySolidColor::CreateWebRenderComm
   mozilla::wr::DisplayListBuilder& aBuilder,
   mozilla::wr::IpcResourceUpdateQueue& aResources,
   const StackingContextHelper& aSc,
   mozilla::layers::WebRenderLayerManager* aManager,
   nsDisplayListBuilder* aDisplayListBuilder)
 {
   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
     GetPaintRect(), mFrame->PresContext()->AppUnitsPerDevPixel());
-  wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(bounds);
-
-  aBuilder.PushRect(roundedRect,
-                    roundedRect,
-                    !BackfaceIsHidden(),
-                    wr::ToColorF(ToDeviceColor(mColor)));
+
+  LayoutDeviceRect contentRect = aDisplayListBuilder->GetContentRenderRootRect();
+
+  if (bounds.Intersects(contentRect) && !aBuilder.IsContentRectBuilder()) {
+    MOZ_ASSERT(bounds.x == contentRect.x);
+    wr::LayoutRect contentRoundedRect =
+      wr::ToRoundedLayoutRect(bounds.Intersect(contentRect));
+
+    aBuilder.ContentRectBuilder().PushRect(contentRoundedRect,
+                                           contentRoundedRect,
+                                           !BackfaceIsHidden(),
+                                           wr::ToColorF(ToDeviceColor(mColor)));
+
+    bounds = LayoutDeviceRect(bounds.x, bounds.y,
+                              bounds.Width(), contentRect.y - bounds.y);
+    wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(bounds);
+
+    aBuilder.PushRect(roundedRect,
+                      roundedRect,
+                      !BackfaceIsHidden(),
+                      wr::ToColorF(ToDeviceColor(mColor)));
+
+  } else {
+    wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(bounds);
+
+    aBuilder.PushRect(roundedRect,
+                      roundedRect,
+                      !BackfaceIsHidden(),
+                      wr::ToColorF(ToDeviceColor(mColor)));
+  }
+
 
   return true;
 }
 
 nsRect
 nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
                                      bool* aSnap) const
 {
@@ -6675,17 +6700,18 @@ nsDisplayOpacity::CreateWebRenderCommand
   const StackingContextHelper& aSc,
   mozilla::layers::WebRenderLayerManager* aManager,
   nsDisplayListBuilder* aDisplayListBuilder)
 {
   float* opacityForSC = &mOpacity;
 
   RefPtr<WebRenderAnimationData> animationData =
     aManager->CommandBuilder()
-      .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this);
+      .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this,
+                                                                aBuilder.IsContentRectBuilder());
   AnimationInfo& animationInfo = animationData->GetAnimationInfo();
   AddAnimationsForProperty(Frame(),
                            aDisplayListBuilder,
                            this,
                            eCSSProperty_opacity,
                            animationInfo,
                            false,
                            true);
@@ -6697,17 +6723,17 @@ nsDisplayOpacity::CreateWebRenderCommand
   wr::WrAnimationProperty prop;
 
   if (!animationInfo.GetAnimations().IsEmpty()) {
     prop.id = animationsId;
     prop.effect_type = wr::WrAnimationType::Opacity;
 
     OpAddCompositorAnimations anim(
       CompositorAnimations(animationInfo.GetAnimations(), animationsId));
-    aManager->WrBridge()->AddWebRenderParentCommand(anim);
+    aManager->WrBridge()->AddWebRenderParentCommand(anim, aBuilder.IsContentRectBuilder());
     aManager->AddActiveCompositorAnimationId(animationsId);
   } else if (animationsId) {
     aManager->AddCompositorAnimationsIdForDiscard(animationsId);
     animationsId = 0;
   }
 
   nsTArray<mozilla::wr::WrFilterOp> filters;
   StackingContextHelper sc(aSc,
@@ -7042,17 +7068,18 @@ nsDisplayOwnLayer::CreateWebRenderComman
       aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
   }
 
   // APZ is enabled and this is a scroll thumb, so we need to create and
   // set an animation id. That way APZ can move this scrollthumb around as
   // needed.
   RefPtr<WebRenderAnimationData> animationData =
     aManager->CommandBuilder()
-      .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this);
+      .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this,
+                                                                aBuilder.IsContentRectBuilder());
   AnimationInfo& animationInfo = animationData->GetAnimationInfo();
   animationInfo.EnsureAnimationsId();
   mWrAnimationId = animationInfo.GetCompositorAnimationsId();
 
   wr::WrAnimationProperty prop;
   prop.id = mWrAnimationId;
   prop.effect_type = wr::WrAnimationType::Transform;
 
@@ -7098,16 +7125,53 @@ void
 nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream)
 {
   aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
                              (int)mFlags,
                              mScrollbarData.mTargetViewId)
                .get();
 }
 
+nsDisplayRenderRoot::nsDisplayRenderRoot(
+  nsDisplayListBuilder* aBuilder,
+  nsIFrame* aFrame,
+  nsDisplayList* aList,
+  const ActiveScrolledRoot* aActiveScrolledRoot,
+  bool aNeedsRenderDLUpdate)
+  : nsDisplayWrapList(aBuilder,
+                      aFrame,
+                      aList,
+                      aActiveScrolledRoot,
+                      true)
+  , mNeedsRenderDLUpdate(aNeedsRenderDLUpdate)
+{
+  MOZ_COUNT_CTOR(nsDisplayRenderRoot);
+}
+
+bool
+nsDisplayRenderRoot::CreateWebRenderCommands(
+  mozilla::wr::DisplayListBuilder& aBuilder,
+  mozilla::wr::IpcResourceUpdateQueue& aResources,
+  const StackingContextHelper& aSc,
+  WebRenderLayerManager* aManager,
+  nsDisplayListBuilder* aDisplayListBuilder)
+{
+  if (mNeedsRenderDLUpdate || aManager->GetNeedsContentRectDisplayList()) {
+    aBuilder.SetSendContentRectDisplayList();
+  }
+
+  wr::DisplayListBuilder& contentRectBuilder = aBuilder.ContentRectBuilder();
+  wr::IpcResourceUpdateQueue& contentRectResources = aResources.ContentRectUpdateQueue();
+  const StackingContextHelper& contentRectSc = aSc.ContentRectHelper();
+
+  nsDisplayWrapList::CreateWebRenderCommands(
+    contentRectBuilder, contentRectResources, contentRectSc, aManager, aDisplayListBuilder);
+  return true;
+}
+
 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
                                            nsIFrame* aFrame,
                                            nsSubDocumentFrame* aSubDocFrame,
                                            nsDisplayList* aList,
                                            nsDisplayOwnLayerFlags aFlags)
   : nsDisplayOwnLayer(aBuilder,
                       aFrame,
                       aList,
@@ -8760,17 +8824,18 @@ nsDisplayTransform::CreateWebRenderComma
     // If the transform is an identity transform, strip it out so that WR
     // doesn't turn this stacking context into a reference frame, as it
     // affects positioning. Bug 1345577 tracks a better fix.
     transformForSC = nullptr;
   }
 
   RefPtr<WebRenderAnimationData> animationData =
     aManager->CommandBuilder()
-      .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this);
+      .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this,
+                                                                aBuilder.IsContentRectBuilder());
 
   AnimationInfo& animationInfo = animationData->GetAnimationInfo();
   AddAnimationsForProperty(Frame(),
                            aDisplayListBuilder,
                            this,
                            eCSSProperty_transform,
                            animationInfo,
                            false,
@@ -8782,17 +8847,17 @@ nsDisplayTransform::CreateWebRenderComma
   uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
   wr::WrAnimationProperty prop;
   if (!animationInfo.GetAnimations().IsEmpty()) {
     prop.id = animationsId;
     prop.effect_type = wr::WrAnimationType::Transform;
 
     OpAddCompositorAnimations anim(
       CompositorAnimations(animationInfo.GetAnimations(), animationsId));
-    aManager->WrBridge()->AddWebRenderParentCommand(anim);
+    aManager->WrBridge()->AddWebRenderParentCommand(anim, aBuilder.IsContentRectBuilder());
     aManager->AddActiveCompositorAnimationId(animationsId);
   } else if (animationsId) {
     aManager->AddCompositorAnimationsIdForDiscard(animationsId);
     animationsId = 0;
   }
 
   nsTArray<mozilla::wr::WrFilterOp> filters;
   Maybe<nsDisplayTransform*> deferredTransformItem;
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -401,16 +401,17 @@ enum class nsDisplayListBuilderMode : ui
  * coordinate system for all display list items. Some of the parameters are
  * available from the prescontext/presshell, but we copy them into the builder
  * for faster/more convenient access.
  */
 class nsDisplayListBuilder
 {
   typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
   typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion;
+  typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
 
   /**
    * This manages status of a 3d context to collect visible rects of
    * descendants and passing a dirty rect.
    *
    * Since some transforms maybe singular, passing visible rects or
    * the dirty rect level by level from parent to children may get a
    * wrong result, being different from the result of appling with
@@ -977,16 +978,26 @@ public:
 
   /**
    * Subtracts aRegion from *aVisibleRegion. We avoid letting
    * aVisibleRegion become overcomplex by simplifying it if necessary.
    */
   void SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
                                  const nsRegion& aRegion);
 
+  void SetContentRenderRootRect(LayoutDeviceRect aRect)
+  {
+    mContentRenderRootRect = mContentRenderRootRect.Union(aRect);
+  }
+
+  LayoutDeviceRect GetContentRenderRootRect()
+  {
+    return mContentRenderRootRect;
+  }
+
   /**
    * Mark the frames in aFrames to be displayed if they intersect aDirtyRect
    * (which is relative to aDirtyFrame). If the frames have placeholders
    * that might not be displayed, we mark the placeholders and their ancestors
    * to ensure that display list construction descends into them
    * anyway. nsDisplayListBuilder will take care of unmarking them when it is
    * destroyed.
    */
@@ -2053,16 +2064,19 @@ private:
   // mCurrentFrame is the frame that we're currently calling (or about to call)
   // BuildDisplayList on.
   const nsIFrame* mCurrentFrame;
   // The reference frame for mCurrentFrame.
   const nsIFrame* mCurrentReferenceFrame;
   // The offset from mCurrentFrame to mCurrentReferenceFrame.
   nsPoint mCurrentOffsetToReferenceFrame;
 
+  LayoutDeviceRect mMainRenderRootRect;
+  LayoutDeviceRect mContentRenderRootRect;
+
   RefPtr<AnimatedGeometryRoot> mRootAGR;
   RefPtr<AnimatedGeometryRoot> mCurrentAGR;
 
   // will-change budget tracker
   nsDataHashtable<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget>
     mWillChangeBudget;
 
   // Any frame listed in this set is already counted in the budget
@@ -6303,16 +6317,47 @@ protected:
    * default-constructed (in particular with mDirection == Nothing())
    * and can be ignored.
    */
   ScrollbarData mScrollbarData;
   bool mForceActive;
   uint64_t mWrAnimationId;
 };
 
+class nsDisplayRenderRoot : public nsDisplayWrapList
+{
+  nsDisplayRenderRoot(
+    nsDisplayListBuilder* aBuilder,
+    nsIFrame* aFrame,
+    nsDisplayList* aList,
+    const ActiveScrolledRoot* aActiveScrolledRoot,
+    bool aNeedsRenderDLUpdate);
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+  ~nsDisplayRenderRoot() override { MOZ_COUNT_DTOR(nsDisplayRenderRoot); }
+#endif
+
+  NS_DISPLAY_DECL_NAME("RenderRoot", TYPE_RENDER_ROOT)
+
+  bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override
+  {
+    return false;
+  }
+
+  bool CreateWebRenderCommands(
+    mozilla::wr::DisplayListBuilder& aBuilder,
+    mozilla::wr::IpcResourceUpdateQueue& aResources,
+    const StackingContextHelper& aSc,
+    mozilla::layers::WebRenderLayerManager* aManager,
+    nsDisplayListBuilder* aDisplayListBuilder) override;
+
+protected:
+  bool mNeedsRenderDLUpdate;
+};
+
 /**
  * A display item for subdocuments. This is more or less the same as
  * nsDisplayOwnLayer, except that it always populates the FrameMetrics instance
  * on the ContainerLayer it builds.
  */
 class nsDisplaySubDocument : public nsDisplayOwnLayer
 {
 public:
--- a/layout/tools/reftest/reftest.xul
+++ b/layout/tools/reftest/reftest.xul
@@ -3,12 +3,13 @@
    - 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/. -->
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="reftest-window"
         hidechrome="true"
         onload="OnRefTestLoad();"
         onunload="OnRefTestUnload();"
         style="background:white; overflow:hidden"
+        webrendercontent="true"
         >
     <script type="application/ecmascript" src="resource://reftest/reftest.jsm" />
     <!-- The reftest browser element is dynamically created, here -->
 </window>
--- a/layout/xul/nsBoxFrame.cpp
+++ b/layout/xul/nsBoxFrame.cpp
@@ -55,16 +55,17 @@
 #include "nsSprocketLayout.h"
 #include "nsIScrollableFrame.h"
 #include "nsWidgetsCID.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsContainerFrame.h"
 #include "nsITheme.h"
 #include "nsTransform2D.h"
 #include "mozilla/EventStateManager.h"
+#include "mozilla/gfx/gfxVars.h"
 #include "nsDisplayList.h"
 #include "mozilla/Preferences.h"
 #include "nsStyleConsts.h"
 #include "nsLayoutUtils.h"
 #include "nsSliderFrame.h"
 #include <algorithm>
 
 // Needed for Print Preview
@@ -1145,69 +1146,86 @@ nsBoxFrame::AttributeChanged(int32_t aNa
   return rv;
 }
 
 void
 nsBoxFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                              const nsDisplayListSet& aLists)
 {
   bool forceLayer = false;
+  bool wrContentRect = false;
 
   if (GetContent()->IsXULElement()) {
+    if (gfxVars::UseWebRender() &&
+        GetContent()->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::webrendercontent)) {
+      wrContentRect = true;
+    }
     // forcelayer is only supported on XUL elements with box layout
     if (GetContent()->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
       forceLayer = true;
     }
     // Check for frames that are marked as a part of the region used
     // in calculating glass margins on Windows.
     const nsStyleDisplay* styles = StyleDisplay();
     if (styles && styles->mAppearance == StyleAppearance::MozWinExcludeGlass) {
       aBuilder->AddWindowExcludeGlassRegion(
           this,
           nsRect(aBuilder->ToReferenceFrame(this), GetSize()));
     }
   }
 
   nsDisplayListCollection tempLists(aBuilder);
-  const nsDisplayListSet& destination = forceLayer ? tempLists : aLists;
+  const nsDisplayListSet& destination = (forceLayer || wrContentRect) ? tempLists : aLists;
 
   DisplayBorderBackgroundOutline(aBuilder, destination);
 
   Maybe<nsDisplayListBuilder::AutoContainerASRTracker> contASRTracker;
-  if (forceLayer) {
+  if (forceLayer || wrContentRect) {
     contASRTracker.emplace(aBuilder);
   }
 
   BuildDisplayListForChildren(aBuilder, destination);
 
   // see if we have to draw a selection frame around this container
   DisplaySelectionOverlay(aBuilder, destination.Content());
 
-  if (forceLayer) {
+  if (forceLayer || wrContentRect) {
     // This is a bit of a hack. Collect up all descendant display items
     // and merge them into a single Content() list. This can cause us
     // to violate CSS stacking order, but forceLayer is a magic
     // XUL-only extension anyway.
     nsDisplayList masterList;
     masterList.AppendToTop(tempLists.BorderBackground());
     masterList.AppendToTop(tempLists.BlockBorderBackgrounds());
     masterList.AppendToTop(tempLists.Floats());
     masterList.AppendToTop(tempLists.Content());
     masterList.AppendToTop(tempLists.PositionedDescendants());
     masterList.AppendToTop(tempLists.Outlines());
-
     const ActiveScrolledRoot* ownLayerASR = contASRTracker->GetContainerASR();
-
     DisplayListClipState::AutoSaveRestore ownLayerClipState(aBuilder);
 
-    // Wrap the list to make it its own layer
-    aLists.Content()->AppendToTop(
-      MakeDisplayItem<nsDisplayOwnLayer>(aBuilder, this, &masterList, ownLayerASR,
-                                         nsDisplayOwnLayerFlags::eNone,
-                                         mozilla::layers::ScrollbarData{}, true, true));
+    if (forceLayer) {
+      MOZ_ASSERT(!wrContentRect);
+      // Wrap the list to make it its own layer
+      aLists.Content()->AppendToTop(
+        MakeDisplayItem<nsDisplayOwnLayer>(aBuilder, this, &masterList, ownLayerASR,
+                                           nsDisplayOwnLayerFlags::eNone,
+                                           mozilla::layers::ScrollbarData{}, true, true));
+    } else {
+      MOZ_ASSERT(wrContentRect);
+      MOZ_ASSERT(!XRE_IsContentProcess());
+      mozilla::LayoutDeviceRect rect =
+        mozilla::LayoutDeviceRect::FromAppUnits(GetRect(),
+                                                PresContext()->AppUnitsPerDevPixel());
+      aBuilder->SetContentRenderRootRect(rect);
+      aLists.Content()->AppendToTop(
+        MakeDisplayItem<nsDisplayRenderRoot>(aBuilder, this, &masterList, ownLayerASR,
+                                             HasAnyStateBits(NS_FRAME_NEEDS_PAINT |
+                                                             NS_FRAME_DESCENDANT_NEEDS_PAINT)));
+    }
   }
 }
 
 void
 nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
                                         const nsDisplayListSet& aLists)
 {
   nsIFrame* kid = mFrames.FirstChild();
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -609,16 +609,17 @@ STATIC_ATOMS = [
     Atom("menuitem", "menuitem"),
     Atom("menulist", "menulist"),
     Atom("menupopup", "menupopup"),
     Atom("menuseparator", "menuseparator"),
     Atom("message", "message"),
     Atom("meta", "meta"),
     Atom("referrer", "referrer"),
     Atom("referrerpolicy", "referrerpolicy"),
+    Atom("webrendercontent", "webrendercontent"),
     Atom("headerReferrerPolicy", "referrer-policy"),
     Atom("meter", "meter"),
     Atom("method", "method"),
     Atom("middle", "middle"),
     Atom("min", "min"),
     Atom("minheight", "minheight"),
     Atom("minimum_scale", "minimum-scale"),
     Atom("minlength", "minlength"),