Merge inbound to mozilla-central. a=merge
authorNoemi Erli <nerli@mozilla.com>
Tue, 09 Oct 2018 00:52:28 +0300
changeset 498456 df860e79a6a3cc41712cfd86ffbd18cf84fce626
parent 498436 e3d243f3ff07fa45ae97858db2e0ccf07822918f (current diff)
parent 498455 113ba8a072a6cb6074fe176fe84965bbd7a1970d (diff)
child 498457 f95186d58bfaad221b599e284b57d980ade32468
child 498469 aab7bf199799d5c3c696fa5c369491bff062873f
child 498572 26913fab74050dbe195b330a250795c14407bf4f
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
df860e79a6a3 / 64.0a1 / 20181008220129 / files
nightly linux64
df860e79a6a3 / 64.0a1 / 20181008220129 / files
nightly mac
df860e79a6a3 / 64.0a1 / 20181008220129 / files
nightly win32
df860e79a6a3 / 64.0a1 / 20181008220129 / files
nightly win64
df860e79a6a3 / 64.0a1 / 20181008220129 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- 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()
 {