mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
538087de5f
This adds a new FX_TAB_SWITCH_TOTAL_MS that should more accurately represent the user experience of tab switch time than FX_TAB_SWITCH_UPDATE_MS. FX_TAB_SWITCH_UPDATE_MS is being kept because it gives a good indication of how much time is being spent in the front end parts vs the painting parts. This works by measuring the time between beginTabSwitch() and the first call to LayerManager::PostPresent().
1309 lines
36 KiB
C++
1309 lines
36 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: sw=2 ts=8 et :
|
|
*/
|
|
/* 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 "mozilla/layers/PLayers.h"
|
|
#include "mozilla/layers/ShadowLayers.h"
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
#include "ImageLayers.h"
|
|
#include "ImageContainer.h"
|
|
#include "Layers.h"
|
|
#include "gfxPlatform.h"
|
|
#include "ReadbackLayer.h"
|
|
#include "gfxUtils.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "mozilla/Util.h"
|
|
#include "LayerSorter.h"
|
|
#include "AnimationCommon.h"
|
|
|
|
using namespace mozilla::layers;
|
|
using namespace mozilla::gfx;
|
|
|
|
typedef FrameMetrics::ViewID ViewID;
|
|
const ViewID FrameMetrics::NULL_SCROLL_ID = 0;
|
|
const ViewID FrameMetrics::ROOT_SCROLL_ID = 1;
|
|
const ViewID FrameMetrics::START_SCROLL_ID = 2;
|
|
|
|
uint8_t gLayerManagerLayerBuilder;
|
|
|
|
#ifdef MOZ_LAYERS_HAVE_LOG
|
|
FILE*
|
|
FILEOrDefault(FILE* aFile)
|
|
{
|
|
return aFile ? aFile : stderr;
|
|
}
|
|
#endif // MOZ_LAYERS_HAVE_LOG
|
|
|
|
namespace {
|
|
|
|
// XXX pretty general utilities, could centralize
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const void* p,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
s += nsPrintfCString("%p", p);
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const gfxPattern::GraphicsFilter& f,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
switch (f) {
|
|
case gfxPattern::FILTER_FAST: s += "fast"; break;
|
|
case gfxPattern::FILTER_GOOD: s += "good"; break;
|
|
case gfxPattern::FILTER_BEST: s += "best"; break;
|
|
case gfxPattern::FILTER_NEAREST: s += "nearest"; break;
|
|
case gfxPattern::FILTER_BILINEAR: s += "bilinear"; break;
|
|
case gfxPattern::FILTER_GAUSSIAN: s += "gaussian"; break;
|
|
default:
|
|
NS_ERROR("unknown filter type");
|
|
s += "???";
|
|
}
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, ViewID n,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
s.AppendInt(n);
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const gfxRGBA& c,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
s += nsPrintfCString(
|
|
"rgba(%d, %d, %d, %g)",
|
|
uint8_t(c.r*255.0), uint8_t(c.g*255.0), uint8_t(c.b*255.0), c.a);
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const gfx3DMatrix& m,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
if (m.IsIdentity())
|
|
s += "[ I ]";
|
|
else {
|
|
gfxMatrix matrix;
|
|
if (m.Is2D(&matrix)) {
|
|
s += nsPrintfCString(
|
|
"[ %g %g; %g %g; %g %g; ]",
|
|
matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0);
|
|
} else {
|
|
s += nsPrintfCString(
|
|
"[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; ]",
|
|
m._11, m._12, m._13, m._14,
|
|
m._21, m._22, m._23, m._24,
|
|
m._31, m._32, m._33, m._34,
|
|
m._41, m._42, m._43, m._44);
|
|
}
|
|
}
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const nsIntPoint& p,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
s += nsPrintfCString("(x=%d, y=%d)", p.x, p.y);
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const Point& p,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
s += nsPrintfCString("(x=%f, y=%f)", p.x, p.y);
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const nsIntRect& r,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
s += nsPrintfCString(
|
|
"(x=%d, y=%d, w=%d, h=%d)",
|
|
r.x, r.y, r.width, r.height);
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const Rect& r,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
s.AppendPrintf(
|
|
"(x=%f, y=%f, w=%f, h=%f)",
|
|
r.x, r.y, r.width, r.height);
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const nsIntRegion& r,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
|
|
nsIntRegionRectIterator it(r);
|
|
s += "< ";
|
|
while (const nsIntRect* sr = it.Next())
|
|
AppendToString(s, *sr) += "; ";
|
|
s += ">";
|
|
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const nsIntSize& sz,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
s += nsPrintfCString("(w=%d, h=%d)", sz.width, sz.height);
|
|
return s += sfx;
|
|
}
|
|
|
|
nsACString&
|
|
AppendToString(nsACString& s, const FrameMetrics& m,
|
|
const char* pfx="", const char* sfx="")
|
|
{
|
|
s += pfx;
|
|
AppendToString(s, m.mViewport, "{ viewport=");
|
|
AppendToString(s, m.mScrollOffset, " viewportScroll=");
|
|
AppendToString(s, m.mDisplayPort, " displayport=");
|
|
AppendToString(s, m.mScrollId, " scrollId=", " }");
|
|
return s += sfx;
|
|
}
|
|
|
|
} // namespace <anon>
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
//--------------------------------------------------
|
|
// LayerManager
|
|
Layer*
|
|
LayerManager::GetPrimaryScrollableLayer()
|
|
{
|
|
if (!mRoot) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsTArray<Layer*> queue;
|
|
queue.AppendElement(mRoot);
|
|
while (queue.Length()) {
|
|
ContainerLayer* containerLayer = queue[0]->AsContainerLayer();
|
|
queue.RemoveElementAt(0);
|
|
if (!containerLayer) {
|
|
continue;
|
|
}
|
|
|
|
const FrameMetrics& frameMetrics = containerLayer->GetFrameMetrics();
|
|
if (frameMetrics.IsScrollable()) {
|
|
return containerLayer;
|
|
}
|
|
|
|
Layer* child = containerLayer->GetFirstChild();
|
|
while (child) {
|
|
queue.AppendElement(child);
|
|
child = child->GetNextSibling();
|
|
}
|
|
}
|
|
|
|
return mRoot;
|
|
}
|
|
|
|
already_AddRefed<gfxASurface>
|
|
LayerManager::CreateOptimalSurface(const gfxIntSize &aSize,
|
|
gfxASurface::gfxImageFormat aFormat)
|
|
{
|
|
return gfxPlatform::GetPlatform()->
|
|
CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFormat));
|
|
}
|
|
|
|
already_AddRefed<gfxASurface>
|
|
LayerManager::CreateOptimalMaskSurface(const gfxIntSize &aSize)
|
|
{
|
|
return CreateOptimalSurface(aSize, gfxASurface::ImageFormatA8);
|
|
}
|
|
|
|
TemporaryRef<DrawTarget>
|
|
LayerManager::CreateDrawTarget(const IntSize &aSize,
|
|
SurfaceFormat aFormat)
|
|
{
|
|
return gfxPlatform::GetPlatform()->
|
|
CreateOffscreenDrawTarget(aSize, aFormat);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
LayerManager::Mutated(Layer* aLayer)
|
|
{
|
|
}
|
|
#endif // DEBUG
|
|
|
|
already_AddRefed<ImageContainer>
|
|
LayerManager::CreateImageContainer()
|
|
{
|
|
nsRefPtr<ImageContainer> container = new ImageContainer(ImageContainer::DISABLE_ASYNC);
|
|
return container.forget();
|
|
}
|
|
|
|
already_AddRefed<ImageContainer>
|
|
LayerManager::CreateAsynchronousImageContainer()
|
|
{
|
|
nsRefPtr<ImageContainer> container = new ImageContainer(ImageContainer::ENABLE_ASYNC);
|
|
return container.forget();
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// Layer
|
|
|
|
Layer::Layer(LayerManager* aManager, void* aImplData) :
|
|
mManager(aManager),
|
|
mParent(nullptr),
|
|
mNextSibling(nullptr),
|
|
mPrevSibling(nullptr),
|
|
mImplData(aImplData),
|
|
mMaskLayer(nullptr),
|
|
mPostXScale(1.0f),
|
|
mPostYScale(1.0f),
|
|
mOpacity(1.0),
|
|
mContentFlags(0),
|
|
mUseClipRect(false),
|
|
mUseTileSourceRect(false),
|
|
mIsFixedPosition(false),
|
|
mDebugColorIndex(0)
|
|
{}
|
|
|
|
Layer::~Layer()
|
|
{}
|
|
|
|
Animation*
|
|
Layer::AddAnimation(TimeStamp aStart, TimeDuration aDuration, float aIterations,
|
|
int aDirection, nsCSSProperty aProperty, const AnimationData& aData)
|
|
{
|
|
Animation* anim = mAnimations.AppendElement();
|
|
anim->startTime() = aStart;
|
|
anim->duration() = aDuration;
|
|
anim->numIterations() = aIterations;
|
|
anim->direction() = aDirection;
|
|
anim->property() = aProperty;
|
|
anim->data() = aData;
|
|
|
|
Mutated();
|
|
return anim;
|
|
}
|
|
|
|
void
|
|
Layer::ClearAnimations()
|
|
{
|
|
mAnimations.Clear();
|
|
mAnimationData.Clear();
|
|
Mutated();
|
|
}
|
|
|
|
static nsCSSValueList*
|
|
CreateCSSValueList(const InfallibleTArray<TransformFunction>& aFunctions)
|
|
{
|
|
nsAutoPtr<nsCSSValueList> result;
|
|
nsCSSValueList** resultTail = getter_Transfers(result);
|
|
for (uint32_t i = 0; i < aFunctions.Length(); i++) {
|
|
nsRefPtr<nsCSSValue::Array> arr;
|
|
switch (aFunctions[i].type()) {
|
|
case TransformFunction::TRotationX:
|
|
{
|
|
float theta = aFunctions[i].get_RotationX().radians();
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotatex, resultTail);
|
|
arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
|
|
break;
|
|
}
|
|
case TransformFunction::TRotationY:
|
|
{
|
|
float theta = aFunctions[i].get_RotationY().radians();
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotatey, resultTail);
|
|
arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
|
|
break;
|
|
}
|
|
case TransformFunction::TRotationZ:
|
|
{
|
|
float theta = aFunctions[i].get_RotationZ().radians();
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotatez, resultTail);
|
|
arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
|
|
break;
|
|
}
|
|
case TransformFunction::TRotation:
|
|
{
|
|
float theta = aFunctions[i].get_Rotation().radians();
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotate, resultTail);
|
|
arr->Item(1).SetFloatValue(theta, eCSSUnit_Radian);
|
|
break;
|
|
}
|
|
case TransformFunction::TRotation3D:
|
|
{
|
|
float x = aFunctions[i].get_Rotation3D().x();
|
|
float y = aFunctions[i].get_Rotation3D().y();
|
|
float z = aFunctions[i].get_Rotation3D().z();
|
|
float theta = aFunctions[i].get_Rotation3D().radians();
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_rotate3d, resultTail);
|
|
arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
|
|
arr->Item(2).SetFloatValue(y, eCSSUnit_Number);
|
|
arr->Item(3).SetFloatValue(z, eCSSUnit_Number);
|
|
arr->Item(4).SetFloatValue(theta, eCSSUnit_Radian);
|
|
break;
|
|
}
|
|
case TransformFunction::TScale:
|
|
{
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_scale3d, resultTail);
|
|
arr->Item(1).SetFloatValue(aFunctions[i].get_Scale().x(), eCSSUnit_Number);
|
|
arr->Item(2).SetFloatValue(aFunctions[i].get_Scale().y(), eCSSUnit_Number);
|
|
arr->Item(3).SetFloatValue(aFunctions[i].get_Scale().z(), eCSSUnit_Number);
|
|
break;
|
|
}
|
|
case TransformFunction::TTranslation:
|
|
{
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_translate3d, resultTail);
|
|
arr->Item(1).SetFloatValue(aFunctions[i].get_Translation().x(), eCSSUnit_Pixel);
|
|
arr->Item(2).SetFloatValue(aFunctions[i].get_Translation().y(), eCSSUnit_Pixel);
|
|
arr->Item(3).SetFloatValue(aFunctions[i].get_Translation().z(), eCSSUnit_Pixel);
|
|
break;
|
|
}
|
|
case TransformFunction::TSkewX:
|
|
{
|
|
float x = aFunctions[i].get_SkewX().x();
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_skewx, resultTail);
|
|
arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
|
|
break;
|
|
}
|
|
case TransformFunction::TSkewY:
|
|
{
|
|
float y = aFunctions[i].get_SkewY().y();
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_skewy, resultTail);
|
|
arr->Item(1).SetFloatValue(y, eCSSUnit_Number);
|
|
break;
|
|
}
|
|
case TransformFunction::TTransformMatrix:
|
|
{
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_matrix3d, resultTail);
|
|
const gfx3DMatrix& matrix = aFunctions[i].get_TransformMatrix().value();
|
|
arr->Item(1).SetFloatValue(matrix._11, eCSSUnit_Number);
|
|
arr->Item(2).SetFloatValue(matrix._12, eCSSUnit_Number);
|
|
arr->Item(3).SetFloatValue(matrix._13, eCSSUnit_Number);
|
|
arr->Item(4).SetFloatValue(matrix._14, eCSSUnit_Number);
|
|
arr->Item(5).SetFloatValue(matrix._21, eCSSUnit_Number);
|
|
arr->Item(6).SetFloatValue(matrix._22, eCSSUnit_Number);
|
|
arr->Item(7).SetFloatValue(matrix._23, eCSSUnit_Number);
|
|
arr->Item(8).SetFloatValue(matrix._24, eCSSUnit_Number);
|
|
arr->Item(9).SetFloatValue(matrix._31, eCSSUnit_Number);
|
|
arr->Item(10).SetFloatValue(matrix._32, eCSSUnit_Number);
|
|
arr->Item(11).SetFloatValue(matrix._33, eCSSUnit_Number);
|
|
arr->Item(12).SetFloatValue(matrix._34, eCSSUnit_Number);
|
|
arr->Item(13).SetFloatValue(matrix._41, eCSSUnit_Number);
|
|
arr->Item(14).SetFloatValue(matrix._42, eCSSUnit_Number);
|
|
arr->Item(15).SetFloatValue(matrix._43, eCSSUnit_Number);
|
|
arr->Item(16).SetFloatValue(matrix._44, eCSSUnit_Number);
|
|
break;
|
|
}
|
|
case TransformFunction::TPerspective:
|
|
{
|
|
float perspective = aFunctions[i].get_Perspective().value();
|
|
arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_perspective, resultTail);
|
|
arr->Item(1).SetFloatValue(perspective, eCSSUnit_Pixel);
|
|
break;
|
|
}
|
|
default:
|
|
NS_ASSERTION(false, "All functions should be implemented?");
|
|
}
|
|
}
|
|
if (aFunctions.Length() == 0) {
|
|
result = new nsCSSValueList();
|
|
result->mValue.SetNoneValue();
|
|
}
|
|
return result.forget();
|
|
}
|
|
|
|
void
|
|
Layer::SetAnimations(const AnimationArray& aAnimations)
|
|
{
|
|
mAnimations = aAnimations;
|
|
mAnimationData.Clear();
|
|
for (uint32_t i = 0; i < mAnimations.Length(); i++) {
|
|
AnimData* data = mAnimationData.AppendElement();
|
|
InfallibleTArray<css::ComputedTimingFunction*>& functions = data->mFunctions;
|
|
nsTArray<AnimationSegment> segments = mAnimations.ElementAt(i).segments();
|
|
for (uint32_t j = 0; j < segments.Length(); j++) {
|
|
TimingFunction tf = segments.ElementAt(j).sampleFn();
|
|
css::ComputedTimingFunction* ctf = new css::ComputedTimingFunction();
|
|
switch (tf.type()) {
|
|
case TimingFunction::TCubicBezierFunction: {
|
|
CubicBezierFunction cbf = tf.get_CubicBezierFunction();
|
|
ctf->Init(nsTimingFunction(cbf.x1(), cbf.y1(), cbf.x2(), cbf.y2()));
|
|
break;
|
|
}
|
|
default: {
|
|
NS_ASSERTION(tf.type() == TimingFunction::TStepFunction,
|
|
"Function must be bezier or step");
|
|
StepFunction sf = tf.get_StepFunction();
|
|
nsTimingFunction::Type type = sf.type() == 1 ? nsTimingFunction::StepStart
|
|
: nsTimingFunction::StepEnd;
|
|
ctf->Init(nsTimingFunction(type, sf.steps()));
|
|
break;
|
|
}
|
|
}
|
|
functions.AppendElement(ctf);
|
|
}
|
|
|
|
// Precompute the nsStyleAnimation::Values that we need if this is a transform
|
|
// animation.
|
|
InfallibleTArray<nsStyleAnimation::Value>& startValues = data->mStartValues;
|
|
InfallibleTArray<nsStyleAnimation::Value>& endValues = data->mEndValues;
|
|
for (uint32_t j = 0; j < mAnimations[i].segments().Length(); j++) {
|
|
const AnimationSegment& segment = mAnimations[i].segments()[j];
|
|
nsStyleAnimation::Value* startValue = startValues.AppendElement();
|
|
nsStyleAnimation::Value* endValue = endValues.AppendElement();
|
|
if (segment.endState().type() == Animatable::TArrayOfTransformFunction) {
|
|
const InfallibleTArray<TransformFunction>& startFunctions =
|
|
segment.startState().get_ArrayOfTransformFunction();
|
|
startValue->SetAndAdoptCSSValueListValue(CreateCSSValueList(startFunctions),
|
|
nsStyleAnimation::eUnit_Transform);
|
|
|
|
const InfallibleTArray<TransformFunction>& endFunctions =
|
|
segment.endState().get_ArrayOfTransformFunction();
|
|
endValue->SetAndAdoptCSSValueListValue(CreateCSSValueList(endFunctions),
|
|
nsStyleAnimation::eUnit_Transform);
|
|
} else {
|
|
NS_ASSERTION(segment.endState().type() == Animatable::Tfloat,
|
|
"Unknown Animatable type");
|
|
startValue->SetFloatValue(segment.startState().get_float());
|
|
endValue->SetFloatValue(segment.endState().get_float());
|
|
}
|
|
}
|
|
}
|
|
|
|
Mutated();
|
|
}
|
|
|
|
void
|
|
Layer::ApplyPendingUpdatesToSubtree()
|
|
{
|
|
ApplyPendingUpdatesForThisTransaction();
|
|
for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
|
|
child->ApplyPendingUpdatesToSubtree();
|
|
}
|
|
}
|
|
|
|
bool
|
|
Layer::CanUseOpaqueSurface()
|
|
{
|
|
// If the visible content in the layer is opaque, there is no need
|
|
// for an alpha channel.
|
|
if (GetContentFlags() & CONTENT_OPAQUE)
|
|
return true;
|
|
// Also, if this layer is the bottommost layer in a container which
|
|
// doesn't need an alpha channel, we can use an opaque surface for this
|
|
// layer too. Any transparent areas must be covered by something else
|
|
// in the container.
|
|
ContainerLayer* parent = GetParent();
|
|
return parent && parent->GetFirstChild() == this &&
|
|
parent->CanUseOpaqueSurface();
|
|
}
|
|
|
|
// NB: eventually these methods will be defined unconditionally, and
|
|
// can be moved into Layers.h
|
|
const nsIntRect*
|
|
Layer::GetEffectiveClipRect()
|
|
{
|
|
if (ShadowLayer* shadow = AsShadowLayer()) {
|
|
return shadow->GetShadowClipRect();
|
|
}
|
|
return GetClipRect();
|
|
}
|
|
|
|
const nsIntRegion&
|
|
Layer::GetEffectiveVisibleRegion()
|
|
{
|
|
if (ShadowLayer* shadow = AsShadowLayer()) {
|
|
return shadow->GetShadowVisibleRegion();
|
|
}
|
|
return GetVisibleRegion();
|
|
}
|
|
|
|
gfx3DMatrix
|
|
Layer::SnapTransform(const gfx3DMatrix& aTransform,
|
|
const gfxRect& aSnapRect,
|
|
gfxMatrix* aResidualTransform)
|
|
{
|
|
if (aResidualTransform) {
|
|
*aResidualTransform = gfxMatrix();
|
|
}
|
|
|
|
gfxMatrix matrix2D;
|
|
gfx3DMatrix result;
|
|
if (mManager->IsSnappingEffectiveTransforms() &&
|
|
aTransform.Is2D(&matrix2D) &&
|
|
matrix2D.HasNonIntegerTranslation() &&
|
|
!matrix2D.IsSingular() &&
|
|
!matrix2D.HasNonAxisAlignedTransform()) {
|
|
gfxMatrix snappedMatrix;
|
|
gfxPoint topLeft = matrix2D.Transform(aSnapRect.TopLeft());
|
|
topLeft.Round();
|
|
// first compute scale factors that scale aSnapRect to the snapped rect
|
|
if (aSnapRect.IsEmpty()) {
|
|
snappedMatrix.xx = matrix2D.xx;
|
|
snappedMatrix.yy = matrix2D.yy;
|
|
} else {
|
|
gfxPoint bottomRight = matrix2D.Transform(aSnapRect.BottomRight());
|
|
bottomRight.Round();
|
|
snappedMatrix.xx = (bottomRight.x - topLeft.x)/aSnapRect.Width();
|
|
snappedMatrix.yy = (bottomRight.y - topLeft.y)/aSnapRect.Height();
|
|
}
|
|
// compute translation factors that will move aSnapRect to the snapped rect
|
|
// given those scale factors
|
|
snappedMatrix.x0 = topLeft.x - aSnapRect.X()*snappedMatrix.xx;
|
|
snappedMatrix.y0 = topLeft.y - aSnapRect.Y()*snappedMatrix.yy;
|
|
result = gfx3DMatrix::From2D(snappedMatrix);
|
|
if (aResidualTransform && !snappedMatrix.IsSingular()) {
|
|
// set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
|
|
// (i.e., appying snappedMatrix after aResidualTransform gives the
|
|
// ideal transform.
|
|
gfxMatrix snappedMatrixInverse = snappedMatrix;
|
|
snappedMatrixInverse.Invert();
|
|
*aResidualTransform = matrix2D * snappedMatrixInverse;
|
|
}
|
|
} else {
|
|
result = aTransform;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
nsIntRect
|
|
Layer::CalculateScissorRect(const nsIntRect& aCurrentScissorRect,
|
|
const gfxMatrix* aWorldTransform)
|
|
{
|
|
ContainerLayer* container = GetParent();
|
|
NS_ASSERTION(container, "This can't be called on the root!");
|
|
|
|
// Establish initial clip rect: it's either the one passed in, or
|
|
// if the parent has an intermediate surface, it's the extents of that surface.
|
|
nsIntRect currentClip;
|
|
if (container->UseIntermediateSurface()) {
|
|
currentClip.SizeTo(container->GetIntermediateSurfaceRect().Size());
|
|
} else {
|
|
currentClip = aCurrentScissorRect;
|
|
}
|
|
|
|
const nsIntRect *clipRect = GetEffectiveClipRect();
|
|
if (!clipRect)
|
|
return currentClip;
|
|
|
|
if (clipRect->IsEmpty()) {
|
|
// We might have a non-translation transform in the container so we can't
|
|
// use the code path below.
|
|
return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0));
|
|
}
|
|
|
|
nsIntRect scissor = *clipRect;
|
|
if (!container->UseIntermediateSurface()) {
|
|
gfxMatrix matrix;
|
|
DebugOnly<bool> is2D = container->GetEffectiveTransform().Is2D(&matrix);
|
|
// See DefaultComputeEffectiveTransforms below
|
|
NS_ASSERTION(is2D && matrix.PreservesAxisAlignedRectangles(),
|
|
"Non preserves axis aligned transform with clipped child should have forced intermediate surface");
|
|
gfxRect r(scissor.x, scissor.y, scissor.width, scissor.height);
|
|
gfxRect trScissor = matrix.TransformBounds(r);
|
|
trScissor.Round();
|
|
if (!gfxUtils::GfxRectToIntRect(trScissor, &scissor)) {
|
|
return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0));
|
|
}
|
|
|
|
// Find the nearest ancestor with an intermediate surface
|
|
do {
|
|
container = container->GetParent();
|
|
} while (container && !container->UseIntermediateSurface());
|
|
}
|
|
if (container) {
|
|
scissor.MoveBy(-container->GetIntermediateSurfaceRect().TopLeft());
|
|
} else if (aWorldTransform) {
|
|
gfxRect r(scissor.x, scissor.y, scissor.width, scissor.height);
|
|
gfxRect trScissor = aWorldTransform->TransformBounds(r);
|
|
trScissor.Round();
|
|
if (!gfxUtils::GfxRectToIntRect(trScissor, &scissor))
|
|
return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0));
|
|
}
|
|
return currentClip.Intersect(scissor);
|
|
}
|
|
|
|
const gfx3DMatrix
|
|
Layer::GetTransform()
|
|
{
|
|
gfx3DMatrix transform = mTransform;
|
|
if (ContainerLayer* c = AsContainerLayer()) {
|
|
transform.Scale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
|
|
}
|
|
transform.ScalePost(mPostXScale, mPostYScale, 1.0f);
|
|
return transform;
|
|
}
|
|
|
|
const gfx3DMatrix
|
|
Layer::GetLocalTransform()
|
|
{
|
|
gfx3DMatrix transform;
|
|
if (ShadowLayer* shadow = AsShadowLayer())
|
|
transform = shadow->GetShadowTransform();
|
|
else
|
|
transform = mTransform;
|
|
if (ContainerLayer* c = AsContainerLayer()) {
|
|
transform.Scale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
|
|
}
|
|
transform.ScalePost(mPostXScale, mPostYScale, 1.0f);
|
|
return transform;
|
|
}
|
|
|
|
void
|
|
Layer::ApplyPendingUpdatesForThisTransaction()
|
|
{
|
|
if (mPendingTransform && *mPendingTransform != mTransform) {
|
|
mTransform = *mPendingTransform;
|
|
Mutated();
|
|
}
|
|
mPendingTransform = nullptr;
|
|
}
|
|
|
|
const float
|
|
Layer::GetLocalOpacity()
|
|
{
|
|
if (ShadowLayer* shadow = AsShadowLayer())
|
|
return shadow->GetShadowOpacity();
|
|
return mOpacity;
|
|
}
|
|
|
|
float
|
|
Layer::GetEffectiveOpacity()
|
|
{
|
|
float opacity = GetLocalOpacity();
|
|
for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface();
|
|
c = c->GetParent()) {
|
|
opacity *= c->GetLocalOpacity();
|
|
}
|
|
return opacity;
|
|
}
|
|
|
|
void
|
|
Layer::ComputeEffectiveTransformForMaskLayer(const gfx3DMatrix& aTransformToSurface)
|
|
{
|
|
if (mMaskLayer) {
|
|
mMaskLayer->mEffectiveTransform = aTransformToSurface;
|
|
|
|
#ifdef DEBUG
|
|
gfxMatrix maskTranslation;
|
|
bool maskIs2D = mMaskLayer->GetTransform().CanDraw2D(&maskTranslation);
|
|
NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
|
|
#endif
|
|
mMaskLayer->mEffectiveTransform.PreMultiply(mMaskLayer->GetTransform());
|
|
}
|
|
}
|
|
|
|
void
|
|
ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
|
|
{
|
|
aAttrs = ContainerLayerAttributes(GetFrameMetrics(), mPreXScale, mPreYScale);
|
|
}
|
|
|
|
bool
|
|
ContainerLayer::HasMultipleChildren()
|
|
{
|
|
uint32_t count = 0;
|
|
for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
|
|
const nsIntRect *clipRect = child->GetEffectiveClipRect();
|
|
if (clipRect && clipRect->IsEmpty())
|
|
continue;
|
|
if (child->GetVisibleRegion().IsEmpty())
|
|
continue;
|
|
++count;
|
|
if (count > 1)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray)
|
|
{
|
|
nsAutoTArray<Layer*, 10> toSort;
|
|
|
|
for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
|
|
ContainerLayer* container = l->AsContainerLayer();
|
|
if (container && container->GetContentFlags() & CONTENT_PRESERVE_3D) {
|
|
toSort.AppendElement(l);
|
|
} else {
|
|
if (toSort.Length() > 0) {
|
|
SortLayersBy3DZOrder(toSort);
|
|
aArray.MoveElementsFrom(toSort);
|
|
}
|
|
aArray.AppendElement(l);
|
|
}
|
|
}
|
|
if (toSort.Length() > 0) {
|
|
SortLayersBy3DZOrder(toSort);
|
|
aArray.MoveElementsFrom(toSort);
|
|
}
|
|
}
|
|
|
|
void
|
|
ContainerLayer::DefaultComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
|
|
{
|
|
gfxMatrix residual;
|
|
gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
|
|
idealTransform.ProjectTo2D();
|
|
mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), &residual);
|
|
|
|
bool useIntermediateSurface;
|
|
if (GetMaskLayer()) {
|
|
useIntermediateSurface = true;
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
} else if (gfxUtils::sDumpPainting) {
|
|
useIntermediateSurface = true;
|
|
#endif
|
|
} else {
|
|
float opacity = GetEffectiveOpacity();
|
|
if (opacity != 1.0f && HasMultipleChildren()) {
|
|
useIntermediateSurface = true;
|
|
} else {
|
|
useIntermediateSurface = false;
|
|
gfxMatrix contTransform;
|
|
if (!mEffectiveTransform.Is2D(&contTransform) ||
|
|
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
|
|
!contTransform.PreservesAxisAlignedRectangles()) {
|
|
#else
|
|
contTransform.HasNonIntegerTranslation()) {
|
|
#endif
|
|
for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
|
|
const nsIntRect *clipRect = child->GetEffectiveClipRect();
|
|
/* We can't (easily) forward our transform to children with a non-empty clip
|
|
* rect since it would need to be adjusted for the transform. See
|
|
* the calculations performed by CalculateScissorRect above.
|
|
* Nor for a child with a mask layer.
|
|
*/
|
|
if ((clipRect && !clipRect->IsEmpty() && !child->GetVisibleRegion().IsEmpty()) ||
|
|
child->GetMaskLayer()) {
|
|
useIntermediateSurface = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mUseIntermediateSurface = useIntermediateSurface;
|
|
if (useIntermediateSurface) {
|
|
ComputeEffectiveTransformsForChildren(gfx3DMatrix::From2D(residual));
|
|
} else {
|
|
ComputeEffectiveTransformsForChildren(idealTransform);
|
|
}
|
|
|
|
if (idealTransform.CanDraw2D()) {
|
|
ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
|
|
} else {
|
|
ComputeEffectiveTransformForMaskLayer(gfx3DMatrix());
|
|
}
|
|
}
|
|
|
|
void
|
|
ContainerLayer::ComputeEffectiveTransformsForChildren(const gfx3DMatrix& aTransformToSurface)
|
|
{
|
|
for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
|
|
l->ComputeEffectiveTransforms(aTransformToSurface);
|
|
}
|
|
}
|
|
|
|
void
|
|
ContainerLayer::DidRemoveChild(Layer* aLayer)
|
|
{
|
|
ThebesLayer* tl = aLayer->AsThebesLayer();
|
|
if (tl && tl->UsedForReadback()) {
|
|
for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
|
|
if (l->GetType() == TYPE_READBACK) {
|
|
static_cast<ReadbackLayer*>(l)->NotifyThebesLayerRemoved(tl);
|
|
}
|
|
}
|
|
}
|
|
if (aLayer->GetType() == TYPE_READBACK) {
|
|
static_cast<ReadbackLayer*>(aLayer)->NotifyRemoved();
|
|
}
|
|
}
|
|
|
|
void
|
|
ContainerLayer::DidInsertChild(Layer* aLayer)
|
|
{
|
|
if (aLayer->GetType() == TYPE_READBACK) {
|
|
mMayHaveReadbackChild = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
RefLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
|
|
{
|
|
aAttrs = RefLayerAttributes(GetReferentId());
|
|
}
|
|
|
|
void
|
|
LayerManager::StartFrameTimeRecording()
|
|
{
|
|
mLastFrameTime = TimeStamp::Now();
|
|
}
|
|
|
|
void
|
|
LayerManager::PostPresent()
|
|
{
|
|
if (!mLastFrameTime.IsNull()) {
|
|
TimeStamp now = TimeStamp::Now();
|
|
mFrameTimes.AppendElement((now - mLastFrameTime).ToMilliseconds());
|
|
mLastFrameTime = now;
|
|
}
|
|
if (!mTabSwitchStart.IsNull()) {
|
|
Telemetry::Accumulate(Telemetry::FX_TAB_SWITCH_TOTAL_MS,
|
|
uint32_t((TimeStamp::Now() - mTabSwitchStart).ToMilliseconds()));
|
|
mTabSwitchStart = TimeStamp();
|
|
}
|
|
}
|
|
|
|
nsTArray<float>
|
|
LayerManager::StopFrameTimeRecording()
|
|
{
|
|
mLastFrameTime = TimeStamp();
|
|
nsTArray<float> result = mFrameTimes;
|
|
mFrameTimes.Clear();
|
|
return result;
|
|
}
|
|
|
|
void
|
|
LayerManager::BeginTabSwitch()
|
|
{
|
|
mTabSwitchStart = TimeStamp::Now();
|
|
}
|
|
|
|
#ifdef MOZ_LAYERS_HAVE_LOG
|
|
|
|
static nsACString& PrintInfo(nsACString& aTo, ShadowLayer* aShadowLayer);
|
|
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
template <typename T>
|
|
void WriteSnapshotLinkToDumpFile(T* aObj, FILE* aFile)
|
|
{
|
|
nsCString string(aObj->Name());
|
|
string.Append("-");
|
|
string.AppendInt((uint64_t)aObj);
|
|
fprintf(aFile, "href=\"javascript:ViewImage('%s')\"", string.BeginReading());
|
|
}
|
|
|
|
template <typename T>
|
|
void WriteSnapshotToDumpFile_internal(T* aObj, gfxASurface* aSurf)
|
|
{
|
|
nsCString string(aObj->Name());
|
|
string.Append("-");
|
|
string.AppendInt((uint64_t)aObj);
|
|
if (gfxUtils::sDumpPaintFile)
|
|
fprintf(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading());
|
|
aSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile);
|
|
if (gfxUtils::sDumpPaintFile)
|
|
fprintf(gfxUtils::sDumpPaintFile, "\";");
|
|
}
|
|
|
|
void WriteSnapshotToDumpFile(Layer* aLayer, gfxASurface* aSurf)
|
|
{
|
|
WriteSnapshotToDumpFile_internal(aLayer, aSurf);
|
|
}
|
|
|
|
void WriteSnapshotToDumpFile(LayerManager* aManager, gfxASurface* aSurf)
|
|
{
|
|
WriteSnapshotToDumpFile_internal(aManager, aSurf);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
Layer::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml)
|
|
{
|
|
if (aDumpHtml) {
|
|
fprintf(aFile, "<li><a id=\"%p\" ", this);
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
if (GetType() == TYPE_CONTAINER || GetType() == TYPE_THEBES) {
|
|
WriteSnapshotLinkToDumpFile(this, aFile);
|
|
}
|
|
#endif
|
|
fprintf(aFile, ">");
|
|
}
|
|
DumpSelf(aFile, aPrefix);
|
|
if (aDumpHtml) {
|
|
fprintf(aFile, "</a>");
|
|
}
|
|
|
|
if (Layer* mask = GetMaskLayer()) {
|
|
nsAutoCString pfx(aPrefix);
|
|
pfx += " Mask layer: ";
|
|
mask->Dump(aFile, pfx.get());
|
|
}
|
|
|
|
if (Layer* kid = GetFirstChild()) {
|
|
nsAutoCString pfx(aPrefix);
|
|
pfx += " ";
|
|
if (aDumpHtml) {
|
|
fprintf(aFile, "<ul>");
|
|
}
|
|
kid->Dump(aFile, pfx.get());
|
|
if (aDumpHtml) {
|
|
fprintf(aFile, "</ul>");
|
|
}
|
|
}
|
|
|
|
if (aDumpHtml) {
|
|
fprintf(aFile, "</li>");
|
|
}
|
|
if (Layer* next = GetNextSibling())
|
|
next->Dump(aFile, aPrefix, aDumpHtml);
|
|
}
|
|
|
|
void
|
|
Layer::DumpSelf(FILE* aFile, const char* aPrefix)
|
|
{
|
|
nsAutoCString str;
|
|
PrintInfo(str, aPrefix);
|
|
fprintf(FILEOrDefault(aFile), "%s\n", str.get());
|
|
}
|
|
|
|
void
|
|
Layer::Log(const char* aPrefix)
|
|
{
|
|
if (!IsLogEnabled())
|
|
return;
|
|
|
|
LogSelf(aPrefix);
|
|
|
|
if (Layer* kid = GetFirstChild()) {
|
|
nsAutoCString pfx(aPrefix);
|
|
pfx += " ";
|
|
kid->Log(pfx.get());
|
|
}
|
|
|
|
if (Layer* next = GetNextSibling())
|
|
next->Log(aPrefix);
|
|
}
|
|
|
|
void
|
|
Layer::LogSelf(const char* aPrefix)
|
|
{
|
|
if (!IsLogEnabled())
|
|
return;
|
|
|
|
nsAutoCString str;
|
|
PrintInfo(str, aPrefix);
|
|
MOZ_LAYERS_LOG(("%s", str.get()));
|
|
}
|
|
|
|
nsACString&
|
|
Layer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{
|
|
aTo += aPrefix;
|
|
aTo += nsPrintfCString("%s%s (0x%p)", mManager->Name(), Name(), this);
|
|
|
|
::PrintInfo(aTo, AsShadowLayer());
|
|
|
|
if (mUseClipRect) {
|
|
AppendToString(aTo, mClipRect, " [clip=", "]");
|
|
}
|
|
if (1.0 != mPostXScale || 1.0 != mPostYScale) {
|
|
aTo.AppendPrintf(" [postScale=%g, %g]", mPostXScale, mPostYScale);
|
|
}
|
|
if (!mTransform.IsIdentity()) {
|
|
AppendToString(aTo, mTransform, " [transform=", "]");
|
|
}
|
|
if (!mVisibleRegion.IsEmpty()) {
|
|
AppendToString(aTo, mVisibleRegion, " [visible=", "]");
|
|
} else {
|
|
aTo += " [not visible]";
|
|
}
|
|
if (1.0 != mOpacity) {
|
|
aTo.AppendPrintf(" [opacity=%g]", mOpacity);
|
|
}
|
|
if (GetContentFlags() & CONTENT_OPAQUE) {
|
|
aTo += " [opaqueContent]";
|
|
}
|
|
if (GetContentFlags() & CONTENT_COMPONENT_ALPHA) {
|
|
aTo += " [componentAlpha]";
|
|
}
|
|
if (GetIsFixedPosition()) {
|
|
aTo += " [isFixedPosition]";
|
|
}
|
|
|
|
return aTo;
|
|
}
|
|
|
|
nsACString&
|
|
ThebesLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{
|
|
Layer::PrintInfo(aTo, aPrefix);
|
|
if (!mValidRegion.IsEmpty()) {
|
|
AppendToString(aTo, mValidRegion, " [valid=", "]");
|
|
}
|
|
return aTo;
|
|
}
|
|
|
|
nsACString&
|
|
ContainerLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{
|
|
Layer::PrintInfo(aTo, aPrefix);
|
|
if (!mFrameMetrics.IsDefault()) {
|
|
AppendToString(aTo, mFrameMetrics, " [metrics=", "]");
|
|
}
|
|
if (UseIntermediateSurface()) {
|
|
aTo += " [usesTmpSurf]";
|
|
}
|
|
if (1.0 != mPreXScale || 1.0 != mPreYScale) {
|
|
aTo.AppendPrintf(" [preScale=%g, %g]", mPreXScale, mPreYScale);
|
|
}
|
|
return aTo;
|
|
}
|
|
|
|
nsACString&
|
|
ColorLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{
|
|
Layer::PrintInfo(aTo, aPrefix);
|
|
AppendToString(aTo, mColor, " [color=", "]");
|
|
return aTo;
|
|
}
|
|
|
|
nsACString&
|
|
CanvasLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{
|
|
Layer::PrintInfo(aTo, aPrefix);
|
|
if (mFilter != gfxPattern::FILTER_GOOD) {
|
|
AppendToString(aTo, mFilter, " [filter=", "]");
|
|
}
|
|
return aTo;
|
|
}
|
|
|
|
nsACString&
|
|
ImageLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{
|
|
Layer::PrintInfo(aTo, aPrefix);
|
|
if (mFilter != gfxPattern::FILTER_GOOD) {
|
|
AppendToString(aTo, mFilter, " [filter=", "]");
|
|
}
|
|
return aTo;
|
|
}
|
|
|
|
nsACString&
|
|
RefLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{
|
|
ContainerLayer::PrintInfo(aTo, aPrefix);
|
|
if (0 != mId) {
|
|
AppendToString(aTo, mId, " [id=", "]");
|
|
}
|
|
return aTo;
|
|
}
|
|
|
|
nsACString&
|
|
ReadbackLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{
|
|
Layer::PrintInfo(aTo, aPrefix);
|
|
AppendToString(aTo, mSize, " [size=", "]");
|
|
if (mBackgroundLayer) {
|
|
AppendToString(aTo, mBackgroundLayer, " [backgroundLayer=", "]");
|
|
AppendToString(aTo, mBackgroundLayerOffset, " [backgroundOffset=", "]");
|
|
} else if (mBackgroundColor.a == 1.0) {
|
|
AppendToString(aTo, mBackgroundColor, " [backgroundColor=", "]");
|
|
} else {
|
|
aTo += " [nobackground]";
|
|
}
|
|
return aTo;
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// LayerManager
|
|
|
|
void
|
|
LayerManager::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml)
|
|
{
|
|
FILE* file = FILEOrDefault(aFile);
|
|
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
if (aDumpHtml) {
|
|
fprintf(file, "<ul><li><a ");
|
|
WriteSnapshotLinkToDumpFile(this, file);
|
|
fprintf(file, ">");
|
|
}
|
|
#endif
|
|
DumpSelf(file, aPrefix);
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
if (aDumpHtml) {
|
|
fprintf(file, "</a>");
|
|
}
|
|
#endif
|
|
|
|
nsAutoCString pfx(aPrefix);
|
|
pfx += " ";
|
|
if (!GetRoot()) {
|
|
fprintf(file, "%s(null)", pfx.get());
|
|
if (aDumpHtml) {
|
|
fprintf(file, "</li></ul>");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (aDumpHtml) {
|
|
fprintf(file, "<ul>");
|
|
}
|
|
GetRoot()->Dump(file, pfx.get(), aDumpHtml);
|
|
if (aDumpHtml) {
|
|
fprintf(file, "</ul></li></ul>");
|
|
}
|
|
fputc('\n', file);
|
|
}
|
|
|
|
void
|
|
LayerManager::DumpSelf(FILE* aFile, const char* aPrefix)
|
|
{
|
|
nsAutoCString str;
|
|
PrintInfo(str, aPrefix);
|
|
fprintf(FILEOrDefault(aFile), "%s\n", str.get());
|
|
}
|
|
|
|
void
|
|
LayerManager::Log(const char* aPrefix)
|
|
{
|
|
if (!IsLogEnabled())
|
|
return;
|
|
|
|
LogSelf(aPrefix);
|
|
|
|
nsAutoCString pfx(aPrefix);
|
|
pfx += " ";
|
|
if (!GetRoot()) {
|
|
MOZ_LAYERS_LOG(("%s(null)", pfx.get()));
|
|
return;
|
|
}
|
|
|
|
GetRoot()->Log(pfx.get());
|
|
}
|
|
|
|
void
|
|
LayerManager::LogSelf(const char* aPrefix)
|
|
{
|
|
nsAutoCString str;
|
|
PrintInfo(str, aPrefix);
|
|
MOZ_LAYERS_LOG(("%s", str.get()));
|
|
}
|
|
|
|
nsACString&
|
|
LayerManager::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{
|
|
aTo += aPrefix;
|
|
return aTo += nsPrintfCString("%sLayerManager (0x%p)", Name(), this);
|
|
}
|
|
|
|
/*static*/ void
|
|
LayerManager::InitLog()
|
|
{
|
|
if (!sLog)
|
|
sLog = PR_NewLogModule("Layers");
|
|
}
|
|
|
|
/*static*/ bool
|
|
LayerManager::IsLogEnabled()
|
|
{
|
|
NS_ABORT_IF_FALSE(!!sLog,
|
|
"layer manager must be created before logging is allowed");
|
|
return PR_LOG_TEST(sLog, PR_LOG_DEBUG);
|
|
}
|
|
|
|
static nsACString&
|
|
PrintInfo(nsACString& aTo, ShadowLayer* aShadowLayer)
|
|
{
|
|
if (!aShadowLayer) {
|
|
return aTo;
|
|
}
|
|
if (const nsIntRect* clipRect = aShadowLayer->GetShadowClipRect()) {
|
|
AppendToString(aTo, *clipRect, " [shadow-clip=", "]");
|
|
}
|
|
if (!aShadowLayer->GetShadowTransform().IsIdentity()) {
|
|
AppendToString(aTo, aShadowLayer->GetShadowTransform(), " [shadow-transform=", "]");
|
|
}
|
|
if (!aShadowLayer->GetShadowVisibleRegion().IsEmpty()) {
|
|
AppendToString(aTo, aShadowLayer->GetShadowVisibleRegion(), " [shadow-visible=", "]");
|
|
}
|
|
return aTo;
|
|
}
|
|
|
|
#else // !MOZ_LAYERS_HAVE_LOG
|
|
|
|
void Layer::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml) {}
|
|
void Layer::DumpSelf(FILE* aFile, const char* aPrefix) {}
|
|
void Layer::Log(const char* aPrefix) {}
|
|
void Layer::LogSelf(const char* aPrefix) {}
|
|
nsACString&
|
|
Layer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{ return aTo; }
|
|
|
|
nsACString&
|
|
ThebesLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{ return aTo; }
|
|
|
|
nsACString&
|
|
ContainerLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{ return aTo; }
|
|
|
|
nsACString&
|
|
ColorLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{ return aTo; }
|
|
|
|
nsACString&
|
|
CanvasLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{ return aTo; }
|
|
|
|
nsACString&
|
|
ImageLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{ return aTo; }
|
|
|
|
nsACString&
|
|
RefLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{ return aTo; }
|
|
|
|
nsACString&
|
|
ReadbackLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{ return aTo; }
|
|
|
|
void LayerManager::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml) {}
|
|
void LayerManager::DumpSelf(FILE* aFile, const char* aPrefix) {}
|
|
void LayerManager::Log(const char* aPrefix) {}
|
|
void LayerManager::LogSelf(const char* aPrefix) {}
|
|
|
|
nsACString&
|
|
LayerManager::PrintInfo(nsACString& aTo, const char* aPrefix)
|
|
{ return aTo; }
|
|
|
|
/*static*/ void LayerManager::InitLog() {}
|
|
/*static*/ bool LayerManager::IsLogEnabled() { return false; }
|
|
|
|
#endif // MOZ_LAYERS_HAVE_LOG
|
|
|
|
PRLogModuleInfo* LayerManager::sLog;
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|