Bug 1561395 - Move drawSnapshot API to WindowGlobalParent and allow specifying the whole viewport as a rect. r=mattwoodrow,nika
authorRyan Hunt <rhunt@eqrion.net>
Wed, 10 Jul 2019 16:45:46 +0000
changeset 482245 09fcee21b246dbd3622203d214234ee5116805f4
parent 482244 0873b1c9bca77ec839ea5269cceb2ed79f186458
child 482246 d050bd93ea39cc28f622d1cca046805565e0f474
push id89648
push userrhunt@eqrion.net
push dateWed, 10 Jul 2019 16:47:50 +0000
treeherderautoland@09fcee21b246 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, nika
bugs1561395
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1561395 - Move drawSnapshot API to WindowGlobalParent and allow specifying the whole viewport as a rect. r=mattwoodrow,nika Differential Revision: https://phabricator.services.mozilla.com/D35842
dom/base/nsFrameLoader.cpp
dom/base/nsFrameLoader.h
dom/chrome-webidl/WindowGlobalActors.webidl
dom/ipc/BrowserChild.cpp
dom/ipc/BrowserChild.h
dom/ipc/BrowserParent.cpp
dom/ipc/BrowserParent.h
dom/ipc/PBrowser.ipdl
dom/ipc/PWindowGlobal.ipdl
dom/ipc/WindowGlobalChild.cpp
dom/ipc/WindowGlobalChild.h
dom/ipc/WindowGlobalParent.cpp
dom/ipc/WindowGlobalParent.h
dom/webidl/FrameLoader.webidl
gfx/ipc/CrossProcessPaint.cpp
gfx/ipc/CrossProcessPaint.h
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -84,18 +84,16 @@
 #include "mozilla/dom/ChromeMessageSender.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FrameCrashedEvent.h"
 #include "mozilla/dom/FrameLoaderBinding.h"
 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
 #include "mozilla/dom/SessionStoreListener.h"
 #include "mozilla/gfx/CrossProcessPaint.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.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"
@@ -3214,64 +3212,16 @@ void nsFrameLoader::Print(uint64_t aOute
   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) {
-  MOZ_ASSERT(XRE_IsParentProcess());
-  if (!XRE_IsParentProcess()) {
-    aRv = NS_ERROR_FAILURE;
-    return nullptr;
-  }
-
-  RefPtr<nsIGlobalObject> global = GetOwnerContent()->GetOwnerGlobal();
-  RefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  RefPtr<Document> document = GetOwnerContent()->GetOwnerDocument();
-  if (NS_WARN_IF(!document)) {
-    aRv = NS_ERROR_FAILURE;
-    return nullptr;
-  }
-  PresShell* presShell = document->GetPresShell();
-  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(GetBrowserParent()->GetTabId(), rect,
-                                        aScale, color, promise);
-  } else {
-    gfx::CrossProcessPaint::StartLocal(GetDocShell(), rect, aScale, color,
-                                       promise);
-  }
-
-  return promise.forget();
-}
-
 already_AddRefed<nsIRemoteTab> nsFrameLoader::GetRemoteTab() {
   if (!mRemoteBrowser) {
     return nullptr;
   }
   if (auto* browserHost = mRemoteBrowser->AsBrowserHost()) {
     return do_AddRef(browserHost);
   }
   return nullptr;
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -196,20 +196,16 @@ class nsFrameLoader final : public nsStu
   void RequestUpdatePosition(mozilla::ErrorResult& aRv);
 
   bool RequestTabStateFlush(uint32_t aFlushId, bool aIsFinal = false);
 
   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/chrome-webidl/WindowGlobalActors.webidl
+++ b/dom/chrome-webidl/WindowGlobalActors.webidl
@@ -52,16 +52,35 @@ interface WindowGlobalParent {
   JSWindowActorParent getActor(DOMString name);
 
   [Throws]
   Promise<unsigned long long> changeFrameRemoteness(
     BrowsingContext? bc, DOMString remoteType,
     unsigned long long pendingSwitchId);
 
   /**
+   * Renders a region of the frame into an image bitmap.
+   *
+   * @param rect Specify the area of the window to render, in CSS pixels. This
+   * is relative to the current scroll position. If null, the entire viewport
+   * is rendered.
+   * @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(DOMRect? rect,
+                                    double scale,
+                                    DOMString backgroundColor);
+
+  /**
    * Fetches the securityInfo object for this window. This function will
    * look for failed and successful channels to find the security info,
    * thus it will work on regular HTTPS pages as well as certificate
    * error pages.
    *
    * This returns a Promise which resolves to an nsITransportSecurity
    * object with certificate data or undefined if no security info is available.
    */
--- a/dom/ipc/BrowserChild.cpp
+++ b/dom/ipc/BrowserChild.cpp
@@ -2482,44 +2482,16 @@ mozilla::ipc::IPCResult BrowserChild::Re
     }
   }
 
   UpdateVisibility(true);
 
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult BrowserChild::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 BrowserChild::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 BrowserChild::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/BrowserChild.h
+++ b/dom/ipc/BrowserChild.h
@@ -680,24 +680,16 @@ class BrowserChild final : public nsMess
 
   mozilla::ipc::IPCResult RecvSetDocShellIsActive(const bool& aIsActive);
 
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   mozilla::ipc::IPCResult RecvRenderLayers(
       const bool& aEnabled, const bool& aForce,
       const layers::LayersObserverEpoch& aEpoch);
 
