Merge mozilla-central to autoland. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Tue, 09 Oct 2018 00:56:03 +0300
changeset 495757 aab7bf199799d5c3c696fa5c369491bff062873f
parent 495756 696f01b5d804309cedfb82debc291a822efd9072 (current diff)
parent 495744 df860e79a6a3cc41712cfd86ffbd18cf84fce626 (diff)
child 495758 7a32e3740263592cebceda4a55c3cf43caa88dfe
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -190,34 +190,34 @@ class UrlbarInput {
     this.inputField.value = val;
 
     return val;
   }
 
   // Private methods below.
 
   _updateTextOverflow() {
-    if (!this._inOverflow) {
+    if (!this._overflowing) {
       this.removeAttribute("textoverflow");
       return;
     }
 
     this.window.promiseDocumentFlushed(() => {
       // Check overflow again to ensure it didn't change in the meantime.
       let input = this.inputField;
-      if (input && this._inOverflow) {
+      if (input && this._overflowing) {
         let side = input.scrollLeft &&
                    input.scrollLeft == input.scrollLeftMax ? "start" : "end";
         this.setAttribute("textoverflow", side);
       }
     });
   }
 
   _updateUrlTooltip() {
-    if (this.focused || !this._inOverflow) {
+    if (this.focused || !this._overflowing) {
       this.inputField.removeAttribute("title");
     } else {
       this.inputField.setAttribute("title", this.value);
     }
   }
 
   _getSelectedValueForClipboard() {
     // Grab the actual input field's value, not our value, which could
@@ -387,29 +387,29 @@ class UrlbarInput {
   _on_overflow(event) {
     const targetIsPlaceholder =
       !event.originalTarget.classList.contains("anonymous-div");
     // We only care about the non-placeholder text.
     // This shouldn't be needed, see bug 1487036.
     if (targetIsPlaceholder) {
       return;
     }
-    this._inOverflow = true;
+    this._overflowing = true;
     this._updateTextOverflow();
   }
 
   _on_underflow(event) {
     const targetIsPlaceholder =
       !event.originalTarget.classList.contains("anonymous-div");
     // We only care about the non-placeholder text.
     // This shouldn't be needed, see bug 1487036.
     if (targetIsPlaceholder) {
       return;
     }
-    this._inOverflow = false;
+    this._overflowing = false;
 
     this._updateTextOverflow();
 
     this._updateUrlTooltip();
   }
 
   _on_scrollend(event) {
     this._updateTextOverflow();
--- a/devtools/client/netmonitor/src/selectors/requests.js
+++ b/devtools/client/netmonitor/src/selectors/requests.js
@@ -30,20 +30,27 @@ function sortWithClones(requests, sorter
       return -1;
     }
     b = requests.get(bOrigId);
   }
 
   return sorter(a, b);
 }
 
-const getFilterFn = createSelector(
+/**
+ * Take clones into account when filtering. If a request is
+ * a clone, it's not filtered out.
+ */
+const getFilterWithCloneFn = createSelector(
   state => state.filters,
   filters => r => {
     const matchesType = Object.keys(filters.requestFilterTypes).some(filter => {
+      if (r.id.endsWith("-clone")) {
+        return true;
+      }
       return filters.requestFilterTypes[filter] && Filters[filter] && Filters[filter](r);
     });
     return matchesType && isFreetextMatch(r, filters.requestFilterText);
   }
 );
 
 const getTypeFilterFn = createSelector(
   state => state.filters,
@@ -73,17 +80,17 @@ const getSortedRequests = createSelector
     arr.isEmpty = () => this.length == 0;
     arr.size = arr.length;
     return arr;
   }
 );
 
 const getDisplayedRequests = createSelector(
   state => state.requests,
-  getFilterFn,
+  getFilterWithCloneFn,
   getSortFn,
   ({ requests }, filterFn, sortFn) => {
     const arr = [...requests.values()].filter(filterFn).sort(sortFn);
     arr.get = index => arr[index];
     arr.isEmpty = () => this.length == 0;
     arr.size = arr.length;
     return arr;
   }
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -115,16 +115,17 @@ skip-if = (verify && debug && os == 'win
 subsuite = clipboard
 [browser_net_cors_requests.js]
 [browser_net_cyrillic-01.js]
 [browser_net_cyrillic-02.js]
 [browser_net_frame.js]
 skip-if = (os == 'mac') # Bug 1479782
 [browser_net_header-docs.js]
 [browser_net_edit_resend_caret.js]
+[browser_net_edit_resend_with_filtering.js]
 [browser_net_filter-01.js]
 [browser_net_filter-02.js]
 [browser_net_filter-03.js]
 [browser_net_filter-04.js]
 [browser_net_filter-autocomplete.js]
 [browser_net_filter-flags.js]
 [browser_net_footer-summary.js]
 [browser_net_headers-alignment.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_edit_resend_with_filtering.js
@@ -0,0 +1,55 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests if resending a XHR request while filtering XHR displays
+ * the correct requests
+ */
+add_task(async function() {
+  const { tab, monitor } = await initNetMonitor(POST_RAW_URL);
+
+  const { document, store, windowRequire, parent } = monitor.panelWin;
+  const parentDocument = parent.document;
+  const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+  store.dispatch(Actions.batchEnable(false));
+
+  // Execute XHR request and filter by XHR
+  await performRequests(monitor, tab, 1);
+  document.querySelector(".requests-list-filter-xhr-button").click();
+
+  // Confirm XHR request and click it
+  const xhrRequestItem = document.querySelectorAll(".request-list-item")[0];
+  EventUtils.sendMouseEvent({ type: "mousedown" }, xhrRequestItem);
+
+  const {
+    getSelectedRequest
+  } = windowRequire("devtools/client/netmonitor/src/selectors/index");
+  const firstRequest = getSelectedRequest(store.getState());
+
+  // Open context menu and execute "Edit & Resend".
+  EventUtils.sendMouseEvent({ type: "contextmenu" }, xhrRequestItem);
+  parentDocument.querySelector("#request-list-context-resend").click();
+
+  // Click Resend
+  await waitUntil(() => document.querySelector("#custom-request-send-button"));
+  document.querySelector("#custom-request-send-button").click();
+
+  // Filtering by "other" so the resent request is visible after completion
+  document.querySelector(".requests-list-filter-other-button").click();
+
+  // Select the cloned request
+  document.querySelectorAll(".request-list-item")[0].click();
+  const resendRequest = getSelectedRequest(store.getState());
+
+  ok(resendRequest.id !== firstRequest.id,
+    "The second XHR request was made and is unique");
+
+  ok(resendRequest.id.replace(/-clone$/, "") == firstRequest.id,
+    "The second XHR request is a clone of the first");
+
+  return teardown(monitor);
+});
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -75,18 +75,21 @@
 #include "mozilla/GuardObjects.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/ChromeMessageSender.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FrameLoaderBinding.h"
+#include "mozilla/gfx/CrossProcessPaint.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/ServoCSSParser.h"
+#include "mozilla/ServoStyleSet.h"
 #include "nsGenericHTMLFrameElement.h"
 #include "GeckoProfiler.h"
 
 #include "jsapi.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
 #include "nsSandboxFlags.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/dom/CustomEvent.h"
@@ -124,18 +127,18 @@ using namespace mozilla::layers;
 using namespace mozilla::layout;
 typedef FrameMetrics::ViewID ViewID;
 
 // Bug 136580: Limit to the number of nested content frames that can have the
 //             same URL. This is to stop content that is recursively loading
 //             itself.  Note that "#foo" on the end of URL doesn't affect
 //             whether it's considered identical, but "?foo" or ";foo" are
 //             considered and compared.
-// Bug 228829: Limit this to 1, like IE does.
-#define MAX_SAME_URL_CONTENT_FRAMES 1
+// Limit this to 2, like chromium does.
+#define MAX_SAME_URL_CONTENT_FRAMES 2
 
 // Bug 8065: Limit content frame depth to some reasonable level. This
 // does not count chrome frames when determining depth, nor does it
 // prevent chrome recursion.  Number is fairly arbitrary, but meant to
 // keep number of shells to a reasonable number on accidental recursion with a
 // small (but not 1) branching factor.  With large branching factors the number
 // of shells can rapidly become huge and run us out of memory.  To solve that,
 // we'd need to re-institute a fixed version of bug 98158.
@@ -3098,16 +3101,74 @@ nsFrameLoader::Print(uint64_t aOuterWind
   nsresult rv = webBrowserPrint->Print(aPrintSettings, aProgressListener);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return;
   }
 #endif
 }
 
+already_AddRefed<mozilla::dom::Promise>
+nsFrameLoader::DrawSnapshot(double aX,
+                            double aY,
+                            double aW,
+                            double aH,
+                            double aScale,
+                            const nsAString& aBackgroundColor,
+                            mozilla::ErrorResult& aRv)
+{
+  RefPtr<nsIGlobalObject> global = GetOwnerContent()->GetOwnerGlobal();
+  RefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  RefPtr<nsIDocument> document = GetOwnerContent()->GetOwnerDocument();
+  if (NS_WARN_IF(!document)) {
+    aRv = NS_ERROR_FAILURE;
+    return nullptr;
+  }
+  nsIPresShell* presShell = document->GetShell();
+  if (NS_WARN_IF(!presShell)) {
+    aRv = NS_ERROR_FAILURE;
+    return nullptr;
+  }
+
+  nscolor color;
+  css::Loader* loader = document->CSSLoader();
+  ServoStyleSet* set = presShell->StyleSet();
+  if (NS_WARN_IF(!ServoCSSParser::ComputeColor(set,
+                                               NS_RGB(0, 0, 0),
+                                               aBackgroundColor,
+                                               &color,
+                                               nullptr,
+                                               loader))) {
+    aRv = NS_ERROR_FAILURE;
+    return nullptr;
+  }
+
+  gfx::IntRect rect = gfx::IntRect::RoundOut(gfx::Rect(aX, aY, aW, aH));
+
+  if (IsRemoteFrame()) {
+    gfx::CrossProcessPaint::StartRemote(mRemoteBrowser->GetTabId(),
+                                        rect,
+                                        aScale,
+                                        color,
+                                        promise);
+  } else {
+    gfx::CrossProcessPaint::StartLocal(mDocShell,
+                                       rect,
+                                       aScale,
+                                       color,
+                                       promise);
+  }
+
+  return promise.forget();
+}
+
 already_AddRefed<nsITabParent>
 nsFrameLoader::GetTabParent()
 {
   return do_AddRef(mRemoteBrowser);
 }
 
 already_AddRefed<nsILoadContext>
 nsFrameLoader::LoadContext()
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -166,16 +166,25 @@ public:
 
   void RequestUpdatePosition(mozilla::ErrorResult& aRv);
 
   void Print(uint64_t aOuterWindowID,
              nsIPrintSettings* aPrintSettings,
              nsIWebProgressListener* aProgressListener,
              mozilla::ErrorResult& aRv);
 
+  already_AddRefed<mozilla::dom::Promise>
+  DrawSnapshot(double aX,
+               double aY,
+               double aW,
+               double aH,
+               double aScale,
+               const nsAString& aBackgroundColor,
+               mozilla::ErrorResult& aRv);
+
   void StartPersistence(uint64_t aOuterWindowID,
                         nsIWebBrowserPersistDocumentReceiver* aRecv,
                         mozilla::ErrorResult& aRv);
 
   // WebIDL getters
 
   already_AddRefed<mozilla::dom::MessageSender> GetMessageManager();
 
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -804,16 +804,27 @@ ImageBitmap::ToCloneData() const
   RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
   result->mSurface = surface->GetDataSurface();
   MOZ_ASSERT(result->mSurface);
 
   return result;
 }
 
 /* static */ already_AddRefed<ImageBitmap>
+ImageBitmap::CreateFromSourceSurface(nsIGlobalObject* aGlobal,
+                                     gfx::SourceSurface* aSource,
+                                     ErrorResult& aRv)
+{
+  RefPtr<layers::Image> data = CreateImageFromSurface(aSource);
+  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+  ret->mAllocatedImageData = true;
+  return ret.forget();
+}
+
+/* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
                                  ImageBitmapCloneData* aData)
 {
   RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aData->mAlphaType);
 
   ret->mAllocatedImageData = true;
--- a/dom/canvas/ImageBitmap.h
+++ b/dom/canvas/ImageBitmap.h
@@ -114,16 +114,21 @@ public:
   already_AddRefed<layers::Image>
   TransferAsImage();
 
   // This method returns null if the image has been already closed.
   UniquePtr<ImageBitmapCloneData>
   ToCloneData() const;
 
   static already_AddRefed<ImageBitmap>
+  CreateFromSourceSurface(nsIGlobalObject* aGlobal,
+                          gfx::SourceSurface* aSource,
+                          ErrorResult& aRv);
+
+  static already_AddRefed<ImageBitmap>
   CreateFromCloneData(nsIGlobalObject* aGlobal, ImageBitmapCloneData* aData);
 
   static already_AddRefed<ImageBitmap>
   CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
                             OffscreenCanvas& aOffscreenCanvas,
                             ErrorResult& aRv);
 
   static already_AddRefed<Promise>
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -602,8 +602,9 @@ skip-if = os == "android" # up/down arro
 [test_fakepath.html]
 [test_script_module.html]
 support-files =
   file_script_module.html
   file_script_nomodule.html
 [test_getElementsByName_after_mutation.html]
 [test_bug1279218.html]
 [test_set_input_files.html]
+[test_nestediframe.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_nestediframe.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for same URLs nested iframes</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+function reportState(msg) {
+  if (location.href.includes("#")) {
+    parent.postMessage(msg, "*");
+    return;
+  }
+
+  if (msg == "OK 1") {
+    ok(true, "First frame loaded");
+  } else if (msg == "KO 2") {
+    ok(true, "Second frame load failed");
+    SimpleTest.finish();
+  } else {
+    ok(false, "Unknown message: " + msg);
+  }
+}
+
+addEventListener("message", event => {
+  reportState(event.data);
+});
+
+var recursion;
+if (!location.href.includes("#")) {
+  recursion = 1;
+} else {
+  recursion = parseInt(localStorage.recursion) + 1;
+}
+localStorage.recursion = "" + recursion;
+
+var ifr = document.createElement('iframe');
+ifr.src = location.href.split("#")[0] + "#" + recursion;
+document.body.appendChild(ifr);
+
+ifr.onload = function() {
+  reportState("OK " + recursion);
+}
+
+ifr.onerror = function() {
+  reportState("KO " + recursion);
+}
+
+</script>
+</body>
+</html>
--- a/dom/ipc/ContentProcessManager.cpp
+++ b/dom/ipc/ContentProcessManager.cpp
@@ -176,16 +176,17 @@ ContentProcessManager::RegisterRemoteFra
       return false;
     }
     info.mOpenerCpId = aOpenerCpId;
     info.mOpenerTabId = aOpenerTabId;
     info.mContext = tc.GetTabContext();
   }
 
   iter->second.mRemoteFrames[aTabId] = info;
+  mTabProcessMap[aTabId] = aChildCpId;
   return true;
 }
 
 void
 ContentProcessManager::UnregisterRemoteFrame(const ContentParentId& aChildCpId,
                                              const TabId& aChildTabId)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -195,16 +196,21 @@ ContentProcessManager::UnregisterRemoteF
     ASSERT_UNLESS_FUZZING();
     return;
   }
 
   auto remoteFrameIter = iter->second.mRemoteFrames.find(aChildTabId);
   if (remoteFrameIter != iter->second.mRemoteFrames.end()) {
     iter->second.mRemoteFrames.erase(aChildTabId);
   }
+
+  auto tabProcessIter = mTabProcessMap.find(aChildTabId);
+  if (tabProcessIter != mTabProcessMap.end()) {
+    mTabProcessMap.erase(tabProcessIter);
+  }
 }
 
 bool
 ContentProcessManager::GetTabContextByProcessAndTabId(const ContentParentId& aChildCpId,
                                                       const TabId& aChildTabId,
                                                       /*out*/ TabContext* aTabContext)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -268,16 +274,27 @@ ContentProcessManager::GetRemoteFrameOpe
   }
 
   *aOpenerCpId = remoteFrameIter->second.mOpenerCpId;
   *aOpenerTabId = remoteFrameIter->second.mOpenerTabId;
 
   return true;
 }
 
+ContentParentId
+ContentProcessManager::GetTabProcessId(const TabId& aTabId)
+{
+  auto tabProcessIter = mTabProcessMap.find(aTabId);
+  MOZ_ASSERT(tabProcessIter != mTabProcessMap.end());
+  if (tabProcessIter == mTabProcessMap.end()) {
+    return ContentParentId(0);
+  }
+  return tabProcessIter->second;
+}
+
 already_AddRefed<TabParent>
 ContentProcessManager::GetTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
                                                      const TabId& aChildTabId)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   auto iter = mContentParentMap.find(aChildCpId);
   if (NS_WARN_IF(iter == mContentParentMap.end())) {
--- a/dom/ipc/ContentProcessManager.h
+++ b/dom/ipc/ContentProcessManager.h
@@ -111,16 +111,22 @@ public:
    * XXX Currently not used. Plan to be used for bug 1020179.
    */
   bool GetRemoteFrameOpenerTabId(const ContentParentId& aChildCpId,
                                  const TabId& aChildTabId,
                                  /*out*/ContentParentId* aOpenerCpId,
                                  /*out*/ TabId* aOpenerTabId);
 
   /**
+   * Get the ContentParentId of the parent of the given tab id.
+   */
+  ContentParentId
+  GetTabProcessId(const TabId& aTabId);
+
+  /**
    * Get all TabParents' Ids managed by the givent content process.
    * Return empty array when TabParent couldn't be found via aChildCpId
    */
   nsTArray<TabId>
   GetTabParentsByProcessId(const ContentParentId& aChildCpId);
 
   /**
    * Get the number of TabParents managed by the givent content process.
@@ -152,16 +158,17 @@ public:
    */
   already_AddRefed<TabParent>
   GetTopLevelTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
                                         const TabId& aChildTabId);
 
 private:
   static StaticAutoPtr<ContentProcessManager> sSingleton;
   std::map<ContentParentId, ContentProcessInfo> mContentParentMap;
+  std::map<TabId, ContentParentId> mTabProcessMap;
 
   ContentProcessManager() {MOZ_COUNT_CTOR(ContentProcessManager);};
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ContentProcessManager_h
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -27,16 +27,17 @@ include JavaScriptTypes;
 include URIParams;
 include PPrintingTypes;
 include PTabContext;
 
 include "mozilla/GfxMessageUtils.h";
 include "mozilla/layers/LayersMessageUtils.h";
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
+using moveonly mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
 using mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
 using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using mozilla::LayoutDevicePoint from "Units.h";
 using mozilla::ScreenIntPoint from "Units.h";
 using ScreenIntSize from "Units.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
@@ -761,16 +762,19 @@ child:
      *        visible.
      * @param aEpoch
      *        The layer observer epoch for this activation. This message should be
      *        ignored if this epoch has already been observed (via
      *        PaintWhileInterruptingJS).
      */
     async RenderLayers(bool aEnabled, bool aForceRepaint, LayersObserverEpoch aEpoch);
 
+    async RequestRootPaint(IntRect aRect, float aScale, nscolor aBackgroundColor) returns (PaintFragment retval);
+    async RequestSubPaint(float aScale, nscolor aBackgroundColor) returns (PaintFragment retval);
+child:
     /**
      * Notify the child that it shouldn't paint the offscreen displayport.
      * This is useful to speed up interactive operations over async
      * scrolling performance like resize, tabswitch, pageload.
      *
      * Each enable call must be matched with a disable call. The child
      * will remain in the suppress mode as long as there's
      * a single unmatched call.
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/PaymentRequestChild.h"
+#include "mozilla/gfx/CrossProcessPaint.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/layers/APZCTreeManagerChild.h"
 #include "mozilla/layers/APZEventState.h"
 #include "mozilla/layers/ContentProcessController.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
@@ -2663,16 +2664,41 @@ TabChild::RecvRenderLayers(const bool& a
 
     MakeHidden();
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+TabChild::RecvRequestRootPaint(const IntRect& aRect, const float& aScale, const nscolor& aBackgroundColor, RequestRootPaintResolver&& aResolve)
+{
+  nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+  if (!docShell) {
+    return IPC_OK();
+  }
+
+  aResolve(gfx::PaintFragment::Record(docShell, aRect, aScale, aBackgroundColor));
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+TabChild::RecvRequestSubPaint(const float& aScale, const nscolor& aBackgroundColor, RequestSubPaintResolver&& aResolve)
+{
+  nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+  if (!docShell) {
+    return IPC_OK();
+  }
+
+  gfx::IntRect rect = gfx::RoundedIn(gfx::Rect(0.0f, 0.0f, mUnscaledInnerSize.width, mUnscaledInnerSize.height));
+  aResolve(gfx::PaintFragment::Record(docShell, rect, aScale, aBackgroundColor));
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 TabChild::RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation)
 {
   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   if (fm) {
     RefPtr<Element> result;
     nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
 
     // Move to the first or last document.
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -719,16 +719,20 @@ protected:
   virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
 
   virtual mozilla::ipc::IPCResult RecvDestroy() override;
 
   virtual mozilla::ipc::IPCResult RecvSetDocShellIsActive(const bool& aIsActive) override;
 
   virtual mozilla::ipc::IPCResult RecvRenderLayers(const bool& aEnabled, const bool& aForce, const layers::LayersObserverEpoch& aEpoch) override;
 
+  virtual mozilla::ipc::IPCResult RecvRequestRootPaint(const IntRect& aRect, const float& aScale, const nscolor& aBackgroundColor, RequestRootPaintResolver&& aResolve) override;
+
+  virtual mozilla::ipc::IPCResult RecvRequestSubPaint(const float& aScale, const nscolor& aBackgroundColor, RequestSubPaintResolver&& aResolve) override;
+
   virtual mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward,
                                                     const bool& aForDocumentNavigation) override;
 
   virtual mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() override;
 
   virtual mozilla::ipc::IPCResult RecvSuppressDisplayport(const bool& aEnabled) override;
 
   virtual mozilla::ipc::IPCResult RecvParentActivated(const bool& aActivated) override;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -3093,16 +3093,48 @@ TabParent::LayerTreeUpdate(const LayersO
   } else {
     event->InitEvent(NS_LITERAL_STRING("MozLayerTreeCleared"), true, false);
   }
   event->SetTrusted(true);
   event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   mFrameElement->DispatchEvent(*event);
 }
 
+void
+TabParent::RequestRootPaint(gfx::CrossProcessPaint* aPaint, IntRect aRect, float aScale, nscolor aBackgroundColor)
+{
+  auto promise = SendRequestRootPaint(aRect, aScale, aBackgroundColor);
+
+  RefPtr<gfx::CrossProcessPaint> paint(aPaint);
+  TabId tabId(GetTabId());
+  promise->Then(GetMainThreadSerialEventTarget(), __func__,
+                [paint, tabId] (PaintFragment&& aFragment) {
+                  paint->ReceiveFragment(tabId, std::move(aFragment));
+                },
+                [paint, tabId] (ResponseRejectReason aReason) {
+                  paint->LostFragment(tabId);
+                });
+}
+
+void
+TabParent::RequestSubPaint(gfx::CrossProcessPaint* aPaint, float aScale, nscolor aBackgroundColor)
+{
+  auto promise = SendRequestSubPaint(aScale, aBackgroundColor);
+
+  RefPtr<gfx::CrossProcessPaint> paint(aPaint);
+  TabId tabId(GetTabId());
+  promise->Then(GetMainThreadSerialEventTarget(), __func__,
+                [paint, tabId] (PaintFragment&& aFragment) {
+                  paint->ReceiveFragment(tabId, std::move(aFragment));
+                },
+                [paint, tabId] (ResponseRejectReason aReason) {
+                  paint->LostFragment(tabId);
+                });
+}
+
 mozilla::ipc::IPCResult
 TabParent::RecvPaintWhileInterruptingJSNoOp(const LayersObserverEpoch& aEpoch)
 {
   // We sent a PaintWhileInterruptingJS message when layers were already visible. In this
   // case, we should act as if an update occurred even though we already have
   // the layers.
   LayerTreeUpdate(aEpoch, true);
   return IPC_OK();
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -12,16 +12,17 @@
 #include "mozilla/ContentCache.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/PFilePickerParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/gfx/CrossProcessPaint.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Move.h"
 #include "nsCOMPtr.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDOMEventListener.h"
 #include "nsIKeyEventInPluginCallback.h"
@@ -557,16 +558,19 @@ public:
 
   bool IsInitedByParent() const { return mInitedByParent; }
 
   bool SendLoadRemoteScript(const nsString& aURL,
                             const bool& aRunInGlobalScope);
 
   void LayerTreeUpdate(const LayersObserverEpoch& aEpoch, bool aActive);
 
+  void RequestRootPaint(gfx::CrossProcessPaint* aPaint, IntRect aRect, float aScale, nscolor aBackgroundColor);
+  void RequestSubPaint(gfx::CrossProcessPaint* aPaint, float aScale, nscolor aBackgroundColor);
+
   virtual mozilla::ipc::IPCResult
   RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
                         const uint32_t& aAction,
                         const OptionalShmem& aVisualDnDData,
                         const uint32_t& aStride, const gfx::SurfaceFormat& aFormat,
                         const LayoutDeviceIntRect& aDragRect,
                         const nsCString& aPrincipalURISpec) override;
 
--- a/dom/webidl/FrameLoader.webidl
+++ b/dom/webidl/FrameLoader.webidl
@@ -104,16 +104,40 @@ interface FrameLoader {
    * @param aProgressListener optional print progress listener.
    */
   [Throws]
   void print(unsigned long long aOuterWindowID,
              nsIPrintSettings aPrintSettings,
              optional nsIWebProgressListener? aProgressListener = null);
 
   /**
+   * Renders a region of the frame into an image bitmap.
+   *
+   * @param x
+   * @param y
+   * @param w
+   * @param h Specify the area of the window to render, in CSS
+   * pixels. This is relative to the current scroll position.
+   * @param scale The scale to render the window at. Use devicePixelRatio
+   * to have comparable rendering to the OS.
+   * @param backgroundColor The background color to use.
+   *
+   * This API can only be used in the parent process, as content processes
+   * cannot access the rendering of out of process iframes. This API works
+   * with remote and local frames.
+   */
+  [Throws]
+  Promise<ImageBitmap> drawSnapshot(double x,
+                                    double y,
+                                    double w,
+                                    double h,
+                                    double scale,
+                                    DOMString backgroundColor);
+
+  /**
    * If false, then the subdocument is not clipped to its CSS viewport, and the
    * subdocument's viewport scrollbar(s) are not rendered.
    * Defaults to true.
    */
   attribute boolean clipSubdocument;
 
   /**
    * The element which owns this frame loader.
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1032,16 +1032,22 @@ public:
    * @param aSurfOptions DrawSurface options that are applied
    */
   virtual void DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
                            const Rect &aSource,
                            const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
                            const DrawOptions &aOptions = DrawOptions()) = 0;
 
