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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Ryan Hunt 2019-07-10 16:45:46 +00:00
Родитель 55b9f173eb
Коммит 27bd25767f
16 изменённых файлов: 292 добавлений и 307 удалений

Просмотреть файл

@ -89,8 +89,6 @@
#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"
@ -3219,54 +3217,6 @@ void nsFrameLoader::Print(uint64_t aOuterWindowID,
#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;

Просмотреть файл

@ -201,10 +201,6 @@ class nsFrameLoader final : public nsStubMutationObserver,
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);

Просмотреть файл

@ -56,6 +56,25 @@ interface WindowGlobalParent {
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,

Просмотреть файл

@ -2487,34 +2487,6 @@ mozilla::ipc::IPCResult BrowserChild::RecvRenderLayers(
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();

Просмотреть файл

@ -685,14 +685,6 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
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);

Просмотреть файл

@ -3328,39 +3328,6 @@ void BrowserParent::LayerTreeUpdate(const LayersObserverEpoch& aEpoch,
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

Просмотреть файл

@ -678,11 +678,6 @@ class BrowserParent final : public PBrowserParent,
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,

Просмотреть файл

@ -31,7 +31,6 @@ 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";
@ -47,7 +46,6 @@ using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/Geck
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";
@ -56,7 +54,6 @@ 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";
@ -886,9 +883,6 @@ child:
* 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.

Просмотреть файл

@ -11,6 +11,9 @@ 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 {
@ -40,6 +43,8 @@ child:
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.
*/

Просмотреть файл

@ -18,6 +18,7 @@
#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"
@ -258,6 +259,20 @@ IPCResult WindowGlobalChild::RecvChangeFrameRemoteness(
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;

Просмотреть файл

@ -103,6 +103,11 @@ class WindowGlobalChild final : public WindowGlobalActor,
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);

Просмотреть файл

@ -18,6 +18,8 @@
#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"
@ -284,7 +286,7 @@ already_AddRefed<Promise> WindowGlobalParent::ChangeFrameRemoteness(
return nullptr;
}
nsIGlobalObject* global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
nsIGlobalObject* global = GetParentObject();
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
@ -340,6 +342,49 @@ already_AddRefed<Promise> WindowGlobalParent::ChangeFrameRemoteness(
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();
@ -348,7 +393,7 @@ already_AddRefed<Promise> WindowGlobalParent::GetSecurityInfo(
return nullptr;
}
nsIGlobalObject* global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
nsIGlobalObject* global = GetParentObject();
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
@ -410,7 +455,7 @@ JSObject* WindowGlobalParent::WrapObject(JSContext* aCx,
return WindowGlobalParent_Binding::Wrap(aCx, this, aGivenProto);
}
nsISupports* WindowGlobalParent::GetParentObject() {
nsIGlobalObject* WindowGlobalParent::GetParentObject() {
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
}

Просмотреть файл

@ -8,6 +8,7 @@
#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"
@ -21,6 +22,11 @@ class nsIURI;
class nsFrameLoader;
namespace mozilla {
namespace gfx {
class CrossProcessPaint;
} // namespace gfx
namespace dom {
class WindowGlobalChild;
@ -32,6 +38,7 @@ class JSWindowActorMessageMeta;
*/
class WindowGlobalParent final : public WindowGlobalActor,
public PWindowGlobalParent {
friend class gfx::CrossProcessPaint;
friend class PWindowGlobalParent;
public:
@ -107,6 +114,10 @@ class WindowGlobalParent final : public WindowGlobalActor,
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
@ -117,7 +128,7 @@ class WindowGlobalParent final : public WindowGlobalActor,
// 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;
@ -140,6 +151,10 @@ class WindowGlobalParent final : public WindowGlobalActor,
void ActorDestroy(ActorDestroyReason aWhy) override;
void DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
const Maybe<IntRect>& aRect, float aScale,
nscolor aBackgroundColor);
private:
~WindowGlobalParent();

Просмотреть файл

@ -120,30 +120,6 @@ interface FrameLoader {
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.
*

Просмотреть файл

@ -9,6 +9,9 @@
#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"
@ -41,9 +44,41 @@ 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;
@ -53,7 +88,7 @@ PaintFragment PaintFragment::Record(nsIDocShell* aDocShell,
"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));
@ -68,16 +103,6 @@ PaintFragment PaintFragment::Record(nsIDocShell* aDocShell,
// 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(
@ -91,10 +116,10 @@ PaintFragment PaintFragment::Record(nsIDocShell* aDocShell,
// 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));
@ -128,100 +153,119 @@ PaintFragment::PaintFragment(IntSize aSize, ByteBuf&& aRecording,
mDependencies(std::move(aDependencies)) {}
/* static */
void CrossProcessPaint::StartLocal(nsIDocShell* 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 local paint. "
"[docshell=%p, "
"rect=(%d, %d) x (%d, %d), "
"Starting paint. "
"[wgp=%p, "
"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),
aRoot, 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));
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;
}
@ -229,36 +273,14 @@ void CrossProcessPaint::LostFragment(dom::TabId aId) {
Clear();
}
void CrossProcessPaint::QueueRootPaint(dom::TabId aId, const IntRect& aRect,
float aScale, nscolor aBackgroundColor) {
MOZ_ASSERT(!mReceivedFragments.GetValue(aId));
MOZ_ASSERT(mPendingFragments == 1);
void CrossProcessPaint::QueuePaint(dom::WindowGlobalParent* aWGP,
const Maybe<IntRect>& aRect) {
MOZ_ASSERT(!mReceivedFragments.GetValue(aWGP));
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);
// 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::BrowserParent> tab =
cpm->GetBrowserParentByProcessAndTabId(cpId, aId);
tab->RequestSubPaint(this, mScale, mBackgroundColor);
CPP_LOG("Queueing paint for %p.\n", aWGP);
// TODO - Don't apply the background color to all paints (Bug 1562722)
aWGP->DrawSnapshotInternal(this, aRect, mScale, mBackgroundColor);
mPendingFragments += 1;
}
@ -270,6 +292,16 @@ void CrossProcessPaint::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
@ -283,7 +315,7 @@ void CrossProcessPaint::MaybeResolve() {
// 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);
@ -291,8 +323,8 @@ void CrossProcessPaint::MaybeResolve() {
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;
@ -309,19 +341,31 @@ void CrossProcessPaint::MaybeResolve() {
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;
}
}
@ -331,8 +375,8 @@ bool CrossProcessPaint::ResolveInternal(dom::TabId aId,
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;
}
@ -342,15 +386,14 @@ bool CrossProcessPaint::ResolveInternal(dom::TabId aId,
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;
}
@ -361,7 +404,7 @@ bool CrossProcessPaint::ResolveInternal(dom::TabId aId,
aResolved->Remove(dependency);
}
aResolved->Put(aId, snapshot);
aResolved->Put(surfaceId, snapshot);
return true;
}

Просмотреть файл

@ -10,7 +10,9 @@
#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"
@ -23,6 +25,11 @@ struct ParamTraits;
} // namespace IPC
namespace mozilla {
namespace dom {
class WindowGlobalParent;
} // namespace dom
namespace gfx {
class CrossProcessPaint;
@ -40,7 +47,8 @@ class PaintFragment final {
* 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.
@ -49,8 +57,9 @@ class PaintFragment final {
* @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;
@ -80,56 +89,42 @@ class CrossProcessPaint final {
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.
*/
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.
* @returns Whether the paint was able to be initiated or not.
*/
static void StartRemote(dom::TabId 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);
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.
@ -141,10 +136,11 @@ class CrossProcessPaint final {
/// 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;