-  mozilla::ipc::IPCResult RecvRequestRootPaint(
-      const IntRect& aRect, const float& aScale,
-      const nscolor& aBackgroundColor, RequestRootPaintResolver&& aResolve);
-
-  mozilla::ipc::IPCResult RecvRequestSubPaint(
-      const float& aScale, const nscolor& aBackgroundColor,
-      RequestSubPaintResolver&& aResolve);
-
   mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward,
                                             const bool& aForDocumentNavigation);
 
   mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint();
 
   mozilla::ipc::IPCResult RecvSuppressDisplayport(const bool& aEnabled);
 
   mozilla::ipc::IPCResult RecvParentActivated(const bool& aActivated);
--- a/dom/ipc/BrowserParent.cpp
+++ b/dom/ipc/BrowserParent.cpp
@@ -3323,49 +3323,16 @@ void BrowserParent::LayerTreeUpdate(cons
   } else {
     event->InitEvent(NS_LITERAL_STRING("MozLayerTreeCleared"), true, false);
   }
   event->SetTrusted(true);
   event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   mFrameElement->DispatchEvent(*event);
 }
 
-void BrowserParent::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 BrowserParent::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 BrowserParent::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/BrowserParent.h
+++ b/dom/ipc/BrowserParent.h
@@ -673,21 +673,16 @@ class BrowserParent final : public PBrow
 
   bool DeallocPPaymentRequestParent(PPaymentRequestParent* aActor);
 
   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);
-
   mozilla::ipc::IPCResult RecvInvokeDragSession(
       nsTArray<IPCDataTransfer>&& aTransfers, const uint32_t& aAction,
       Maybe<Shmem>&& aVisualDnDData, const uint32_t& aStride,
       const gfx::SurfaceFormat& aFormat, const LayoutDeviceIntRect& aDragRect,
       nsIPrincipal* aPrincipal);
 
   void AddInitialDnDDataTo(DataTransfer* aDataTransfer,
                            nsIPrincipal** aPrincipal);
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -26,42 +26,39 @@ include JavaScriptTypes;
 include URIParams;
 include PPrintingTypes;
 include PTabContext;
 include PBackgroundSharedTypes;
 
 include "mozilla/GfxMessageUtils.h";
 include "mozilla/layers/LayersMessageUtils.h";
 
-using moveonly mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
 using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
 using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using mozilla::LayoutDevicePoint from "Units.h";
 using mozilla::ScreenIntPoint from "Units.h";
 using ScreenIntSize from "Units.h";
 using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
 using struct mozilla::layers::ZoomConstraints from "mozilla/layers/ZoomConstraints.h";
 using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::MaybeZoomConstraints from "mozilla/layers/ZoomConstraints.h";
 using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/GeckoContentController.h";
 using ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
-using nscolor from "nsColor.h";
 using class mozilla::WidgetCompositionEvent from "ipc/nsGUIEventIPC.h";
 using struct mozilla::widget::IMENotification from "mozilla/widget/IMEData.h";
 using struct mozilla::widget::IMENotificationRequests from "mozilla/widget/IMEData.h";
 using struct mozilla::widget::IMEState from "mozilla/widget/IMEData.h";
 using struct mozilla::widget::InputContext from "mozilla/widget/IMEData.h";
 using struct mozilla::widget::InputContextAction from "mozilla/widget/IMEData.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
 using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
-using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
 using class mozilla::ContentCache from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetKeyboardEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetMouseEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetWheelEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetDragEvent from "ipc/nsGUIEventIPC.h";
 using struct nsRect from "nsRect.h";
 using class mozilla::WidgetSelectionEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetTouchEvent from "ipc/nsGUIEventIPC.h";
@@ -881,19 +878,16 @@ child:
      *        True if the child should force a paint even if it's already
      *        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/dom/ipc/PWindowGlobal.ipdl
+++ b/dom/ipc/PWindowGlobal.ipdl
@@ -6,16 +6,19 @@
 
 include protocol PBrowser;
 include protocol PInProcess;
 include protocol PBrowserBridge;
 
 include DOMTypes;
 
 using JSWindowActorMessageKind from "mozilla/dom/JSWindowActor.h";