+  virtual void DrawDependentSurface(uint64_t aId,
+                                    const Rect &aDest,
+                                    const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+                                    const DrawOptions &aOptions = DrawOptions())
+  { MOZ_CRASH("GFX: DrawDependentSurface"); }
+
   /**
    * Draw the output of a FilterNode to the DrawTarget.
    *
    * @param aNode FilterNode to draw
    * @param aSourceRect Source rectangle in FilterNode space to draw
    * @param aDestPoint Destination point on the DrawTarget to draw the
    *                   SourceRectangle of the filter output to
    */
--- a/gfx/2d/DrawEventRecorder.cpp
+++ b/gfx/2d/DrawEventRecorder.cpp
@@ -61,16 +61,28 @@ DrawEventRecorderFile::RecordEvent(const
 void
 DrawEventRecorderMemory::RecordEvent(const RecordedEvent &aEvent)
 {
   WriteElement(mOutputStream, aEvent.mType);
 
   aEvent.RecordToStream(mOutputStream);
 }
 
+void
+DrawEventRecorderMemory::AddDependentSurface(uint64_t aDependencyId)
+{
+  mDependentSurfaces.PutEntry(aDependencyId);
+}
+
+nsTHashtable<nsUint64HashKey>&&
+DrawEventRecorderMemory::TakeDependentSurfaces()
+{
+  return std::move(mDependentSurfaces);
+}
+
 DrawEventRecorderFile::DrawEventRecorderFile(const char_type* aFilename)
   : mOutputStream(aFilename, ofstream::binary)
 {
   WriteHeader(mOutputStream);
 }
 
 DrawEventRecorderFile::~DrawEventRecorderFile()
 {
@@ -109,17 +121,17 @@ DrawEventRecorderFile::Close()
 DrawEventRecorderMemory::DrawEventRecorderMemory()
 {
   WriteHeader(mOutputStream);
 }
 
 DrawEventRecorderMemory::DrawEventRecorderMemory(const SerializeResourcesFn &aFn) :
   mSerializeCallback(aFn)
 {
-  mExternalFonts = true;
+  mExternalFonts = !!mSerializeCallback;
   WriteHeader(mOutputStream);
 }
 
 
 void
 DrawEventRecorderMemory::Flush()
 {
 }
--- a/gfx/2d/DrawEventRecorder.h
+++ b/gfx/2d/DrawEventRecorder.h
@@ -11,16 +11,19 @@
 #include "RecordedEvent.h"
 #include "RecordingTypes.h"
 #include "mozilla/FStream.h"
 
 #include <unordered_set>
 #include <unordered_map>
 #include <functional>
 
+#include "nsHashKeys.h"
+#include "nsTHashtable.h"
+
 namespace mozilla {
 namespace gfx {
 
 class PathRecording;
 
 class DrawEventRecorderPrivate : public DrawEventRecorder
 {
 public:
@@ -105,16 +108,21 @@ public:
   void TakeExternalSurfaces(std::vector<RefPtr<SourceSurface>>& aSurfaces)
   {
     aSurfaces = std::move(mExternalSurfaces);
   }
 
   virtual void StoreSourceSurfaceRecording(SourceSurface *aSurface,
                                            const char *aReason);
 
+  virtual void AddDependentSurface(uint64_t aDependencyId)
+  {
+    MOZ_CRASH("GFX: AddDependentSurface");
+  }
+
 protected:
   void StoreExternalSurfaceRecording(SourceSurface* aSurface,
                                      uint64_t aKey);
 
   virtual void Flush() = 0;
 
   std::unordered_set<const void*> mStoredObjects;
   std::unordered_set<uint64_t> mStoredFontData;
@@ -172,16 +180,20 @@ public:
   /**
    * Constructs a DrawEventRecorder that stores the recording in memory.
    */
   DrawEventRecorderMemory();
   explicit DrawEventRecorderMemory(const SerializeResourcesFn &aSerialize);
 
   void RecordEvent(const RecordedEvent &aEvent) override;
 
+  void AddDependentSurface(uint64_t aDependencyId) override;
+
+  nsTHashtable<nsUint64HashKey>&& TakeDependentSurfaces();
+
   /**
    * @return the current size of the recording (in chars).
    */
   size_t RecordingSize();
 
   /**
    * Wipes the internal recording buffer, but the recorder does NOT forget which
    * objects it has recorded. This can be used so that a recording can be copied
@@ -200,16 +212,17 @@ public:
    */
   MemStream mIndex;
 
 protected:
   ~DrawEventRecorderMemory() {};
 
 private:
   SerializeResourcesFn mSerializeCallback;
+  nsTHashtable<nsUint64HashKey> mDependentSurfaces;
 
   void Flush() override;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_DRAWEVENTRECORDER_H_ */
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -413,16 +413,26 @@ DrawTargetRecording::DrawSurface(SourceS
                                  const DrawOptions &aOptions)
 {
   EnsureSurfaceStoredRecording(mRecorder, aSurface, "DrawSurface");
 
   mRecorder->RecordEvent(RecordedDrawSurface(this, aSurface, aDest, aSource, aSurfOptions, aOptions));
 }
 
 void
+DrawTargetRecording::DrawDependentSurface(uint64_t aId,
+                                          const Rect &aDest,
+                                          const DrawSurfaceOptions &aSurfOptions,
+                                          const DrawOptions &aOptions)
+{
+  mRecorder->AddDependentSurface(aId);
+  mRecorder->RecordEvent(RecordedDrawDependentSurface(this, aId, aDest, aSurfOptions, aOptions));
+}
+
+void
 DrawTargetRecording::DrawSurfaceWithShadow(SourceSurface *aSurface,
                                            const Point &aDest,
                                            const Color &aColor,
                                            const Point &aOffset,
                                            Float aSigma,
                                            CompositionOp aOp)
 {
   EnsureSurfaceStoredRecording(mRecorder, aSurface, "DrawSurfaceWithShadow");
--- a/gfx/2d/DrawTargetRecording.h
+++ b/gfx/2d/DrawTargetRecording.h
@@ -52,16 +52,21 @@ public:
    * aSurfOptions DrawSurface options that are applied
    */
   virtual void DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
                            const Rect &aSource,
                            const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
                            const DrawOptions &aOptions = DrawOptions()) override;
 
+  virtual void DrawDependentSurface(uint64_t aId,
+                                    const Rect &aDest,
+                                    const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+                                    const DrawOptions &aOptions = DrawOptions()) override;
+
   virtual void DrawFilter(FilterNode *aNode,
                           const Rect &aSourceRect,
                           const Point &aDestPoint,
                           const DrawOptions &aOptions = DrawOptions()) override;
 
   /*
    * Blend a surface to the draw target with a shadow. The shadow is drawn as a
    * gaussian blur using a specified sigma. The shadow is clipped to the size
--- a/gfx/2d/InlineTranslator.h
+++ b/gfx/2d/InlineTranslator.h
@@ -29,16 +29,21 @@ using gfx::NativeFontResource;
 
 class InlineTranslator : public Translator
 {
 public:
   explicit InlineTranslator(DrawTarget* aDT, void* aFontContext = nullptr);
 
   bool TranslateRecording(char *, size_t len);
 
+  void SetExternalSurfaces(nsRefPtrHashtable<nsUint64HashKey, SourceSurface>* aExternalSurfaces)
+  {
+    mExternalSurfaces = aExternalSurfaces;
+  }
+
   DrawTarget* LookupDrawTarget(ReferencePtr aRefPtr) final
   {
     DrawTarget* result = mDrawTargets.GetWeak(aRefPtr);
     MOZ_ASSERT(result);
     return result;
   }
 
   Path* LookupPath(ReferencePtr aRefPtr) final
@@ -85,16 +90,21 @@ public:
 
   NativeFontResource* LookupNativeFontResource(uint64_t aKey) final
   {
     NativeFontResource* result = mNativeFontResources.GetWeak(aKey);
     MOZ_ASSERT(result);
     return result;
   }
 
+  already_AddRefed<SourceSurface> LookupExternalSurface(uint64_t aKey) override
+  {
+    return mExternalSurfaces->Get(aKey);
+  }
+
   void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget *aDT) final
   {
     mDrawTargets.Put(aRefPtr, aDT);
   }
 
   void AddPath(ReferencePtr aRefPtr, Path *aPath) final
   {
     mPaths.Put(aRefPtr, aPath);
@@ -182,14 +192,15 @@ private:
   nsRefPtrHashtable<nsPtrHashKey<void>, DrawTarget> mDrawTargets;
   nsRefPtrHashtable<nsPtrHashKey<void>, Path> mPaths;
   nsRefPtrHashtable<nsPtrHashKey<void>, SourceSurface> mSourceSurfaces;
   nsRefPtrHashtable<nsPtrHashKey<void>, FilterNode> mFilterNodes;
   nsRefPtrHashtable<nsPtrHashKey<void>, GradientStops> mGradientStops;
   nsRefPtrHashtable<nsPtrHashKey<void>, ScaledFont> mScaledFonts;
   nsRefPtrHashtable<nsPtrHashKey<void>, UnscaledFont> mUnscaledFonts;
   nsRefPtrHashtable<nsUint64HashKey, NativeFontResource> mNativeFontResources;
+  nsRefPtrHashtable<nsUint64HashKey, SourceSurface>* mExternalSurfaces;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // mozilla_layout_InlineTranslator_h
--- a/gfx/2d/RecordedEvent.cpp
+++ b/gfx/2d/RecordedEvent.cpp
@@ -60,16 +60,18 @@ RecordedEvent::GetEventName(EventType aT
   case FILLGLYPHS:
     return "FillGlyphs";
   case MASK:
     return "Mask";
   case STROKE:
     return "Stroke";
   case DRAWSURFACE:
     return "DrawSurface";
+  case DRAWDEPENDENTSURFACE:
+    return "DrawDependentSurface";
   case DRAWSURFACEWITHSHADOW:
     return "DrawSurfaceWithShadow";
   case DRAWFILTER:
     return "DrawFilter";
   case PATHCREATION:
     return "PathCreation";
   case PATHDESTRUCTION:
     return "PathDestruction";
--- a/gfx/2d/RecordedEvent.h
+++ b/gfx/2d/RecordedEvent.h
@@ -229,16 +229,17 @@ public:
     PUSHCLIP,
     PUSHCLIPRECT,
     POPCLIP,
     FILL,
     FILLGLYPHS,
     MASK,
     STROKE,
     DRAWSURFACE,
+    DRAWDEPENDENTSURFACE,
     DRAWSURFACEWITHSHADOW,
     PATHCREATION,
     PATHDESTRUCTION,
     SOURCESURFACECREATION,
     SOURCESURFACEDESTRUCTION,
     GRADIENTSTOPSCREATION,
     GRADIENTSTOPSDESTRUCTION,
     SNAPSHOT,
--- a/gfx/2d/RecordedEventImpl.h
+++ b/gfx/2d/RecordedEventImpl.h
@@ -639,16 +639,45 @@ private:
 
   ReferencePtr mRefSource;
   Rect mDest;
   Rect mSource;
   DrawSurfaceOptions mDSOptions;
   DrawOptions mOptions;
 };
 
+class RecordedDrawDependentSurface : public RecordedDrawingEvent<RecordedDrawDependentSurface> {
+public:
+  RecordedDrawDependentSurface(DrawTarget *aDT, uint64_t aId, const Rect &aDest,
+                      const DrawSurfaceOptions &aDSOptions,
+                      const DrawOptions &aOptions)
+    : RecordedDrawingEvent(DRAWDEPENDENTSURFACE, aDT), mId(aId), mDest(aDest)
+    , mDSOptions(aDSOptions), mOptions(aOptions)
+  {
+  }
+
+  virtual bool PlayEvent(Translator *aTranslator) const override;
+
+  template<class S> void Record(S &aStream) const;
+  virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const override;
+
+  virtual std::string GetName() const override { return "DrawDependentSurface"; }
+
+private:
+  friend class RecordedEvent;
+
+  template<class S>
+  MOZ_IMPLICIT RecordedDrawDependentSurface(S &aStream);
+
+  uint64_t mId;
+  Rect mDest;
+  DrawSurfaceOptions mDSOptions;
+  DrawOptions mOptions;
+};
+
 class RecordedDrawSurfaceWithShadow : public RecordedDrawingEvent<RecordedDrawSurfaceWithShadow> {
 public:
   RecordedDrawSurfaceWithShadow(DrawTarget *aDT, ReferencePtr aRefSource, const Point &aDest,
                                 const Color &aColor, const Point &aOffset,
                                 Float aSigma, CompositionOp aOp)
     : RecordedDrawingEvent(DRAWSURFACEWITHSHADOW, aDT), mRefSource(aRefSource), mDest(aDest)
     , mColor(aColor), mOffset(aOffset), mSigma(aSigma), mOp(aOp)
   {
@@ -2427,16 +2456,53 @@ RecordedDrawSurface::RecordedDrawSurface
 
 inline void
 RecordedDrawSurface::OutputSimpleEventInfo(std::stringstream &aStringStream) const
 {
   aStringStream << "[" << mDT << "] DrawSurface (" << mRefSource << ")";
 }
 
 inline bool
+RecordedDrawDependentSurface::PlayEvent(Translator *aTranslator) const
+{
+  RefPtr<SourceSurface> surface(aTranslator->LookupExternalSurface(mId));
+  aTranslator->LookupDrawTarget(mDT)->
+    DrawSurface(surface, mDest, Rect(Point(), Size(surface->GetSize())),
+                mDSOptions, mOptions);
+  return true;
+}
+
+template<class S>
+void
+RecordedDrawDependentSurface::Record(S &aStream) const
+{
+  RecordedDrawingEvent::Record(aStream);
+  WriteElement(aStream, mId);
+  WriteElement(aStream, mDest);
+  WriteElement(aStream, mDSOptions);
+  WriteElement(aStream, mOptions);
+}
+
+template<class S>
+RecordedDrawDependentSurface::RecordedDrawDependentSurface(S &aStream)
+  : RecordedDrawingEvent(DRAWDEPENDENTSURFACE, aStream)
+{
+  ReadElement(aStream, mId);
+  ReadElement(aStream, mDest);
+  ReadElement(aStream, mDSOptions);
+  ReadElement(aStream, mOptions);
+}
+
+inline void
+RecordedDrawDependentSurface::OutputSimpleEventInfo(std::stringstream &aStringStream) const
+{
+  aStringStream << "[" << mDT << "] DrawDependentSurface (" << mId << ")";
+}
+
+inline bool
 RecordedDrawFilter::PlayEvent(Translator *aTranslator) const
 {
   aTranslator->LookupDrawTarget(mDT)->
     DrawFilter(aTranslator->LookupFilterNode(mNode), mSourceRect,
                 mDestPoint, mOptions);
   return true;
 }
 
@@ -3438,16 +3504,17 @@ RecordedFilterNodeSetInput::OutputSimple
     f(PUSHCLIPRECT, RecordedPushClipRect); \
     f(PUSHCLIP, RecordedPushClip); \
     f(POPCLIP, RecordedPopClip); \
     f(FILL, RecordedFill); \
     f(FILLGLYPHS, RecordedFillGlyphs); \
     f(MASK, RecordedMask); \
     f(STROKE, RecordedStroke); \
     f(DRAWSURFACE, RecordedDrawSurface); \
+    f(DRAWDEPENDENTSURFACE, RecordedDrawDependentSurface); \
     f(DRAWSURFACEWITHSHADOW, RecordedDrawSurfaceWithShadow); \
     f(DRAWFILTER, RecordedDrawFilter); \
     f(PATHCREATION, RecordedPathCreation); \
     f(PATHDESTRUCTION, RecordedPathDestruction); \
     f(SOURCESURFACECREATION, RecordedSourceSurfaceCreation); \
     f(SOURCESURFACEDESTRUCTION, RecordedSourceSurfaceDestruction); \
     f(FILTERNODECREATION, RecordedFilterNodeCreation); \
     f(FILTERNODEDESTRUCTION, RecordedFilterNodeDestruction); \
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/CrossProcessPaint.cpp
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CrossProcessPaint.h"
+
+#include "mozilla/dom/ContentProcessManager.h"
+#include "mozilla/dom/ImageBitmap.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/gfx/DrawEventRecorder.h"
+#include "mozilla/gfx/InlineTranslator.h"
+#include "mozilla/PresShell.h"
+
+#include "gfxPlatform.h"
+
+#include "nsContentUtils.h"
+#include "nsGlobalWindowInner.h"
+#include "nsIDocShell.h"
+#include "nsPresContext.h"
+
+#define ENABLE_PAINT_LOG 0
+// #define ENABLE_PAINT_LOG 1
+
+#if ENABLE_PAINT_LOG
+#  define PF_LOG(...) printf_stderr("PaintFragment: " __VA_ARGS__)
+#  define CPP_LOG(...) printf_stderr("CrossProcessPaint: " __VA_ARGS__)
+#else
+#  define PF_LOG(...)
+#  define CPP_LOG(...)
+#endif
+
+namespace mozilla {
+namespace gfx {
+
+using namespace mozilla::ipc;
+
+/// The minimum scale we allow tabs to be rasterized at.
+static const float kMinPaintScale = 0.05;
+
+/* static */ PaintFragment
+PaintFragment::Record(nsIDocShell* aDocShell,
+                      const IntRect& aRect,
+                      float aScale,
+                      nscolor aBackgroundColor)
+{
+  IntSize surfaceSize = aRect.Size();
+  surfaceSize.width *= aScale;
+  surfaceSize.height *= aScale;
+
+  CPP_LOG("Recording "
+          "[docshell=%p, "
+          "rect=(%d, %d) x (%d, %d), "
+          "scale=%f, "
+          "color=(%u, %u, %u, %u)]\n",
+    aDocShell,
+    aRect.x, aRect.y, aRect.width, aRect.height,
+    aScale,
+    NS_GET_R(aBackgroundColor),
+    NS_GET_G(aBackgroundColor),
+    NS_GET_B(aBackgroundColor),
+    NS_GET_A(aBackgroundColor));
+
+  // Check for invalid sizes
+  if (surfaceSize.width <= 0 || surfaceSize.height <= 0 ||
+      !Factory::CheckSurfaceSize(surfaceSize)) {
+    PF_LOG("Invalid surface size of (%d x %d).\n",
+      surfaceSize.width,
+      surfaceSize.height);
+    return PaintFragment{};
+  }
+
+  // Flush any pending notifications
+  nsContentUtils::FlushLayoutForTree(aDocShell->GetWindow());
+
+  // Grab the presentation shell to render
+  RefPtr<nsPresContext> presContext;
+  if (aDocShell) {
+    aDocShell->GetPresContext(getter_AddRefs(presContext));
+  }
+  if (!presContext) {
+    PF_LOG("Couldn't find PresContext.\n");
+    return PaintFragment{};
+  }
+
+  // Initialize the recorder
+  SurfaceFormat format = SurfaceFormat::B8G8R8A8;
+  RefPtr<DrawTarget> referenceDt =
+    Factory::CreateDrawTarget(gfxPlatform::GetPlatform()->GetSoftwareBackend(),
+                              IntSize(1, 1),
+                              format);
+
+  // TODO: This may OOM crash if the content is complex enough
+  RefPtr<DrawEventRecorderMemory> recorder =
+    MakeAndAddRef<DrawEventRecorderMemory>(nullptr);
+  RefPtr<DrawTarget> dt =
+    Factory::CreateRecordingDrawTarget(recorder, referenceDt, surfaceSize);
+
+  // Perform the actual rendering
+  {
+    nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x),
+             nsPresContext::CSSPixelsToAppUnits(aRect.y),
+             nsPresContext::CSSPixelsToAppUnits(aRect.width),
+             nsPresContext::CSSPixelsToAppUnits(aRect.height));
+
+    RefPtr<gfxContext> thebes = gfxContext::CreateOrNull(dt);
+    thebes->SetMatrix(Matrix::Scaling(aScale, aScale));
+    nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
+    Unused << shell->RenderDocument(r, 0, aBackgroundColor, thebes);
+  }
+
+  ByteBuf recording = ByteBuf((uint8_t*)recorder->mOutputStream.mData,
+                              recorder->mOutputStream.mLength,
+                              recorder->mOutputStream.mCapacity);
+  recorder->mOutputStream.mData = nullptr;
+  recorder->mOutputStream.mLength = 0;
+  recorder->mOutputStream.mCapacity = 0;
+
+  return PaintFragment{
+    surfaceSize,
+    std::move(recording),
+    std::move(recorder->TakeDependentSurfaces()),
+  };
+}
+
+bool
+PaintFragment::IsEmpty() const
+{
+  return !mRecording.mData || mRecording.mLen == 0 || mSize == IntSize(0, 0);
+}
+
+PaintFragment::PaintFragment(IntSize aSize,
+                             ByteBuf&& aRecording,
+                             nsTHashtable<nsUint64HashKey>&& aDependencies)
+  : mSize(aSize)
+  , mRecording(std::move(aRecording))
+  , mDependencies(std::move(aDependencies))
+{
+}
+
+/* static */ void
+CrossProcessPaint::StartLocal(nsIDocShell* aRoot,
+                              const IntRect& aRect,
+                              float aScale,
+                              nscolor aBackgroundColor,
+                              dom::Promise* aPromise)
+{
+  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
+  aScale = std::max(aScale, kMinPaintScale);
+
+  CPP_LOG("Starting local paint. "
+          "[docshell=%p, "
+          "rect=(%d, %d) x (%d, %d), "
+          "scale=%f, "
+          "color=(%u, %u, %u, %u)]\n",
+    aRoot,
+    aRect.x, aRect.y, aRect.width, aRect.height,
+    aScale,
+    NS_GET_R(aBackgroundColor),
+    NS_GET_G(aBackgroundColor),
+    NS_GET_B(aBackgroundColor),
+    NS_GET_A(aBackgroundColor));
+
+  RefPtr<CrossProcessPaint> resolver = new CrossProcessPaint(aPromise,
+                                                             aScale,
+                                                             aBackgroundColor,
+                                                             dom::TabId(0));
+  resolver->ReceiveFragment(dom::TabId(0),
+                            PaintFragment::Record(aRoot,
+                                                  aRect,
+                                                  aScale,
+                                                  aBackgroundColor));
+}
+
+/* static */ void
+CrossProcessPaint::StartRemote(dom::TabId aRoot,
+                               const IntRect& aRect,
+                               float aScale,
+                               nscolor aBackgroundColor,
+                               dom::Promise* aPromise)
+{
+  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
+  aScale = std::max(aScale, kMinPaintScale);
+
+  CPP_LOG("Starting remote paint. "
+          "[tab=%llu, "
+          "rect=(%d, %d) x (%d, %d), "
+          "scale=%f, "
+          "color=(%u, %u, %u, %u)]\n",
+    (uint64_t)aRoot,
+    aRect.x, aRect.y, aRect.width, aRect.height,
+    aScale,
+    NS_GET_R(aBackgroundColor),
+    NS_GET_G(aBackgroundColor),
+    NS_GET_B(aBackgroundColor),
+    NS_GET_A(aBackgroundColor));
+
+  RefPtr<CrossProcessPaint> resolver = new CrossProcessPaint(aPromise,
+                                                             aScale,
+                                                             aBackgroundColor,
+                                                             aRoot);
+  resolver->QueueRootPaint(aRoot, aRect, aScale, aBackgroundColor);
+}
+
+CrossProcessPaint::CrossProcessPaint(dom::Promise* aPromise,
+                                     float aScale,
+                                     nscolor aBackgroundColor,
+                                     dom::TabId aRootId)
+    : mPromise{aPromise}
+    , mRootId{aRootId}
+    , mScale{aScale}
+    , mBackgroundColor{aBackgroundColor}
+    , mPendingFragments{1}
+{
+}
+
+CrossProcessPaint::~CrossProcessPaint()
+{
+}
+
+void
+CrossProcessPaint::ReceiveFragment(dom::TabId aId, PaintFragment&& aFragment)
+{
+  if (IsCleared()) {
+    CPP_LOG("Ignoring fragment from %llu.\n", (uint64_t)aId);
+    return;
+  }
+
+  MOZ_ASSERT(mPendingFragments > 0);
+  MOZ_ASSERT(!mReceivedFragments.GetValue(aId));
+  MOZ_ASSERT(!aFragment.IsEmpty());
+
+  // Double check our invariants to protect against a compromised content
+  // process
+  if (mPendingFragments == 0 ||
+      mReceivedFragments.GetValue(aId) ||
+      aFragment.IsEmpty()) {
+    CPP_LOG("Dropping invalid fragment from %llu.\n", (uint64_t)aId);
+    LostFragment(aId);
+    return;
+  }
+
+  CPP_LOG("Receiving fragment from %llu.\n", (uint64_t)aId);
+
+  // Queue paints for child tabs
+  for (auto iter = aFragment.mDependencies.Iter(); !iter.Done(); iter.Next()) {
+    auto dependency = iter.Get()->GetKey();
+    QueueSubPaint(dom::TabId(dependency));
+  }
+
+  mReceivedFragments.Put(aId, std::move(aFragment));
+  mPendingFragments -= 1;
+
+  // Resolve this paint if we have received all pending fragments
+  MaybeResolve();
+}
+
+void
+CrossProcessPaint::LostFragment(dom::TabId aId)
+{
+  if (IsCleared()) {
+    CPP_LOG("Ignoring lost fragment from %llu.\n", (uint64_t)aId);
+    return;
+  }
+
+  mPromise->MaybeReject(NS_ERROR_FAILURE);
+  Clear();
+}
+
+void
+CrossProcessPaint::QueueRootPaint(dom::TabId aId,
+                                  const IntRect& aRect,
+                                  float aScale,
+                                  nscolor aBackgroundColor)
+{
+  MOZ_ASSERT(!mReceivedFragments.GetValue(aId));
+  MOZ_ASSERT(mPendingFragments == 1);
+
+  CPP_LOG("Queueing root paint for %llu.\n", (uint64_t)aId);
+
+  dom::ContentProcessManager* cpm = dom::ContentProcessManager::GetSingleton();
+
+  dom::ContentParentId cpId = cpm->GetTabProcessId(aId);
+  RefPtr<dom::TabParent> tab = cpm->GetTabParentByProcessAndTabId(cpId, aId);
+  tab->RequestRootPaint(this, aRect, aScale, aBackgroundColor);
+
+  // This will always be the first paint, so the constructor will already have
+  // incremented one pending fragment
+}
+
+void
+CrossProcessPaint::QueueSubPaint(dom::TabId aId)
+{
+  MOZ_ASSERT(!mReceivedFragments.GetValue((uint64_t)aId));
+
+  CPP_LOG("Queueing sub paint for %llu.\n", (uint64_t)aId);
+
+  dom::ContentProcessManager* cpm = dom::ContentProcessManager::GetSingleton();
+
+  dom::ContentParentId cpId = cpm->GetTabProcessId(aId);
+  RefPtr<dom::TabParent> tab = cpm->GetTabParentByProcessAndTabId(cpId, aId);
+  tab->RequestSubPaint(this, mScale, mBackgroundColor);
+
+  mPendingFragments += 1;
+}
+
+void
+CrossProcessPaint::Clear()
+{
+  mPromise = nullptr;
+  mPendingFragments = 0;
+  mReceivedFragments.Clear();
+}
+
+bool
+CrossProcessPaint::IsCleared() const
+{
+  return !mPromise;
+}
+
+void
+CrossProcessPaint::MaybeResolve()
+{
+  // Don't do anything if we aren't ready, experienced an error, or already
+  // resolved this paint
+  if (IsCleared() || mPendingFragments > 0) {
+    CPP_LOG("Not ready to resolve yet, have %u fragments left.\n",
+            mPendingFragments);
+    return;
+  }
+
+  CPP_LOG("Starting to resolve fragments.\n");
+
+  // Resolve the paint fragments from the bottom up
+  ResolvedSurfaceMap resolved;
+  if (!ResolveInternal(mRootId, &resolved)) {
+    CPP_LOG("Couldn't resolve.\n");
+
+    mPromise->MaybeReject(NS_ERROR_FAILURE);
+    Clear();
+    return;
+  }
+
+  // Grab the result from the resolved table
+  RefPtr<SourceSurface> root = resolved.Get(mRootId);
+  CPP_LOG("Resolved all fragments.\n");
+
+  ErrorResult rv;
+  RefPtr<dom::ImageBitmap> bitmap =
+    dom::ImageBitmap::CreateFromSourceSurface(mPromise->GetParentObject(),
+                                              root,
+                                              rv);
+
+  if (!rv.Failed()) {
+    CPP_LOG("Success, fulfilling promise.\n");
+    mPromise->MaybeResolve(bitmap);
+  } else {
+    CPP_LOG("Couldn't create ImageBitmap for SourceSurface.\n");
+    mPromise->MaybeReject(rv);
+  }
+  Clear();
+}
+
+bool
+CrossProcessPaint::ResolveInternal(dom::TabId aId,
+                                   ResolvedSurfaceMap* aResolved)
+{
+  // We should not have resolved this paint already
+  MOZ_ASSERT(!aResolved->GetWeak(aId));
+
+  CPP_LOG("Resolving fragment %llu.\n", (uint64_t)aId);
+
+  Maybe<PaintFragment> fragment = mReceivedFragments.GetAndRemove(aId);
+
+  // Rasterize all the dependencies first so that we can resolve this fragment
+  for (auto iter = fragment->mDependencies.Iter(); !iter.Done(); iter.Next()) {
+    auto dependency = iter.Get()->GetKey();
+    if (!ResolveInternal(dom::TabId(dependency), aResolved)) {
+      return false;
+    }
+  }
+
+  // Create the destination draw target
+  RefPtr<DrawTarget> drawTarget =
+    gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(fragment->mSize,
+                                                                 SurfaceFormat::B8G8R8A8);
+  if (!drawTarget || !drawTarget->IsValid()) {
+    CPP_LOG("Couldn't create (%d x %d) surface for fragment %llu.\n",
+      fragment->mSize.width,
+      fragment->mSize.height,
+      (uint64_t)aId);
+    return false;
+  }
+
+  // Translate the recording using our child tabs
+  {
+    InlineTranslator translator(drawTarget, nullptr);
+    translator.SetExternalSurfaces(aResolved);
+    if (!translator.TranslateRecording((char*)fragment->mRecording.mData,
+                                       fragment->mRecording.mLen)) {
+      CPP_LOG("Couldn't translate recording for fragment %llu.\n",
+        (uint64_t)aId);
+      return false;
+    }
+  }
+
+  RefPtr<SourceSurface> snapshot = drawTarget->Snapshot();
+  if (!snapshot) {
+    CPP_LOG("Couldn't get snapshot for fragment %llu.\n",
+      (uint64_t)aId);
+    return false;
+  }
+
+  // We are done with the resolved images of our dependencies, let's remove
+  // them
+  for (auto iter = fragment->mDependencies.Iter(); !iter.Done(); iter.Next()) {
+    auto dependency = iter.Get()->GetKey();
+    aResolved->Remove(dependency);
+  }
+
+  aResolved->Put(aId, snapshot);
+  return true;
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/CrossProcessPaint.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _include_mozilla_gfx_ipc_CrossProcessPaint_h_
+#define _include_mozilla_gfx_ipc_CrossProcessPaint_h_
+
+#include "nsISupportsImpl.h"
+
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/ipc/ByteBuf.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsRefPtrHashtable.h"
+#include "nsTHashtable.h"
+
+namespace IPC {
+template<typename T> struct ParamTraits;
+} // namespace IPC
+
+namespace mozilla {
+namespace gfx {
+
+class CrossProcessPaint;
+
+/**
+ * A fragment of a paint of a cross process document tree.
+ */
+class PaintFragment
+{
+public:
+  /// Initializes an empty PaintFragment
+  PaintFragment() = default;
+
+  /**
+   * Creates a paint fragment by recording the draw commands and dependent tabs
+   * for an nsIDocShell.
+   *
+   * @param aDocShell The document shell to record.
+   * @param aRect The rectangle relative to the viewport to use.
+   * @param aScale The coordinate scale to use. The size of the resolved
+   *   surface will be `aRect.Size() * aScale`, with aScale clamped to
+   *   at least kMinPaintScale.
+   * @param aBackgroundColor The background color to use.
+   *
+   * @return A paint fragment. The paint fragment may be `empty` if rendering
+   *         was unable to be accomplished for some reason.
+   */
+  static PaintFragment Record(nsIDocShell* aDocShell,
+                              const IntRect& aRect,
+                              float aScale,
+                              nscolor aBackgroundColor);
+
+  /// Returns whether this paint fragment contains a valid recording.
+  bool IsEmpty() const;
+
+  PaintFragment(PaintFragment&&) = default;
+  PaintFragment& operator=(PaintFragment&&) = default;
+
+protected:
+  friend struct IPC::ParamTraits<PaintFragment>;
+  friend CrossProcessPaint;
+
+  typedef mozilla::ipc::ByteBuf ByteBuf;
+
+  PaintFragment(IntSize, ByteBuf&&, nsTHashtable<nsUint64HashKey>&&);
+
+  IntSize mSize;
+  ByteBuf mRecording;
+  nsTHashtable<nsUint64HashKey> mDependencies;
+};
+
+/**
+ * An object for painting a cross process document tree.
+ */
+class CrossProcessPaint
+{
+  NS_INLINE_DECL_REFCOUNTING(CrossProcessPaint);
+
+public:
+  /**
+   * Begin an asynchronous paint of a cross process document tree starting at
+   * a local document shell. The local document will be painted, then async
+   * paints will be queued for remote subframes. Once all subframes have been
+   * recorded, the final image will be resolved, and the promise will be
+   * resolved with a dom::ImageBitmap.
+   *
+   * @param aDocShell The document shell to paint.
+   * @param aRect The rectangle relative to the viewport to use.
+   * @param aScale The coordinate scale to use. The size of the resolved
+   *   surface will be `aRect.Size() * aScale`, with aScale clamped to
+   *   at least kMinPaintScale. See the implementation for the current
+   *   minimum value.
+   * @param aBackgroundColor The background color to use.
+   * @param aPromise The promise to resolve with a dom::ImageBitmap.
+   */
+  static void StartLocal(nsIDocShell* aRoot,
+                         const IntRect& aRect,
+                         float aScale,
+                         nscolor aBackgroundColor,
+                         dom::Promise* aPromise);
+
+  /**
+   * Begin an asynchronous paint of a cross process document tree starting at
+   * a remote tab. An async paint for the remote tab will be queued, then async
+   * paints will be recursively queued for remote subframes. Once all subframes
+   * have been recorded, the final image will be resolved, and the promise will
+   * be resolved with a dom::ImageBitmap.
+   *
+   * @param aDocShell The document shell to paint.
+   * @param aRect The rectangle relative to the viewport to use.
+   * @param aScale The coordinate scale to use. The size of the resolved
+   *   surface will be `aRect.Size() * aScale`, with aScale clamped to
+   *   at least kMinPaintScale. See the implementation for the current
+   *   minimum value.
+   * @param aBackgroundColor The background color to use.
+   * @param aPromise The promise to resolve with a dom::ImageBitmap.
+   */
+  static void StartRemote(dom::TabId aRoot,
+                          const IntRect& aRect,
+                          float aScale,
+                          nscolor aBackgroundColor,
+                          dom::Promise* aPromise);
+
+  void ReceiveFragment(dom::TabId aId, PaintFragment&& aFragment);
+  void LostFragment(dom::TabId aId);
+private:
+  typedef nsRefPtrHashtable<nsUint64HashKey, SourceSurface> ResolvedSurfaceMap;
+  typedef nsDataHashtable<nsUint64HashKey, PaintFragment> ReceivedFragmentMap;
+
+  CrossProcessPaint(dom::Promise* aPromise,
+                    float aScale,
+                    nscolor aBackgroundColor,
+                    dom::TabId aRootId);
+  ~CrossProcessPaint();
+
+  void QueueRootPaint(dom::TabId aId,
+                      const IntRect& aRect,
+                      float aScale,
+                      nscolor aBackgroundColor);
+  void QueueSubPaint(dom::TabId aId);
+
+  /// Clear the state of this paint so that it cannot be resolved or receive
+  /// any paint fragments.
+  void Clear();
+
+  /// Returns if this paint has been cleared.
+  bool IsCleared() const;
+
+  /// Resolves the paint fragments if we have none pending and resolves the
+  /// promise.
+  void MaybeResolve();
+  bool ResolveInternal(dom::TabId aId,
+                       ResolvedSurfaceMap* aResolved);
+
+  RefPtr<dom::Promise> mPromise;
+  dom::TabId mRootId;
+  float mScale;
+  nscolor mBackgroundColor;
+  uint32_t mPendingFragments;
+  ReceivedFragmentMap mReceivedFragments;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // _include_mozilla_gfx_ipc_CrossProcessPaint_h_
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -15,16 +15,17 @@
 #include "chrome/common/ipc_message_utils.h"
 #include "gfxFeature.h"
 #include "gfxFallback.h"
 #include "gfxPoint.h"
 #include "gfxRect.h"
 #include "gfxTelemetry.h"
 #include "gfxTypes.h"
 #include "ipc/IPCMessageUtils.h"
+#include "mozilla/gfx/CrossProcessPaint.h"
 #include "mozilla/gfx/Matrix.h"
 #include "nsRect.h"
 #include "nsRegion.h"
 #include "mozilla/Array.h"
 
 #include <stdint.h>
 
 #ifdef _MSC_VER
@@ -1274,11 +1275,28 @@ struct ParamTraits<mozilla::Array<T, Len
       if (!ReadParam<T>(aMsg, aIter, &aResult->operator[](i))) {
         return false;
       }
     }
     return true;
   }
 };
 
+template<>
+struct ParamTraits<mozilla::gfx::PaintFragment>
+{
+  typedef mozilla::gfx::PaintFragment paramType;
+  static void Write(Message* aMsg, paramType& aParam) {
+    WriteParam(aMsg, aParam.mSize);
+    WriteParam(aMsg, aParam.mRecording);
+    WriteParam(aMsg, aParam.mDependencies);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
+    return ReadParam(aMsg, aIter, &aResult->mSize) &&
+           ReadParam(aMsg, aIter, &aResult->mRecording) &&
+           ReadParam(aMsg, aIter, &aResult->mDependencies);
+  }
+};
+
 } /* namespace IPC */
 
 #endif /* __GFXMESSAGEUTILS_H__ */
--- a/gfx/ipc/moz.build
+++ b/gfx/ipc/moz.build
@@ -8,16 +8,17 @@ with Files('**'):
     BUG_COMPONENT = ('Core', 'Graphics: Layers')
 
 EXPORTS.mozilla += [
     'D3DMessageUtils.h',
     'GfxMessageUtils.h'
 ]
 
 EXPORTS.mozilla.gfx += [
+    'CrossProcessPaint.h',
     'GPUChild.h',
     'GPUParent.h',
     'GPUProcessHost.h',
     'GPUProcessImpl.h',
     'GPUProcessListener.h',
     'GPUProcessManager.h',
     'SharedDIB.h',
     'VsyncBridgeChild.h',
@@ -44,16 +45,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind
     UNIFIED_SOURCES += [
         'SharedDIBSurface.cpp',
         'SharedDIBWin.cpp',
     ]
 
 UNIFIED_SOURCES += [
     'CompositorSession.cpp',
     'CompositorWidgetVsyncObserver.cpp',
+    'CrossProcessPaint.cpp',
     'D3DMessageUtils.cpp',
     'GPUChild.cpp',
     'GPUProcessHost.cpp',
     'GPUProcessImpl.cpp',
     'GPUProcessManager.cpp',
     'InProcessCompositorSession.cpp',
     'RemoteCompositorSession.cpp',
     'SharedDIB.cpp',
--- a/ipc/glue/ByteBuf.h
+++ b/ipc/glue/ByteBuf.h
@@ -53,16 +53,24 @@ public:
     , mLen(aFrom.mLen)
     , mCapacity(aFrom.mCapacity)
   {
     aFrom.mData = nullptr;
     aFrom.mLen = 0;
     aFrom.mCapacity = 0;
   }
 
+  ByteBuf& operator=(ByteBuf&& aFrom)
+  {
+    std::swap(mData, aFrom.mData);
+    std::swap(mLen, aFrom.mLen);
+    std::swap(mCapacity, aFrom.mCapacity);
+    return *this;
+  }
+
   ~ByteBuf()
   {
     free(mData);
   }
 
   uint8_t* mData;
   size_t mLen;
   size_t mCapacity;
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -25,21 +25,23 @@
 #include "mozilla/TypeTraits.h"
 #include "mozilla/IntegerTypeTraits.h"
 
 #include <limits>
 #include <stdint.h>
 #include <type_traits>
 
 #include "nsExceptionHandler.h"
+#include "nsHashKeys.h"
 #include "nsID.h"
 #include "nsIWidget.h"
 #include "nsMemory.h"
 #include "nsString.h"
 #include "nsTArray.h"
+#include "nsTHashtable.h"
 #include "js/StructuredClone.h"
 #include "nsCSSPropertyID.h"
 
 #ifdef _MSC_VER
 #pragma warning( disable : 4800 )
 #endif
 
 #if !defined(OS_POSIX)
@@ -532,16 +534,49 @@ struct ParamTraits<nsDependentCSubstring
 template<>
 struct ParamTraits<nsAutoString> : ParamTraits<nsString>
 {
   typedef nsAutoString paramType;
 };
 
 #endif  // MOZILLA_INTERNAL_API
 
+template <>
+struct ParamTraits<nsTHashtable<nsUint64HashKey>>
+{
+  typedef nsTHashtable<nsUint64HashKey> paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    uint32_t count = aParam.Count();
+    WriteParam(aMsg, count);
+    for (auto iter = aParam.ConstIter(); !iter.Done(); iter.Next()) {
+      WriteParam(aMsg, iter.Get()->GetKey());
+    }
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    uint32_t count;
+    if (!ReadParam(aMsg, aIter, &count)) {
+      return false;
+    }
+    paramType table(count);
+    for (uint32_t i = 0; i < count; ++i) {
+      uint64_t key;
+      if (!ReadParam(aMsg, aIter, &key)) {
+        return false;
+      }
+      table.PutEntry(key);
+    }
+    *aResult = std::move(table);
+    return true;
+  }
+};
+
 // Pickle::ReadBytes and ::WriteBytes take the length in ints, so we must
 // ensure there is no overflow. This returns |false| if it would overflow.
 // Otherwise, it returns |true| and places the byte length in |aByteLength|.
 bool ByteLengthIsValid(uint32_t aNumElements, size_t aElementSize, int* aByteLength);
 
 // Note: IPDL will sometimes codegen specialized implementations of
 // nsTArray serialization and deserialization code in
 // implementSpecialArrayPickling(). This is needed when ParamTraits<E>
--- a/ipc/ipdl/ipdl/ast.py
+++ b/ipc/ipdl/ipdl/ast.py
@@ -172,38 +172,43 @@ class Include(Node):
         Node.__init__(self, loc)
         suffix = 'ipdl'
         if type == 'header':
             suffix += 'h'
         self.file = "%s.%s" % (name, suffix)
 
 
 class UsingStmt(Node):
-    def __init__(self, loc, cxxTypeSpec, cxxHeader=None, kind=None, refcounted=False):
+    def __init__(self, loc, cxxTypeSpec, cxxHeader=None, kind=None,
+                 refcounted=False, moveonly=False):
         Node.__init__(self, loc)
         assert not isinstance(cxxTypeSpec, str)
         assert cxxHeader is None or isinstance(cxxHeader, str)
         assert kind is None or kind == 'class' or kind == 'struct'
         self.type = cxxTypeSpec
         self.header = cxxHeader
         self.kind = kind
         self.refcounted = refcounted
+        self.moveonly = moveonly
 
     def canBeForwardDeclared(self):
         return self.isClass() or self.isStruct()
 
     def isClass(self):
         return self.kind == 'class'
 
     def isStruct(self):
         return self.kind == 'struct'
 
     def isRefcounted(self):
         return self.refcounted
 
+    def isMoveonly(self):
+        return self.moveonly
+
 # "singletons"
 
 
 class PrettyPrinted:
     @classmethod
     def __hash__(cls): return hash(cls.pretty)
 
     @classmethod
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -593,31 +593,35 @@ def _cxxConstRefType(ipdltype, side):
         t.ref = 1
         return t
     if ipdltype.isIPDL() and ipdltype.isArray():
         # Keep same constness as inner type.
         inner = _cxxConstRefType(ipdltype.basetype, side)
         t.const = inner.const or not inner.ref
         t.ref = 1
         return t
+    if ipdltype.isCxx() and ipdltype.isMoveonly():
+        t.ref = 1
+        return t
     if ipdltype.isCxx() and ipdltype.isRefcounted():
         # Use T* instead of const RefPtr<T>&
         t = t.T
         t.ptr = 1
         return t
     t.const = 1
     t.ref = 1
     return t
 
 
 def _cxxTypeNeedsMove(ipdltype):
-    return ipdltype.isIPDL() and (ipdltype.isArray() or
-                                  ipdltype.isShmem() or
-                                  ipdltype.isByteBuf() or
-                                  ipdltype.isEndpoint())
+    return ((ipdltype.isIPDL() and (ipdltype.isArray() or
+                                    ipdltype.isShmem() or
+                                    ipdltype.isByteBuf() or
+                                    ipdltype.isEndpoint())) or
+            (ipdltype.isCxx() and ipdltype.isMoveonly()))
 
 
 def _cxxTypeCanMove(ipdltype):
     return not (ipdltype.isIPDL() and ipdltype.isActor())
 
 
 def _cxxMoveRefType(ipdltype, side):
     t = _cxxBareType(ipdltype, side)
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -127,16 +127,17 @@ reserved = set((
     'namespace',
     'nested',
     'nullable',
     'or',
     'parent',
     'prio',
     'protocol',
     'refcounted',
+    'moveonly',
     'returns',
     'struct',
     'sync',
     'union',
     'upto',
     'using',
     'verify'))
 tokens = [
@@ -283,23 +284,30 @@ def p_UsingKind(p):
 
 
 def p_MaybeRefcounted(p):
     """MaybeRefcounted : REFCOUNTED
                        |"""
     p[0] = 2 == len(p)
 
 
+def p_MaybeMoveOnly(p):
+    """MaybeMoveOnly : MOVEONLY
+                       |"""
+    p[0] = 2 == len(p)
+
+
 def p_UsingStmt(p):
-    """UsingStmt : USING MaybeRefcounted UsingKind CxxType FROM STRING"""
+    """UsingStmt : USING MaybeRefcounted MaybeMoveOnly UsingKind CxxType FROM STRING"""
     p[0] = UsingStmt(locFromTok(p, 1),
                      refcounted=p[2],
-                     kind=p[3],
-                     cxxTypeSpec=p[4],
-                     cxxHeader=p[6])
+                     moveonly=p[3],
+                     kind=p[4],
+                     cxxTypeSpec=p[5],
+                     cxxHeader=p[7])
 
 # --------------------
 # Namespaced stuff
 
 
 def p_NamespacedStuff(p):
     """NamespacedStuff : NamespacedStuff NamespaceThing
                        | NamespaceThing"""
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -137,31 +137,35 @@ class VoidType(Type):
 
 
 VOID = VoidType()
 
 # --------------------
 
 
 class ImportedCxxType(Type):
-    def __init__(self, qname, refcounted):
+    def __init__(self, qname, refcounted, moveonly):
         assert isinstance(qname, QualifiedId)
         self.loc = qname.loc
         self.qname = qname
         self.refcounted = refcounted
+        self.moveonly = moveonly
 
     def isCxx(self):
         return True
 
     def isAtom(self):
         return True
 
     def isRefcounted(self):
         return self.refcounted
 
+    def isMoveonly(self):
+        return self.moveonly
+
     def name(self):
         return self.qname.baseid
 
     def fullname(self):
         return str(self.qname)
 
 # --------------------
 
@@ -843,22 +847,25 @@ class GatherDecls(TcheckVisitor):
             fullname = None
         if fullname == 'mozilla::ipc::Shmem':
             ipdltype = ShmemType(using.type.spec)
         elif fullname == 'mozilla::ipc::ByteBuf':
             ipdltype = ByteBufType(using.type.spec)
         elif fullname == 'mozilla::ipc::FileDescriptor':
             ipdltype = FDType(using.type.spec)
         else:
-            ipdltype = ImportedCxxType(using.type.spec, using.isRefcounted())
+            ipdltype = ImportedCxxType(using.type.spec, using.isRefcounted(), using.isMoveonly())
             existingType = self.symtab.lookup(ipdltype.fullname())
             if existingType and existingType.fullname == ipdltype.fullname():
                 if ipdltype.isRefcounted() != existingType.type.isRefcounted():
                     self.error(using.loc, "inconsistent refcounted status of type `%s`",
                                str(using.type))
+                if ipdltype.isMoveonly() != existingType.type.isMoveonly():
+                    self.error(using.loc, "inconsistent moveonly status of type `%s`",
+                               str(using.type))
                 using.decl = existingType
                 return
         using.decl = self.declare(
             loc=using.loc,
             type=ipdltype,
             shortname=using.type.basename(),
             fullname=fullname)
 
--- a/layout/base/crashtests/645572-1.html
+++ b/layout/base/crashtests/645572-1.html
@@ -6,18 +6,18 @@ function start(){
         tmp.addEventListener("load", start_dataiframe9);
         document.documentElement.appendChild(tmp);
 }function start_dataiframe9(){
         o185=document.getElementById('ifr32247').contentDocument.documentElement;
         tmp=document.createElement('iframe')
         o196=document.getElementById('ifr32247').contentDocument.createElementNS('http:2000svg','altGlyph');
         o230=o185.cloneNode(true);
         tmp.id='ifr42257';
+        tmp.addEventListener("load", start_dataiframe11);
         o230.ownerDocument.documentElement.appendChild(tmp); 
-        start_dataiframe11();
         //window.setTimeout('start_dataiframe11()',100);
 }function start_dataiframe11(){
         o232=o230.ownerDocument.getElementById('ifr42257').contentDocument.documentElement;
         o234=o196;
         tmp=o234.ownerDocument.createElement('iframe');
         tmp.srcdoc="<q id='element2'><q id='element3'><q id='element4'><dd style id='element6'>";
         tmp.id='ifr22371';
         tmp.addEventListener("load", start_dataiframe12);
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -362,17 +362,26 @@ nsSubDocumentFrame::BuildDisplayList(nsD
   // subdocuments no matter what, to determine which parts are transparent for
   // hit-testing or event regions.
   bool needToDescend = aBuilder->GetDescendIntoSubdocuments();
   if (!mInnerView || !needToDescend) {
     return;
   }
 
   if (rfp) {
-    rfp->BuildDisplayList(aBuilder, this, aLists);
+    // We're the subdoc for <browser remote="true"> and it has
+    // painted content.  Display its shadow layer tree.
+    DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+
+    nsPoint offset = aBuilder->ToReferenceFrame(this);
+    nsRect bounds = this->EnsureInnerView()->GetBounds() + offset;
+    clipState.ClipContentDescendants(bounds);
+
+    aLists.Content()->AppendToTop(
+      MakeDisplayItem<nsDisplayRemote>(aBuilder, this));
     return;
   }
 
   nsCOMPtr<nsIPresShell> presShell =
     GetSubdocumentPresShellForPainting(
       aBuilder->IsIgnoringPaintSuppression() ? IGNORE_PAINT_SUPPRESSION : 0);
 
   if (!presShell) {
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -160,23 +160,18 @@ already_AddRefed<Layer>
 RenderFrameParent::BuildLayer(nsDisplayListBuilder* aBuilder,
                               nsIFrame* aFrame,
                               LayerManager* aManager,
                               nsDisplayItem* aItem,
                               const ContainerLayerParameters& aContainerParameters)
 {
   MOZ_ASSERT(aFrame,
              "makes no sense to have a shadow tree without a frame");
-  MOZ_ASSERT(!mContainer ||
-             IsTempLayerManager(aManager) ||
-             mContainer->Manager() == aManager,
-             "retaining manager changed out from under us ... HELP!");
 
-  if (IsTempLayerManager(aManager) ||
-      (mContainer && mContainer->Manager() != aManager)) {
+  if (IsTempLayerManager(aManager)) {
     // This can happen if aManager is a "temporary" manager, or if the
     // widget's layer manager changed out from under us.  We need to
     // FIXME handle the former case somehow, probably with an API to
     // draw a manager's subtree.  The latter is bad bad bad, but the the
     // MOZ_ASSERT() above will flag it.  Returning nullptr here will just
     // cause the shadow subtree not to be rendered.
     if (!aContainerParameters.mForEventsAndPluginsOnly) {
       NS_WARNING("Remote iframe not rendered");
@@ -275,33 +270,16 @@ RenderFrameParent::TriggerRepaint()
     // to /dev/null.
     return;
   }
 
   docFrame->InvalidateLayer(DisplayItemType::TYPE_REMOTE);
 }
 
 void
-RenderFrameParent::BuildDisplayList(nsDisplayListBuilder* aBuilder,
-                                    nsSubDocumentFrame* aFrame,
-                                    const nsDisplayListSet& aLists)
-{
-  // We're the subdoc for <browser remote="true"> and it has
-  // painted content.  Display its shadow layer tree.
-  DisplayListClipState::AutoSaveRestore clipState(aBuilder);
-
-  nsPoint offset = aBuilder->ToReferenceFrame(aFrame);
-  nsRect bounds = aFrame->EnsureInnerView()->GetBounds() + offset;
-  clipState.ClipContentDescendants(bounds);
-
-  aLists.Content()->AppendToTop(
-    MakeDisplayItem<nsDisplayRemote>(aBuilder, aFrame));
-}
-
-void
 RenderFrameParent::GetTextureFactoryIdentifier(TextureFactoryIdentifier* aTextureFactoryIdentifier)
 {
   RefPtr<LayerManager> lm = mFrameLoader ? GetLayerManager(mFrameLoader) : nullptr;
   // Perhaps the document containing this frame currently has no presentation?
   if (lm) {
     *aTextureFactoryIdentifier = lm->GetTextureFactoryIdentifier();
   } else {
     *aTextureFactoryIdentifier = TextureFactoryIdentifier();
@@ -341,27 +319,47 @@ RenderFrameParent::EnsureLayersConnected
 }
 
 } // namespace layout
 } // namespace mozilla
 
 nsDisplayRemote::nsDisplayRemote(nsDisplayListBuilder* aBuilder,
                                  nsSubDocumentFrame* aFrame)
   : nsDisplayItem(aBuilder, aFrame)
+  , mTabId{0}
   , mEventRegionsOverride(EventRegionsOverride::NoOverride)
 {
   bool frameIsPointerEventsNone =
     aFrame->StyleUI()->GetEffectivePointerEvents(aFrame) ==
       NS_STYLE_POINTER_EVENTS_NONE;
   if (aBuilder->IsInsidePointerEventsNoneDoc() || frameIsPointerEventsNone) {
     mEventRegionsOverride |= EventRegionsOverride::ForceEmptyHitRegion;
   }
   if (nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(aFrame->PresShell())) {
     mEventRegionsOverride |= EventRegionsOverride::ForceDispatchToContent;
   }
+
+  nsFrameLoader* frameLoader = GetRenderFrameParent()->FrameLoader();
+  if (frameLoader) {
+    TabParent* browser = TabParent::GetFrom(frameLoader);
+    if (browser) {
+      mTabId = browser->GetTabId();
+    }
+  }
+}
+
+mozilla::LayerState
+nsDisplayRemote::GetLayerState(nsDisplayListBuilder* aBuilder,
+                               LayerManager* aManager,
+                               const ContainerLayerParameters& aParameters)
+{
+  if (mozilla::layout::IsTempLayerManager(aManager)) {
+    return mozilla::LAYER_NONE;
+  }
+  return mozilla::LAYER_ACTIVE_FORCE;
 }
 
 bool
 nsDisplayRemote::HasDeletedFrame() const
 {
   // RenderFrameParent might change without invalidating nsSubDocumentFrame.
   return !GetRenderFrameParent() || nsDisplayItem::HasDeletedFrame();
 }
@@ -378,16 +376,31 @@ nsDisplayRemote::BuildLayer(nsDisplayLis
                                        this, aContainerParameters);
 
   if (layer && layer->AsRefLayer()) {
     layer->AsRefLayer()->SetEventRegionsOverride(mEventRegionsOverride);
   }
   return layer.forget();
 }
 
+void
+nsDisplayRemote::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx)
+{
+  DrawTarget* target = aCtx->GetDrawTarget();
+  if (!target->IsRecording() || mTabId == 0) {
+    NS_WARNING("Remote iframe not rendered");
+    return;
+  }
+
+  int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+  Rect destRect =
+    mozilla::NSRectToSnappedRect(GetContentRect(), appUnitsPerDevPixel, *target);
+  target->DrawDependentSurface(mTabId, destRect);
+}
+
 bool
 nsDisplayRemote::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                          mozilla::wr::IpcResourceUpdateQueue& aResources,
                                          const StackingContextHelper& aSc,
                                          mozilla::layers::WebRenderLayerManager* aManager,
                                          nsDisplayListBuilder* aDisplayListBuilder)
 {
   mOffset = mozilla::layout::GetContentRectLayerOffset(mFrame, aDisplayListBuilder);
--- a/layout/ipc/RenderFrameParent.h
+++ b/layout/ipc/RenderFrameParent.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layout_RenderFrameParent_h
 #define mozilla_layout_RenderFrameParent_h
 
 #include "mozilla/Attributes.h"
 #include <map>
 
+#include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/layers/APZUtils.h"
 #include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layout/PRenderFrameParent.h"
 #include "nsDisplayList.h"
 
 class nsFrameLoader;
 class nsSubDocumentFrame;
@@ -59,19 +60,16 @@ public:
    */
   explicit RenderFrameParent(nsFrameLoader* aFrameLoader);
   virtual ~RenderFrameParent();
 
   bool Init(nsFrameLoader* aFrameLoader);
   bool IsInitted();
   void Destroy();
 
-  void BuildDisplayList(nsDisplayListBuilder* aBuilder,
-                        nsSubDocumentFrame* aFrame,
-                        const nsDisplayListSet& aLists);
 
   already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame,
                                      LayerManager* aManager,
                                      nsDisplayItem* aItem,
                                      const ContainerLayerParameters& aContainerParameters);
 
   void OwnerContentChanged(nsIContent* aContent);
@@ -85,16 +83,21 @@ public:
   inline CompositorOptions GetCompositorOptions() const { return mCompositorOptions; }
 
   void TakeFocusForClickFromTap();
 
   void EnsureLayersConnected(CompositorOptions* aCompositorOptions);
 
   LayerManager* AttachLayerManager();
 
+  nsFrameLoader* FrameLoader() const
+  {
+    return mFrameLoader;
+  }
+
 protected:
   void ActorDestroy(ActorDestroyReason why) override;
 
   virtual mozilla::ipc::IPCResult RecvNotifyCompositorTransaction() override;
 
 private:
   void TriggerRepaint();
   void DispatchEventForPanZoomController(const InputEvent& aEvent);
@@ -108,17 +111,16 @@ private:
   // compositor and so this flag is false.
   bool mLayersConnected;
   // The compositor options for this layers id. This is only meaningful if
   // the compositor actually knows about this layers id (i.e. when mLayersConnected
   // is true).
   CompositorOptions mCompositorOptions;
 
   RefPtr<nsFrameLoader> mFrameLoader;
-  RefPtr<ContainerLayer> mContainer;
   RefPtr<LayerManager> mLayerManager;
 
   // True after Destroy() has been called, which is triggered
   // originally by nsFrameLoader::Destroy().  After this point, we can
   // no longer safely ask the frame loader to find its nearest layer
   // manager, because it may have been disconnected from the DOM.
   // It's still OK to *tell* the frame loader that we've painted after
   // it's destroyed; it'll just ignore us, and we won't be able to
@@ -151,37 +153,37 @@ class nsDisplayRemote final : public nsD
 public:
   nsDisplayRemote(nsDisplayListBuilder* aBuilder,
                   nsSubDocumentFrame* aFrame);
 
   bool HasDeletedFrame() const override;
 
   LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                            LayerManager* aManager,
-                           const ContainerLayerParameters& aParameters) override
-  {
-    return mozilla::LAYER_ACTIVE_FORCE;
-  }
+                           const ContainerLayerParameters& aParameters) override;
 
   already_AddRefed<Layer>
   BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager,
              const ContainerLayerParameters& aContainerParameters) override;
 
+  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
+
   bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                mozilla::wr::IpcResourceUpdateQueue& aResources,
                                const StackingContextHelper& aSc,
                                mozilla::layers::WebRenderLayerManager* aManager,
                                nsDisplayListBuilder* aDisplayListBuilder) override;
   bool UpdateScrollData(mozilla::layers::WebRenderScrollData* aData,
                         mozilla::layers::WebRenderLayerScrollData* aLayerData) override;
 
   NS_DISPLAY_DECL_NAME("Remote", TYPE_REMOTE)
 
 private:
   mozilla::layers::LayersId GetRemoteLayersId() const;
   RenderFrameParent* GetRenderFrameParent() const;
 
+  mozilla::dom::TabId mTabId;
   mozilla::LayoutDeviceIntPoint mOffset;
   mozilla::layers::EventRegionsOverride mEventRegionsOverride;
 };
 
 
 #endif  // mozilla_layout_RenderFrameParent_h
