Bug 1475139 part 11 - Add CrossProcessPaint implementation. r=mattwoodrow

This commit adds a CrossProcessPaint class which can be used to paint a
cross process document tree. This API is async, as we cannot block on child
processes, and initially geared towards servicing a JS API and not internal
consumers. The API can only be used in the chrome process for security
reasons.

The class is implemented as a recursive resolver, requesting a root paint,
gathering dependent frames to be painted, then requesting paints from those
tabs. Once all paints have been completed, the dependency tree is rasterized
in a bottom up fashion.

Future improvements can be made here. Currently, the rasterization is
performed on the main thread which could cause jank. We also transmit
recordings directly over IPDl, and no effort is made to minimize the
recordings from child layer trees.

Differential Revision: https://phabricator.services.mozilla.com/D6790

--HG--
extra : rebase_source : b213de269b33486552ddc0be17207f9fb3f78c9c
This commit is contained in:
Ryan Hunt 2018-09-24 21:48:02 -05:00
parent 454819897e
commit 84bbf4f7d0
9 changed files with 687 additions and 0 deletions

View File

@ -32,6 +32,7 @@ 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";
@ -766,6 +767,9 @@ child:
*/
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

View File

@ -26,6 +26,7 @@
#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"
@ -2667,6 +2668,31 @@ TabChild::RecvRenderLayers(const bool& aEnabled, const bool& aForceRepaint, cons
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)
{

View File

@ -724,6 +724,10 @@ protected:
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;

View File

@ -3098,6 +3098,38 @@ TabParent::LayerTreeUpdate(const LayersObserverEpoch& aEpoch, bool aActive)
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)
{

View File

@ -17,6 +17,7 @@
#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"
@ -562,6 +563,9 @@ public:
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,

View File

@ -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

170
gfx/ipc/CrossProcessPaint.h Normal file
View File

@ -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_

View File

@ -20,6 +20,7 @@
#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"
@ -1279,6 +1280,23 @@ struct ParamTraits<mozilla::Array<T, Length>>
}
};
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__ */

View File

@ -13,6 +13,7 @@ EXPORTS.mozilla += [
]
EXPORTS.mozilla.gfx += [
'CrossProcessPaint.h',
'GPUChild.h',
'GPUParent.h',
'GPUProcessHost.h',
@ -49,6 +50,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
UNIFIED_SOURCES += [
'CompositorSession.cpp',
'CompositorWidgetVsyncObserver.cpp',
'CrossProcessPaint.cpp',
'D3DMessageUtils.cpp',
'GPUChild.cpp',
'GPUProcessHost.cpp',