+using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
+using moveonly mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
+using nscolor from "nsColor.h";
 
 namespace mozilla {
 namespace dom {
 
 struct JSWindowActorMessageMeta {
   nsString actorName;
   nsString messageName;
   uint64_t queryId;
@@ -35,16 +38,18 @@ async protocol PWindowGlobal
 child:
   async __delete__();
 
   async ChangeFrameRemoteness(BrowsingContext aFrameContext,
                               nsString aRemoteType,
                               uint64_t aSwitchId)
       returns (nsresult rv, nullable PBrowserBridge bridge);
 
+  async DrawSnapshot(IntRect? aRect, float aScale, nscolor aBackgroundColor) returns (PaintFragment retval);
+
   /**
    * Returns the serialized security info associated with this window.
    */
   async GetSecurityInfo() returns(nsCString? serializedSecInfo);
 
 both:
   async RawMessage(JSWindowActorMessageMeta aMetadata, ClonedMessageData aData);
 
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
 #include "mozilla/dom/BrowserChild.h"
 #include "mozilla/dom/BrowserBridgeChild.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/WindowGlobalActorsBinding.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/ipc/InProcessChild.h"
+#include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsFrameLoaderOwner.h"
 #include "nsGlobalWindowInner.h"
 #include "nsFrameLoaderOwner.h"
 #include "nsQueryObject.h"
 #include "nsSerializationHelper.h"
 
 #include "mozilla/dom/JSWindowActorBinding.h"
@@ -253,16 +254,30 @@ IPCResult WindowGlobalChild::RecvChangeF
   nsresult rv = ChangeFrameRemoteness(this, aBc, aRemoteType, aPendingSwitchId,
                                       getter_AddRefs(bbc));
 
   // To make the type system happy, we've gotta do some gymnastics.
   aResolver(Tuple<const nsresult&, PBrowserBridgeChild*>(rv, bbc));
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult WindowGlobalChild::RecvDrawSnapshot(
+    const Maybe<IntRect>& aRect, const float& aScale,
+    const nscolor& aBackgroundColor, DrawSnapshotResolver&& aResolve) {
+  nsCOMPtr<nsIDocShell> docShell = BrowsingContext()->GetDocShell();
+  if (!docShell) {
+    aResolve(gfx::PaintFragment{});
+    return IPC_OK();
+  }
+
+  aResolve(
+      gfx::PaintFragment::Record(docShell, aRect, aScale, aBackgroundColor));
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult WindowGlobalChild::RecvGetSecurityInfo(
     GetSecurityInfoResolver&& aResolve) {
   Maybe<nsCString> result;
 
   if (nsCOMPtr<Document> doc = mWindowGlobal->GetDoc()) {
     nsCOMPtr<nsISupports> secInfo;
     nsresult rv = NS_OK;
 
--- a/dom/ipc/WindowGlobalChild.h
+++ b/dom/ipc/WindowGlobalChild.h
@@ -98,16 +98,21 @@ class WindowGlobalChild final : public W
   // IPC messages
   mozilla::ipc::IPCResult RecvRawMessage(const JSWindowActorMessageMeta& aMeta,
                                          const ClonedMessageData& aData);
 
   mozilla::ipc::IPCResult RecvChangeFrameRemoteness(
       dom::BrowsingContext* aBc, const nsString& aRemoteType,
       uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver);
 
+  mozilla::ipc::IPCResult RecvDrawSnapshot(const Maybe<IntRect>& aRect,
+                                           const float& aScale,
+                                           const nscolor& aBackgroundColor,
+                                           DrawSnapshotResolver&& aResolve);
+
   mozilla::ipc::IPCResult RecvGetSecurityInfo(
       GetSecurityInfoResolver&& aResolve);
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
  private:
   WindowGlobalChild(nsGlobalWindowInner* aWindow, dom::BrowsingContext* aBc);
   ~WindowGlobalChild();
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -13,16 +13,18 @@
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/BrowserHost.h"
 #include "mozilla/dom/BrowserParent.h"
 #include "mozilla/dom/WindowGlobalActorsBinding.h"
 #include "mozilla/dom/WindowGlobalChild.h"
 #include "mozilla/dom/ChromeUtils.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/ServoCSSParser.h"
+#include "mozilla/ServoStyleSet.h"
 #include "mozJSComponentLoader.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsError.h"
 #include "nsFrameLoader.h"
 #include "nsFrameLoaderOwner.h"
 #include "nsGlobalWindowInner.h"
 #include "nsQueryObject.h"
@@ -279,17 +281,17 @@ already_AddRefed<Promise> WindowGlobalPa
     dom::BrowsingContext* aBc, const nsAString& aRemoteType,
     uint64_t aPendingSwitchId, ErrorResult& aRv) {
   RefPtr<BrowserParent> embedderBrowserParent = GetBrowserParent();
   if (NS_WARN_IF(!embedderBrowserParent)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsIGlobalObject* global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
+  nsIGlobalObject* global = GetParentObject();
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   RefPtr<CanonicalBrowsingContext> browsingContext =
       CanonicalBrowsingContext::Cast(aBc);
 
@@ -335,25 +337,68 @@ already_AddRefed<Promise> WindowGlobalPa
     promise->MaybeReject(NS_ERROR_FAILURE);
   };
 
   SendChangeFrameRemoteness(aBc, PromiseFlatString(aRemoteType),
                             aPendingSwitchId, resolve, reject);
   return promise.forget();
 }
 
+already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
+    const DOMRect* aRect, double aScale, const nsAString& aBackgroundColor,
+    mozilla::ErrorResult& aRv) {
+  nsIGlobalObject* global = GetParentObject();
+  RefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  nscolor color;
+  if (NS_WARN_IF(!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0),
+                                               aBackgroundColor, &color,
+                                               nullptr, nullptr))) {
+    aRv = NS_ERROR_FAILURE;
+    return nullptr;
+  }
+
+  if (!gfx::CrossProcessPaint::Start(this, aRect, (float)aScale, color,
+                                     promise)) {
+    aRv = NS_ERROR_FAILURE;
+    return nullptr;
+  }
+  return promise.forget();
+}
+
+void WindowGlobalParent::DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
+                                              const Maybe<IntRect>& aRect,
+                                              float aScale,
+                                              nscolor aBackgroundColor) {
+  auto promise = SendDrawSnapshot(aRect, aScale, aBackgroundColor);
+
+  RefPtr<gfx::CrossProcessPaint> paint(aPaint);
+  RefPtr<WindowGlobalParent> wgp(this);
+  promise->Then(
+      GetMainThreadSerialEventTarget(), __func__,
+      [paint, wgp](PaintFragment&& aFragment) {
+        paint->ReceiveFragment(wgp, std::move(aFragment));
+      },
+      [paint, wgp](ResponseRejectReason&& aReason) {
+        paint->LostFragment(wgp);
+      });
+}
+
 already_AddRefed<Promise> WindowGlobalParent::GetSecurityInfo(
     ErrorResult& aRv) {
   RefPtr<BrowserParent> browserParent = GetBrowserParent();
   if (NS_WARN_IF(!browserParent)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsIGlobalObject* global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
+  nsIGlobalObject* global = GetParentObject();
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   SendGetSecurityInfo(
       [promise](Maybe<nsCString>&& aResult) {
         if (aResult) {
@@ -405,17 +450,17 @@ WindowGlobalParent::~WindowGlobalParent(
   MOZ_ASSERT(!mWindowActors.Count());
 }
 
 JSObject* WindowGlobalParent::WrapObject(JSContext* aCx,
                                          JS::Handle<JSObject*> aGivenProto) {
   return WindowGlobalParent_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-nsISupports* WindowGlobalParent::GetParentObject() {
+nsIGlobalObject* WindowGlobalParent::GetParentObject() {
   return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowGlobalParent, WindowGlobalActor,
                                    mFrameLoader, mBrowsingContext,
                                    mWindowActors)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WindowGlobalParent,
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -3,40 +3,47 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_WindowGlobalParent_h
 #define mozilla_dom_WindowGlobalParent_h
 
 #include "mozilla/RefPtr.h"
+#include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/PWindowGlobalParent.h"
 #include "mozilla/dom/BrowserParent.h"
 #include "nsRefPtrHashtable.h"
 #include "nsWrapperCache.h"
 #include "nsISupports.h"
 #include "mozilla/dom/WindowGlobalActor.h"
 #include "mozilla/dom/CanonicalBrowsingContext.h"
 
 class nsIPrincipal;
 class nsIURI;
 class nsFrameLoader;
 
 namespace mozilla {
+
+namespace gfx {
+class CrossProcessPaint;
+}  // namespace gfx
+
 namespace dom {
 
 class WindowGlobalChild;
 class JSWindowActorParent;
 class JSWindowActorMessageMeta;
 
 /**
  * A handle in the parent process to a specific nsGlobalWindowInner object.
  */
 class WindowGlobalParent final : public WindowGlobalActor,
                                  public PWindowGlobalParent {
+  friend class gfx::CrossProcessPaint;
   friend class PWindowGlobalParent;
 
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WindowGlobalParent,
                                                          WindowGlobalActor)
 
   static already_AddRefed<WindowGlobalParent> GetByInnerWindowId(
@@ -102,27 +109,31 @@ class WindowGlobalParent final : public 
 
   bool IsInitialDocument() { return mIsInitialDocument; }
 
   already_AddRefed<Promise> ChangeFrameRemoteness(dom::BrowsingContext* aBc,
                                                   const nsAString& aRemoteType,
                                                   uint64_t aPendingSwitchId,
                                                   ErrorResult& aRv);
 
+  already_AddRefed<mozilla::dom::Promise> DrawSnapshot(
+      const DOMRect* aRect, double aScale, const nsAString& aBackgroundColor,
+      mozilla::ErrorResult& aRv);
+
   already_AddRefed<Promise> GetSecurityInfo(ErrorResult& aRv);
 
   // Create a WindowGlobalParent from over IPC. This method should not be called
   // from outside of the IPC constructors.
   WindowGlobalParent(const WindowGlobalInit& aInit, bool aInProcess);
 
   // Initialize the mFrameLoader fields for a created WindowGlobalParent. Must
   // be called after setting the Manager actor.
   void Init(const WindowGlobalInit& aInit);
 
-  nsISupports* GetParentObject();
+  nsIGlobalObject* GetParentObject();
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
  protected:
   const nsAString& GetRemoteType() override;
   JSWindowActor::Type GetSide() override { return JSWindowActor::Type::Parent; }
 
   // IPC messages
@@ -135,16 +146,20 @@ class WindowGlobalParent final : public 
   mozilla::ipc::IPCResult RecvDestroy();
   mozilla::ipc::IPCResult RecvRawMessage(const JSWindowActorMessageMeta& aMeta,
                                          const ClonedMessageData& aData);
   mozilla::ipc::IPCResult RecvDidEmbedBrowsingContext(
       dom::BrowsingContext* aContext);
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
+  void DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
+                            const Maybe<IntRect>& aRect, float aScale,
+                            nscolor aBackgroundColor);
+
  private:
   ~WindowGlobalParent();
 
   // NOTE: This document principal doesn't reflect possible |document.domain|
   // mutations which may have been made in the actual document.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
   nsCOMPtr<nsIURI> mDocumentURI;
   RefPtr<nsFrameLoader> mFrameLoader;
--- a/dom/webidl/FrameLoader.webidl
+++ b/dom/webidl/FrameLoader.webidl
@@ -116,40 +116,16 @@ 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);
-
-  /**
    * The element which owns this frame loader.
    *
    * For example, if this is a frame loader for an <iframe>, this attribute
    * returns the iframe element.
    */
   [Pure]
   readonly attribute Element? ownerElement;
 
--- a/gfx/ipc/CrossProcessPaint.cpp
+++ b/gfx/ipc/CrossProcessPaint.cpp
@@ -4,16 +4,19 @@
  * 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/BrowserParent.h"
+#include "mozilla/dom/PWindowGlobalParent.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/WindowGlobalChild.h"
 #include "mozilla/gfx/DrawEventRecorder.h"
 #include "mozilla/gfx/InlineTranslator.h"
 #include "mozilla/PresShell.h"
 
 #include "gfxPlatform.h"
 
 #include "nsContentUtils.h"
 #include "nsGlobalWindowInner.h"
@@ -36,70 +39,92 @@ namespace gfx {
 
 using namespace mozilla::ipc;
 
 /// The minimum scale we allow tabs to be rasterized at.
 static const float kMinPaintScale = 0.05f;
 
 /* static */
 PaintFragment PaintFragment::Record(nsIDocShell* aDocShell,
-                                    const IntRect& aRect, float aScale,
+                                    const Maybe<IntRect>& aRect, float aScale,
                                     nscolor aBackgroundColor) {
-  IntSize surfaceSize = aRect.Size();
+  if (!aDocShell) {
+    PF_LOG("Couldn't find DocShell.\n");
+    return PaintFragment{};
+  }
+
+  RefPtr<nsPresContext> presContext = aDocShell->GetPresContext();
+  if (!presContext) {
+    PF_LOG("Couldn't find PresContext.\n");
+    return PaintFragment{};
+  }
+
+  IntRect rect;
+  if (!aRect) {
+    nsCOMPtr<nsIWidget> widget =
+        nsContentUtils::WidgetForDocument(aDocShell->GetDocument());
+
+    // TODO: Apply some sort of clipping to visible bounds here (Bug 1562720)
+    LayoutDeviceIntRect boundsDevice = widget->GetBounds();
+    boundsDevice.MoveTo(0, 0);
+    nsRect boundsAu = LayoutDevicePixel::ToAppUnits(
+        boundsDevice, presContext->AppUnitsPerDevPixel());
+    rect = gfx::RoundedOut(CSSPixel::FromAppUnits(boundsAu).ToUnknownRect());
+  } else {
+    rect = *aRect;
+  }
+
+  if (rect.IsEmpty()) {
+    // TODO: Should we return an empty surface here?
+    PF_LOG("Empty rect to paint.\n");
+    return PaintFragment{};
+  }
+
+  IntSize surfaceSize = rect.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,
+      aDocShell, rect.x, rect.y, rect.width, rect.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) {
-    presContext = aDocShell->GetPresContext();
-  }
-  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, IntRect(IntPoint(0, 0), surfaceSize));
 
   // Perform the actual rendering
   {
-    nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x),
-             nsPresContext::CSSPixelsToAppUnits(aRect.y),
-             nsPresContext::CSSPixelsToAppUnits(aRect.width),
-             nsPresContext::CSSPixelsToAppUnits(aRect.height));
+    nsRect r(nsPresContext::CSSPixelsToAppUnits(rect.x),
+             nsPresContext::CSSPixelsToAppUnits(rect.y),
+             nsPresContext::CSSPixelsToAppUnits(rect.width),
+             nsPresContext::CSSPixelsToAppUnits(rect.height));
 
     RefPtr<gfxContext> thebes = gfxContext::CreateOrNull(dt);
     thebes->SetMatrix(Matrix::Scaling(aScale, aScale));
     RefPtr<PresShell> presShell = presContext->PresShell();
     Unused << presShell->RenderDocument(r, RenderDocumentFlags::None,
                                         aBackgroundColor, thebes);
   }
 
@@ -123,247 +148,265 @@ bool PaintFragment::IsEmpty() const {
 
 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) {
+bool CrossProcessPaint::Start(dom::WindowGlobalParent* aRoot,
+                              const dom::DOMRect* 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), "
+      "Starting paint. "
+      "[wgp=%p, "
       "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),
+      aRoot, aScale, NS_GET_R(aBackgroundColor), NS_GET_G(aBackgroundColor),
       NS_GET_B(aBackgroundColor), NS_GET_A(aBackgroundColor));
 
+  Maybe<IntRect> rect;
+  if (aRect) {
+    *rect = IntRect::RoundOut((float)aRect->X(), (float)aRect->Y(),
+                              (float)aRect->Width(), (float)aRect->Height());
+  }
+
   RefPtr<CrossProcessPaint> resolver =
       new CrossProcessPaint(aPromise, aScale, aBackgroundColor, aRoot);
-  resolver->QueueRootPaint(aRoot, aRect, aScale, aBackgroundColor);
+
+  if (aRoot->IsInProcess()) {
+    RefPtr<dom::WindowGlobalChild> childActor = aRoot->GetChildActor();
+    if (!childActor) {
+      return false;
+    }
+
+    // `BrowsingContext()` cannot be nullptr.
+    nsCOMPtr<nsIDocShell> docShell =
+        childActor->BrowsingContext()->GetDocShell();
+    if (!docShell) {
+      return false;
+    }
+
+    resolver->mPendingFragments += 1;
+    resolver->ReceiveFragment(
+        aRoot, PaintFragment::Record(docShell, rect, aScale, aBackgroundColor));
+  } else {
+    resolver->QueuePaint(aRoot, rect);
+  }
+  return true;
 }
 
 CrossProcessPaint::CrossProcessPaint(dom::Promise* aPromise, float aScale,
                                      nscolor aBackgroundColor,
-                                     dom::TabId aRootId)
+                                     dom::WindowGlobalParent* aRoot)
     : mPromise{aPromise},
-      mRootId{aRootId},
+      mRoot{aRoot},
       mScale{aScale},
       mBackgroundColor{aBackgroundColor},
-      mPendingFragments{1} {}
+      mPendingFragments{0} {}
 
 CrossProcessPaint::~CrossProcessPaint() {}
 
-void CrossProcessPaint::ReceiveFragment(dom::TabId aId,
+void CrossProcessPaint::ReceiveFragment(dom::WindowGlobalParent* aWGP,
                                         PaintFragment&& aFragment) {
   if (IsCleared()) {
-    CPP_LOG("Ignoring fragment from %llu.\n", (uint64_t)aId);
+    CPP_LOG("Ignoring fragment from %p.\n", aWGP);
     return;
   }
 
   MOZ_ASSERT(mPendingFragments > 0);
-  MOZ_ASSERT(!mReceivedFragments.GetValue(aId));
+  MOZ_ASSERT(!mReceivedFragments.GetValue(aWGP));
   MOZ_ASSERT(!aFragment.IsEmpty());
 
   // Double check our invariants to protect against a compromised content
   // process
-  if (mPendingFragments == 0 || mReceivedFragments.GetValue(aId) ||
+  if (mPendingFragments == 0 || mReceivedFragments.GetValue(aWGP) ||
       aFragment.IsEmpty()) {
-    CPP_LOG("Dropping invalid fragment from %llu.\n", (uint64_t)aId);
-    LostFragment(aId);
+    CPP_LOG("Dropping invalid fragment from %p.\n", aWGP);
+    LostFragment(aWGP);
     return;
   }
 
-  CPP_LOG("Receiving fragment from %llu.\n", (uint64_t)aId);
+  CPP_LOG("Receiving fragment from %p.\n", aWGP);
 
   // Queue paints for child tabs
   for (auto iter = aFragment.mDependencies.Iter(); !iter.Done(); iter.Next()) {
-    auto dependency = iter.Get()->GetKey();
-    QueueSubPaint(dom::TabId(dependency));
+    auto dependency = dom::TabId(iter.Get()->GetKey());
+
+    // Get the current WindowGlobalParent of the remote browser that was marked
+    // as a dependency
+    dom::ContentProcessManager* cpm =
+        dom::ContentProcessManager::GetSingleton();
+    dom::ContentParentId cpId = cpm->GetTabProcessId(dependency);
+    RefPtr<dom::BrowserParent> browser =
+        cpm->GetBrowserParentByProcessAndTabId(cpId, dependency);
+    RefPtr<dom::WindowGlobalParent> wgp =
+        browser->GetBrowsingContext()->GetCurrentWindowGlobal();
+
+    if (!wgp) {
+      CPP_LOG("Skipping dependency %llu with no current WGP.\n",
+              (uint64_t)dependency);
+      continue;
+    }
+
+    // TODO: Apply some sort of clipping to visible bounds here (Bug 1562720)
+    QueuePaint(wgp, Nothing());
   }
 
-  mReceivedFragments.Put(aId, std::move(aFragment));
+  mReceivedFragments.Put(aWGP, std::move(aFragment));
   mPendingFragments -= 1;
 
   // Resolve this paint if we have received all pending fragments
   MaybeResolve();
 }
 
-void CrossProcessPaint::LostFragment(dom::TabId aId) {
+void CrossProcessPaint::LostFragment(dom::WindowGlobalParent* aWGP) {
   if (IsCleared()) {
-    CPP_LOG("Ignoring lost fragment from %llu.\n", (uint64_t)aId);
+    CPP_LOG("Ignoring lost fragment from %p.\n", aWGP);
     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::BrowserParent> tab =
-      cpm->GetBrowserParentByProcessAndTabId(cpId, aId);
-  tab->RequestRootPaint(this, aRect, aScale, aBackgroundColor);
+void CrossProcessPaint::QueuePaint(dom::WindowGlobalParent* aWGP,
+                                   const Maybe<IntRect>& aRect) {
+  MOZ_ASSERT(!mReceivedFragments.GetValue(aWGP));
 
-  // 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 paint for %p.\n", aWGP);
 
-  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::BrowserParent> tab =
-      cpm->GetBrowserParentByProcessAndTabId(cpId, aId);
-  tab->RequestSubPaint(this, mScale, mBackgroundColor);
-
+  // TODO - Don't apply the background color to all paints (Bug 1562722)
+  aWGP->DrawSnapshotInternal(this, aRect, mScale, mBackgroundColor);
   mPendingFragments += 1;
 }
 
 void CrossProcessPaint::Clear() {
   mPromise = nullptr;
   mPendingFragments = 0;
   mReceivedFragments.Clear();
 }
 
 bool CrossProcessPaint::IsCleared() const { return !mPromise; }
 
+static dom::TabId GetTabId(dom::WindowGlobalParent* aWGP) {
+  // There is no unique TabId for a given WindowGlobalParent, as multiple
+  // WindowGlobalParents share the same PBrowser actor. However, we only
+  // ever queue one paint per PBrowser by just using the current
+  // WindowGlobalParent for a PBrowser. So we can interchange TabId and
+  // WindowGlobalParent when dealing with resolving surfaces.
+  RefPtr<dom::BrowserParent> browserParent = aWGP->GetBrowserParent();
+  return browserParent ? browserParent->GetTabId() : dom::TabId(0);
+}
+
 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)) {
+  if (!ResolveInternal(mRoot, &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);
+  // Grab the result from the resolved table.
+  RefPtr<SourceSurface> root = resolved.Get(GetTabId(mRoot));
   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,
+bool CrossProcessPaint::ResolveInternal(dom::WindowGlobalParent* aWGP,
                                         ResolvedSurfaceMap* aResolved) {
+  // Convert aWGP to an ID we can use for surfaces
+  dom::TabId surfaceId = GetTabId(aWGP);
+
   // We should not have resolved this paint already
-  MOZ_ASSERT(!aResolved->GetWeak(aId));
+  MOZ_ASSERT(!aResolved->GetWeak(surfaceId));
 
-  CPP_LOG("Resolving fragment %llu.\n", (uint64_t)aId);
+  CPP_LOG("Resolving fragment %p.\n", aWGP);
 
-  Maybe<PaintFragment> fragment = mReceivedFragments.GetAndRemove(aId);
+  Maybe<PaintFragment> fragment = mReceivedFragments.GetAndRemove(aWGP);
 
   // 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)) {
+    auto dependency = dom::TabId(iter.Get()->GetKey());
+
+    dom::ContentProcessManager* cpm =
+        dom::ContentProcessManager::GetSingleton();
+    dom::ContentParentId cpId = cpm->GetTabProcessId(dependency);
+    RefPtr<dom::BrowserParent> tab =
+        cpm->GetBrowserParentByProcessAndTabId(cpId, dependency);
+    RefPtr<dom::WindowGlobalParent> wgp =
+        tab->GetBrowsingContext()->GetCurrentWindowGlobal();
+
+    if (!ResolveInternal(wgp, 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);
+    CPP_LOG("Couldn't create (%d x %d) surface for fragment %p.\n",
+            fragment->mSize.width, fragment->mSize.height, aWGP);
     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);
+      CPP_LOG("Couldn't translate recording for fragment %p.\n", aWGP);
       return false;
     }
   }
 
   RefPtr<SourceSurface> snapshot = drawTarget->Snapshot();
   if (!snapshot) {
-    CPP_LOG("Couldn't get snapshot for fragment %llu.\n", (uint64_t)aId);
+    CPP_LOG("Couldn't get snapshot for fragment %p.\n", aWGP);
     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);
+  aResolved->Put(surfaceId, snapshot);
   return true;
 }
 
 }  // namespace gfx
 }  // namespace mozilla
--- a/gfx/ipc/CrossProcessPaint.h
+++ b/gfx/ipc/CrossProcessPaint.h
@@ -5,29 +5,36 @@
  * 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/dom/DOMRect.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/Maybe.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 dom {
+class WindowGlobalParent;
+}  // namespace dom
+
 namespace gfx {
 
 class CrossProcessPaint;
 
 /**
  * A fragment of a paint of a cross process document tree.
  */
 class PaintFragment final {
@@ -35,27 +42,29 @@ class PaintFragment final {
   /// 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 aRect The rectangle relative to the viewport to use. If no
+   *   rectangle is specified, then the whole viewport will be used.
    * @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);
+  static PaintFragment Record(nsIDocShell* aDocShell,
+                              const Maybe<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:
@@ -75,81 +84,68 @@ class PaintFragment final {
  * An object for painting a cross process document tree.
  */
 class CrossProcessPaint final {
   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.
+   * a WindowGlobalParent. A maybe-async paint for the root WGP will be done,
+   * 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 aRoot The WindowGlobalParent to paint.
+   * @param aRect The rectangle relative to the viewport to use, or null to
+   *   render the whole viewport.
    * @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.
+   *
+   * @returns Whether the paint was able to be initiated or not.
    */
-  static void StartLocal(nsIDocShell* aRoot, const IntRect& aRect, float aScale,
-                         nscolor aBackgroundColor, dom::Promise* aPromise);
+  static bool Start(dom::WindowGlobalParent* aRoot, const dom::DOMRect* 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);
+  void ReceiveFragment(dom::WindowGlobalParent* aWGP,
+                       PaintFragment&& aFragment);
+  void LostFragment(dom::WindowGlobalParent* aWGP);
 
  private:
   typedef nsRefPtrHashtable<nsUint64HashKey, SourceSurface> ResolvedSurfaceMap;
-  typedef nsDataHashtable<nsUint64HashKey, PaintFragment> ReceivedFragmentMap;
+  typedef nsDataHashtable<nsRefPtrHashKey<dom::WindowGlobalParent>,
+                          PaintFragment>
+      ReceivedFragmentMap;
 
   CrossProcessPaint(dom::Promise* aPromise, float aScale,
-                    nscolor aBackgroundColor, dom::TabId aRootId);
+                    nscolor aBackgroundColor, dom::WindowGlobalParent* aRoot);
   ~CrossProcessPaint();
 
-  void QueueRootPaint(dom::TabId aId, const IntRect& aRect, float aScale,
-                      nscolor aBackgroundColor);
-  void QueueSubPaint(dom::TabId aId);
+  void QueuePaint(dom::WindowGlobalParent* aWGP, const Maybe<IntRect>& aRect);
 
   /// 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);
+  bool ResolveInternal(dom::WindowGlobalParent* aWGP,
+                       ResolvedSurfaceMap* aResolved);
 
   RefPtr<dom::Promise> mPromise;
-  dom::TabId mRootId;
+  RefPtr<dom::WindowGlobalParent> mRoot;
   float mScale;
   nscolor mBackgroundColor;
   uint32_t mPendingFragments;
   ReceivedFragmentMap mReceivedFragments;
 };
 
 }  // namespace gfx
 }  // namespace mozilla