--- a/testing/web-platform/meta/encrypted-media/encrypted-media-default-feature-policy.https.sub.html.ini
+++ b/testing/web-platform/meta/encrypted-media/encrypted-media-default-feature-policy.https.sub.html.ini
@@ -1,7 +1,3 @@
 [encrypted-media-default-feature-policy.https.sub.html]
-  expected: TIMEOUT
-  [Default "encrypted-media" feature policy ["self"\] allows same-origin iframes.]
-    expected: TIMEOUT
-
   [Default "encrypted-media" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
--- a/testing/web-platform/meta/mediacapture-streams/MediaStream-default-feature-policy.https.html.ini
+++ b/testing/web-platform/meta/mediacapture-streams/MediaStream-default-feature-policy.https.html.ini
@@ -1,68 +1,39 @@
 [MediaStream-default-feature-policy.https.sub.html]
-  expected: TIMEOUT
-  [Default "microphone" feature policy ["self"\] allows the top-level document.]
-    expected: FAIL
-
-  [Default "microphone" feature policy ["self"\] allows same-origin iframes.]
-    expected: TIMEOUT
-
   [Default "microphone" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
   [Feature policy "microphone" can be enabled in cross-origin iframes using "allow" attribute.]
     expected: FAIL
 
-  [Default "camera" feature policy ["self"\] allows the top-level document.]
-    expected: FAIL
-
-  [Default "camera" feature policy ["self"\] allows same-origin iframes.]
-    expected: TIMEOUT
-
   [Default "camera" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
   [Feature policy "camera" can be enabled in cross-origin iframes using "allow" attribute.]
     expected: FAIL
 
-  [Default "camera; microphone" feature policy ["self"\] allows the top-level document.]
-    expected: FAIL
-
-  [Default "camera; microphone" feature policy ["self"\] allows same-origin iframes.]
-    expected: TIMEOUT
-
   [Default "camera; microphone" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
   [Feature policy "camera; microphone" can be enabled in cross-origin iframes using "allow" attribute.]
     expected: FAIL
 
 
 [MediaStream-default-feature-policy.https.html]
-  expected: TIMEOUT
-  [Default "microphone" feature policy ["self"\] allows same-origin iframes.]
-    expected: TIMEOUT
-
   [Default "microphone" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
   [Feature policy "microphone" can be enabled in cross-origin iframes using "allow" attribute.]
     expected: FAIL
 
-  [Default "camera" feature policy ["self"\] allows same-origin iframes.]
-    expected: TIMEOUT
-
   [Default "camera" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
   [Feature policy "camera" can be enabled in cross-origin iframes using "allow" attribute.]
     expected: FAIL
 
-  [Default "camera; microphone" feature policy ["self"\] allows same-origin iframes.]
-    expected: TIMEOUT
-
   [Default "camera; microphone" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
   [Feature policy "camera; microphone" can be enabled in cross-origin iframes using "allow" attribute.]
     expected: FAIL
 
--- a/testing/web-platform/meta/xhr/xmlhttprequest-sync-default-feature-policy.sub.html.ini
+++ b/testing/web-platform/meta/xhr/xmlhttprequest-sync-default-feature-policy.sub.html.ini
@@ -1,8 +1,4 @@
 [xmlhttprequest-sync-default-feature-policy.sub.html]
-  expected: TIMEOUT
-  [Default "sync-xhr" feature policy ["*"\] allows same-origin iframes.]
-    expected: TIMEOUT
-
   [Feature policy "sync-xhr" can be disabled in cross-origin iframes using "allow" attribute.]
     expected: FAIL
 
--- a/toolkit/components/url-classifier/tests/mochitest/chrome.ini
+++ b/toolkit/components/url-classifier/tests/mochitest/chrome.ini
@@ -64,8 +64,9 @@ skip-if = verify
 [test_classifier_changetablepref_bug1395411.html]
 [test_reporturl.html]
 skip-if = verify
 [test_trackingprotection_bug1312515.html]
 [test_advisory_link.html]
 [test_threathit_report.html]
 skip-if = verify
 [test_fastblock_bug1477046.html]
+skip-if = (os == 'win' && os_version == "6.1") || (os == 'mac') # Bug 1495110
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1899,16 +1899,34 @@
             }
 
             this.frameLoader.print(aOuterWindowID, aPrintSettings,
                                    aPrintProgressListener);
           ]]>
         </body>
       </method>
 
+      <method name="drawSnapshot">
+        <parameter name="x"/>
+        <parameter name="y"/>
+        <parameter name="w"/>
+        <parameter name="h"/>
+        <parameter name="scale"/>
+        <parameter name="backgroundColor"/>
+        <body>
+          <![CDATA[
+            if (!this.frameLoader) {
+              throw Components.Exception("No frame loader.",
+                                         Cr.NS_ERROR_FAILURE);
+            }
+            return this.frameLoader.drawSnapshot(x, y, w, h, scale, backgroundColor);
+          ]]>
+        </body>
+      </method>
+
       <method name="dropLinks">
         <parameter name="aLinksCount"/>
         <parameter name="aLinks"/>
         <parameter name="aTriggeringPrincipal"/>
         <body><![CDATA[
           if (!this.droppedLinkHandler) {
             return false;
           }
--- a/xpcom/ds/nsTArray-inl.h
+++ b/xpcom/ds/nsTArray-inl.h
@@ -106,16 +106,33 @@ nsTArray_base<Alloc, Copy>::UsesAutoArra
 
 // defined in nsTArray.cpp
 bool IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity,
                                                   size_t aElemSize);
 
 template<class Alloc, class Copy>
 template<typename ActualAlloc>
 typename ActualAlloc::ResultTypeProxy
+nsTArray_base<Alloc, Copy>::ExtendCapacity(size_type aLength,
+                                           size_type aCount,
+                                           size_type aElemSize)
+{
+  mozilla::CheckedInt<size_type> newLength = aLength;
+  newLength += aCount;
+
+  if (!newLength.isValid()) {
+    return ActualAlloc::FailureResult();
+  }
+
+  return this->EnsureCapacity<ActualAlloc>(newLength.value(), aElemSize);
+}
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc>
+typename ActualAlloc::ResultTypeProxy
 nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type aCapacity,
                                            size_type aElemSize)
 {
   // This should be the most common case so test this first
   if (aCapacity <= mHdr->mCapacity) {
     return ActualAlloc::SuccessResult();
   }
 
@@ -325,39 +342,34 @@ nsTArray_base<Alloc, Copy>::SwapFromEnd(
   Copy::MoveNonOverlappingRegion(baseAddr + destBytes,
                                  baseAddr + sourceBytes,
                                  relocCount,
                                  aElemSize);
 }
 
 template<class Alloc, class Copy>
 template<typename ActualAlloc>
-bool
+typename ActualAlloc::ResultTypeProxy
 nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type aIndex, size_type aCount,
                                           size_type aElemSize,
                                           size_t aElemAlign)
 {
   if (MOZ_UNLIKELY(aIndex > Length())) {
     InvalidArrayIndex_CRASH(aIndex, Length());
   }
 
-  size_type newLen = Length() + aCount;
-
-  EnsureCapacity<ActualAlloc>(newLen, aElemSize);
-
-  // Check for out of memory conditions
-  if (Capacity() < newLen) {
-    return false;
+  if (!ActualAlloc::Successful(this->ExtendCapacity<ActualAlloc>(Length(), aCount, aElemSize))) {
+    return ActualAlloc::FailureResult();
   }
 
   // Move the existing elements as needed.  Note that this will
   // change our mLength, so no need to call IncrementLength.
   ShiftData<ActualAlloc>(aIndex, 0, aCount, aElemSize, aElemAlign);
 
-  return true;
+  return ActualAlloc::SuccessResult();
 }
 
 // nsTArray_base::IsAutoArrayRestorer is an RAII class which takes
 // |nsTArray_base &array| in its constructor.  When it's destructed, it ensures
 // that
 //
 //   * array.mIsAutoArray has the same value as it did when we started, and
 //   * if array has an auto buffer and mHdr would otherwise point to
--- a/xpcom/ds/nsTArray.h
+++ b/xpcom/ds/nsTArray.h
@@ -391,16 +391,27 @@ protected:
   // Resize the storage if necessary to achieve the requested capacity.
   // @param aCapacity The requested number of array elements.
   // @param aElemSize The size of an array element.
   // @return False if insufficient memory is available; true otherwise.
   template<typename ActualAlloc>
   typename ActualAlloc::ResultTypeProxy EnsureCapacity(size_type aCapacity,
                                                        size_type aElemSize);
 
+  // Extend the storage to accommodate aCount extra elements.
+  // @param aLength The current size of the array.
+  // @param aCount The number of elements to add.
+  // @param aElemSize The size of an array element.
+  // @return False if insufficient memory is available or the new length
+  //   would overflow; true otherwise.
+  template<typename ActualAlloc>
+  typename ActualAlloc::ResultTypeProxy ExtendCapacity(size_type aLength,
+                                                       size_type aCount,
+                                                       size_type aElemSize);
+
   // Tries to resize the storage to the minimum required amount. If this fails,
   // the array is left as-is.
   // @param aElemSize  The size of an array element.
   // @param aElemAlign The alignment in bytes of an array element.
   void ShrinkCapacity(size_type aElemSize, size_t aElemAlign);
 
   // This method may be called to resize a "gap" in the array by shifting
   // elements around.  It updates mLength appropriately.  If the resulting
@@ -443,18 +454,19 @@ protected:
 
   // This method inserts blank slots into the array.
   // @param aIndex the place to insert the new elements. This must be no
   //               greater than the current length of the array.
   // @param aCount the number of slots to insert
   // @param aElementSize the size of an array element.
   // @param aElemAlign the alignment in bytes of an array element.
   template<typename ActualAlloc>
-  bool InsertSlotsAt(index_type aIndex, size_type aCount,
-                     size_type aElementSize, size_t aElemAlign);
+  typename ActualAlloc::ResultTypeProxy
+  InsertSlotsAt(index_type aIndex, size_type aCount,
+                size_type aElementSize, size_t aElemAlign);
 
   template<typename ActualAlloc, class Allocator>
   typename ActualAlloc::ResultTypeProxy
   SwapArrayElements(nsTArray_base<Allocator, Copy>& aOther,
                     size_type aElemSize,
                     size_t aElemAlign);
 
   // This is an RAII class used in SwapArrayElements.
@@ -1756,18 +1768,18 @@ public:
   }
 
   // Append new elements without copy-constructing. This is useful to avoid
   // temporaries.
   // @return A pointer to the newly appended elements, or null on OOM.
 protected:
   template<typename ActualAlloc = Alloc>
   elem_type* AppendElements(size_type aCount) {
-    if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
-          Length() + aCount, sizeof(elem_type)))) {
+    if (!ActualAlloc::Successful(this->template ExtendCapacity<ActualAlloc>(
+          Length(), aCount, sizeof(elem_type)))) {
       return nullptr;
     }
     elem_type* elems = Elements() + Length();
     size_type i;
     for (i = 0; i < aCount; ++i) {
       elem_traits::Construct(elems + i);
     }
     this->IncrementLength(aCount);
@@ -2225,19 +2237,18 @@ public:
   // them using elem_type's default constructor.
   // @param aIndex the place to insert the new elements. This must be no
   //               greater than the current length of the array.
   // @param aCount the number of elements to insert
 protected:
   template<typename ActualAlloc = Alloc>
   elem_type* InsertElementsAt(index_type aIndex, size_type aCount)
   {
-    if (!base_type::template InsertSlotsAt<ActualAlloc>(aIndex, aCount,
-                                                        sizeof(elem_type),
-                                                        MOZ_ALIGNOF(elem_type))) {
+    if (!ActualAlloc::Successful(this->template InsertSlotsAt<ActualAlloc>(
+          aIndex, aCount, sizeof(elem_type), MOZ_ALIGNOF(elem_type)))) {
       return nullptr;
     }
 
     // Initialize the extra array elements
     elem_type* iter = Elements() + aIndex;
     elem_type* iend = iter + aCount;
     for (; iter != iend; ++iter) {
       elem_traits::Construct(iter);
@@ -2450,19 +2461,18 @@ nsTArray_Impl<E, Alloc>::RemoveElementsB
 }
 
 template<typename E, class Alloc>
 template<class Item, typename ActualAlloc>
 auto
 nsTArray_Impl<E, Alloc>::InsertElementsAt(index_type aIndex, size_type aCount,
                                           const Item& aItem) -> elem_type*
 {
-  if (!base_type::template InsertSlotsAt<ActualAlloc>(aIndex, aCount,
-                                                      sizeof(elem_type),
-                                                      MOZ_ALIGNOF(elem_type))) {
+  if (!ActualAlloc::Successful(this->template InsertSlotsAt<ActualAlloc>(
+        aIndex, aCount, sizeof(elem_type), MOZ_ALIGNOF(elem_type)))) {
     return nullptr;
   }
 
   // Initialize the extra array elements
   elem_type* iter = Elements() + aIndex;
   elem_type* iend = iter + aCount;
   for (; iter != iend; ++iter) {
     elem_traits::Construct(iter, aItem);
@@ -2475,16 +2485,17 @@ template<typename E, class Alloc>
 template<typename ActualAlloc>
 auto
 nsTArray_Impl<E, Alloc>::InsertElementAt(index_type aIndex) -> elem_type*
 {
   if (MOZ_UNLIKELY(aIndex > Length())) {
     InvalidArrayIndex_CRASH(aIndex, Length());
   }
 
+  // Length() + 1 is guaranteed to not overflow, so EnsureCapacity is OK.
   if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
         Length() + 1, sizeof(elem_type)))) {
     return nullptr;
   }
   this->template ShiftData<ActualAlloc>(aIndex, 0, 1, sizeof(elem_type),
                                         MOZ_ALIGNOF(elem_type));
   elem_type* elem = Elements() + aIndex;
   elem_traits::Construct(elem);
@@ -2495,34 +2506,35 @@ template<typename E, class Alloc>
 template<class Item, typename ActualAlloc>
 auto
 nsTArray_Impl<E, Alloc>::InsertElementAt(index_type aIndex, Item&& aItem) -> elem_type*
 {
   if (MOZ_UNLIKELY(aIndex > Length())) {
     InvalidArrayIndex_CRASH(aIndex, Length());
   }
 
+  // Length() + 1 is guaranteed to not overflow, so EnsureCapacity is OK.
   if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
          Length() + 1, sizeof(elem_type)))) {
     return nullptr;
   }
   this->template ShiftData<ActualAlloc>(aIndex, 0, 1, sizeof(elem_type),
                                         MOZ_ALIGNOF(elem_type));
   elem_type* elem = Elements() + aIndex;
   elem_traits::Construct(elem, std::forward<Item>(aItem));
   return elem;
 }
 
 template<typename E, class Alloc>
 template<class Item, typename ActualAlloc>
 auto
 nsTArray_Impl<E, Alloc>::AppendElements(const Item* aArray, size_type aArrayLen) -> elem_type*
 {
-  if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
-        Length() + aArrayLen, sizeof(elem_type)))) {
+  if (!ActualAlloc::Successful(this->template ExtendCapacity<ActualAlloc>(
+        Length(), aArrayLen, sizeof(elem_type)))) {
     return nullptr;
   }
   index_type len = Length();
   AssignRange(len, aArrayLen, aArray);
   this->IncrementLength(aArrayLen);
   return Elements() + len;
 }
 
@@ -2534,33 +2546,34 @@ nsTArray_Impl<E, Alloc>::AppendElements(
   MOZ_ASSERT(&aArray != this, "argument must be different aArray");
   if (Length() == 0) {
     SwapElements<ActualAlloc>(aArray);
     return Elements();
   }
 
   index_type len = Length();
   index_type otherLen = aArray.Length();
-  if (!Alloc::Successful(this->template EnsureCapacity<Alloc>(
-        len + otherLen, sizeof(elem_type)))) {
+  if (!Alloc::Successful(this->template ExtendCapacity<Alloc>(
+        len, otherLen, sizeof(elem_type)))) {
     return nullptr;
   }
   copy_type::MoveNonOverlappingRegion(Elements() + len, aArray.Elements(), otherLen,
                                       sizeof(elem_type));
   this->IncrementLength(otherLen);
   aArray.template ShiftData<Alloc>(0, otherLen, 0, sizeof(elem_type),
                                    MOZ_ALIGNOF(elem_type));
   return Elements() + len;
 }
 
 template<typename E, class Alloc>
 template<class Item, typename ActualAlloc>
 auto
 nsTArray_Impl<E, Alloc>::AppendElement(Item&& aItem) -> elem_type*
 {
+  // Length() + 1 is guaranteed to not overflow, so EnsureCapacity is OK.
   if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
          Length() + 1, sizeof(elem_type)))) {
     return nullptr;
   }
   elem_type* elem = Elements() + Length();
   elem_traits::Construct(elem, std::forward<Item>(aItem));
   this->mHdr->mLength += 1;
   return elem;
--- a/xpcom/ds/nsTHashtable.h
+++ b/xpcom/ds/nsTHashtable.h
@@ -93,16 +93,17 @@ public:
   {}
 
   /**
    * destructor, cleans up and deallocates
    */
   ~nsTHashtable();
 
   nsTHashtable(nsTHashtable<EntryType>&& aOther);
+  nsTHashtable<EntryType>& operator=(nsTHashtable<EntryType>&& aOther);
 
   /**
    * Return the generation number for the table. This increments whenever
    * the table data items are moved.
    */
   uint32_t GetGeneration() const { return mTable.Generation(); }
 
   /**
@@ -401,16 +402,24 @@ FixedSizeEntryMover(PLDHashTable*,
 
 template<class EntryType>
 nsTHashtable<EntryType>::nsTHashtable(nsTHashtable<EntryType>&& aOther)
   : mTable(std::move(aOther.mTable))
 {
 }
 
 template<class EntryType>
+nsTHashtable<EntryType>&
+nsTHashtable<EntryType>::operator=(nsTHashtable<EntryType>&& aOther)
+{
+  mTable = std::move(aOther.mTable);
+  return *this;
+}
+
+template<class EntryType>
 nsTHashtable<EntryType>::~nsTHashtable()
 {
 }
 
 template<class EntryType>
 /* static */ const PLDHashTableOps*
 nsTHashtable<EntryType>::Ops()
 {