Bug 579276. Part 1: Change layer/layermanager user data API to use keys and values. r=cjones,sr=vlad
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -503,31 +503,31 @@ WebGLContext::GetThebesSurface(gfxASurfa
static PRUint8 gWebGLLayerUserData;
already_AddRefed<layers::CanvasLayer>
WebGLContext::GetCanvasLayer(CanvasLayer *aOldLayer,
LayerManager *aManager)
{
if (!mResetLayer && aOldLayer &&
- aOldLayer->GetUserData() == &gWebGLLayerUserData) {
+ aOldLayer->HasUserData(&gWebGLLayerUserData)) {
NS_ADDREF(aOldLayer);
if (mInvalidated) {
aOldLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
mInvalidated = PR_FALSE;
}
return aOldLayer;
}
nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
if (!canvasLayer) {
NS_WARNING("CreateCanvasLayer returned null!");
return nsnull;
}
- canvasLayer->SetUserData(&gWebGLLayerUserData);
+ canvasLayer->SetUserData(&gWebGLLayerUserData, nsnull);
CanvasLayer::Data data;
// the gl context may either provide a native PBuffer, in which case we want to initialize
// data with the gl context directly, or may provide a surface to which it renders (this is the case
// of OSMesa contexts), in which case we want to initialize data with that surface.
void* native_surface = gl->GetNativeData(gl::GLContext::NativeImageSurface);
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -4149,29 +4149,29 @@ static PRUint8 g2DContextLayerUserData;
already_AddRefed<CanvasLayer>
nsCanvasRenderingContext2D::GetCanvasLayer(CanvasLayer *aOldLayer,
LayerManager *aManager)
{
if (!mValid)
return nsnull;
if (!mResetLayer && aOldLayer &&
- aOldLayer->GetUserData() == &g2DContextLayerUserData) {
+ aOldLayer->HasUserData(&g2DContextLayerUserData)) {
NS_ADDREF(aOldLayer);
// XXX Need to just update the changed area here
aOldLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
return aOldLayer;
}
nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
if (!canvasLayer) {
NS_WARNING("CreateCanvasLayer returned null!");
return nsnull;
}
- canvasLayer->SetUserData(&g2DContextLayerUserData);
+ canvasLayer->SetUserData(&g2DContextLayerUserData, nsnull);
CanvasLayer::Data data;
data.mSurface = mSurface.get();
data.mSize = nsIntSize(mWidth, mHeight);
canvasLayer->Initialize(data);
canvasLayer->SetIsOpaqueContent(mOpaque);
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -78,16 +78,24 @@ class ColorLayer;
class ImageContainer;
class CanvasLayer;
class SpecificLayerAttributes;
#define MOZ_LAYER_DECL_NAME(n, e) \
virtual const char* Name() const { return n; } \
virtual LayerType GetType() const { return e; }
+/**
+ * Base class for userdata objects attached to layers and layer managers.
+ */
+class THEBES_API LayerUserData {
+public:
+ virtual ~LayerUserData() {}
+};
+
/*
* Motivation: For truly smooth animation and video playback, we need to
* be able to compose frames and render them on a dedicated thread (i.e.
* off the main thread where DOM manipulation, script execution and layout
* induce difficult-to-bound latency). This requires Gecko to construct
* some kind of persistent scene structure (graph or tree) that can be
* safely transmitted across threads. We have other scenarios (e.g. mobile
* browsing) where retaining some rendered data between paints is desired
@@ -105,16 +113,62 @@ class SpecificLayerAttributes;
* Gecko used it before layers were introduced). But we also don't want
* to have bifurcated "layers"/"non-layers" rendering paths in Gecko.
* Therefore the layers API is carefully designed to permit maximally
* efficient implementation in an "immediate mode" style. See the
* BasicLayerManager for such an implementation.
*/
/**
+ * Helper class to manage user data for layers and LayerManagers.
+ */
+class THEBES_API LayerUserDataSet {
+public:
+ LayerUserDataSet() : mKey(nsnull) {}
+
+ void Set(void* aKey, LayerUserData* aValue)
+ {
+ NS_ASSERTION(!mKey || mKey == aKey,
+ "Multiple LayerUserData objects not supported");
+ mKey = aKey;
+ mValue = aValue;
+ }
+ /**
+ * This can be used anytime. Ownership passes to the caller!
+ */
+ LayerUserData* Remove(void* aKey)
+ {
+ if (mKey == aKey) {
+ mKey = nsnull;
+ LayerUserData* d = mValue.forget();
+ return d;
+ }
+ return nsnull;
+ }
+ /**
+ * This getter can be used anytime.
+ */
+ PRBool Has(void* aKey)
+ {
+ return mKey == aKey;
+ }
+ /**
+ * This getter can be used anytime. Ownership is retained by this object.
+ */
+ LayerUserData* Get(void* aKey)
+ {
+ return mKey == aKey ? mValue.get() : nsnull;
+ }
+
+private:
+ void* mKey;
+ nsAutoPtr<LayerUserData> mValue;
+};
+
+/**
* A LayerManager controls a tree of layers. All layers in the tree
* must use the same LayerManager.
*
* All modifications to a layer tree must happen inside a transaction.
* Only the state of the layer tree at the end of a transaction is
* rendered. Transactions cannot be nested
*
* Each transaction has two phases:
@@ -137,17 +191,17 @@ class THEBES_API LayerManager {
public:
enum LayersBackend {
LAYERS_BASIC = 0,
LAYERS_OPENGL,
LAYERS_D3D9
};
- LayerManager() : mUserData(nsnull), mDestroyed(PR_FALSE)
+ LayerManager() : mDestroyed(PR_FALSE)
{
InitLog();
}
virtual ~LayerManager() {}
/**
* Release layers and resources held by this layer manager, and mark
* it as destroyed. Should do any cleanup necessary in preparation
@@ -262,20 +316,38 @@ public:
/**
* Type of layer manager his is. This is to be used sparsely in order to
* avoid a lot of Layers backend specific code. It should be used only when
* Layers backend specific functionality is necessary.
*/
virtual LayersBackend GetBackendType() = 0;
- // This setter and getter can be used anytime. The user data is initially
- // null.
- void SetUserData(void* aData) { mUserData = aData; }
- void* GetUserData() { return mUserData; }
+ /**
+ * This setter can be used anytime. The user data for all keys is
+ * initially null. Ownership pases to the layer manager.
+ */
+ void SetUserData(void* aKey, LayerUserData* aData)
+ { mUserData.Set(aKey, aData); }
+ /**
+ * This can be used anytime. Ownership passes to the caller!
+ */
+ nsAutoPtr<LayerUserData> RemoveUserData(void* aKey)
+ { nsAutoPtr<LayerUserData> d(mUserData.Remove(aKey)); return d; }
+ /**
+ * This getter can be used anytime.
+ */
+ PRBool HasUserData(void* aKey)
+ { return mUserData.Has(aKey); }
+ /**
+ * This getter can be used anytime. Ownership is retained by the layer
+ * manager.
+ */
+ LayerUserData* GetUserData(void* aKey)
+ { return mUserData.Get(aKey); }
// We always declare the following logging symbols, because it's
// extremely tricky to conditionally declare them. However, for
// ifndef MOZ_LAYERS_HAVE_LOG builds, they only have trivial
// definitions in Layers.cpp.
virtual const char* Name() const { return "???"; }
/**
@@ -300,17 +372,17 @@ public:
*/
void LogSelf(const char* aPrefix="");
static bool IsLogEnabled();
static PRLogModuleInfo* GetLog() { return sLog; }
protected:
nsRefPtr<Layer> mRoot;
- void* mUserData;
+ LayerUserDataSet mUserData;
PRPackedBool mDestroyed;
// Print interesting information about this into aTo. Internally
// used to implement Dump*() and Log*().
virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
static void InitLog();
static PRLogModuleInfo* sLog;
@@ -455,20 +527,38 @@ public:
// Returns true if it's OK to save the contents of aLayer in an
// opaque surface (a surface without an alpha channel).
// If we can use a surface without an alpha channel, we should, because
// it will often make painting of antialiased text faster and higher
// quality.
PRBool CanUseOpaqueSurface();
- // This setter and getter can be used anytime. The user data is initially
- // null.
- void SetUserData(void* aData) { mUserData = aData; }
- void* GetUserData() { return mUserData; }
+ /**
+ * This setter can be used anytime. The user data for all keys is
+ * initially null. Ownership pases to the layer manager.
+ */
+ void SetUserData(void* aKey, LayerUserData* aData)
+ { mUserData.Set(aKey, aData); }
+ /**
+ * This can be used anytime. Ownership passes to the caller!
+ */
+ nsAutoPtr<LayerUserData> RemoveUserData(void* aKey)
+ { nsAutoPtr<LayerUserData> d(mUserData.Remove(aKey)); return d; }
+ /**
+ * This getter can be used anytime.
+ */
+ PRBool HasUserData(void* aKey)
+ { return mUserData.Has(aKey); }
+ /**
+ * This getter can be used anytime. Ownership is retained by the layer
+ * manager.
+ */
+ LayerUserData* GetUserData(void* aKey)
+ { return mUserData.Get(aKey); }
/**
* Dynamic downcast to a Thebes layer. Returns null if this is not
* a ThebesLayer.
*/
virtual ThebesLayer* AsThebesLayer() { return nsnull; }
virtual const char* Name() const =0;
@@ -514,17 +604,16 @@ public:
protected:
Layer(LayerManager* aManager, void* aImplData) :
mManager(aManager),
mParent(nsnull),
mNextSibling(nsnull),
mPrevSibling(nsnull),
mImplData(aImplData),
- mUserData(nsnull),
mOpacity(1.0),
mUseClipRect(PR_FALSE),
mIsOpaqueContent(PR_FALSE)
{}
void Mutated() { mManager->Mutated(this); }
// Print interesting information about this into aTo. Internally
@@ -534,17 +623,17 @@ protected:
// appends additional info to aTo.
virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
LayerManager* mManager;
ContainerLayer* mParent;
Layer* mNextSibling;
Layer* mPrevSibling;
void* mImplData;
- void* mUserData;
+ LayerUserDataSet mUserData;
nsIntRegion mVisibleRegion;
gfx3DMatrix mTransform;
float mOpacity;
nsIntRect mClipRect;
PRPackedBool mUseClipRect;
PRPackedBool mIsOpaqueContent;
};
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -52,24 +52,29 @@ using namespace mozilla::layers;
namespace mozilla {
namespace {
/**
* This is the userdata we associate with a layer manager.
*/
-class LayerManagerData {
+class LayerManagerData : public LayerUserData {
public:
LayerManagerData() :
mInvalidateAllThebesContent(PR_FALSE),
mInvalidateAllLayers(PR_FALSE)
{
+ MOZ_COUNT_CTOR(LayerManagerData);
+
mFramesWithLayers.Init();
}
+ ~LayerManagerData() {
+ MOZ_COUNT_DTOR(LayerManagerData);
+ }
/**
* Tracks which frames have layers associated with them.
*/
nsTHashtable<nsPtrHashKey<nsIFrame> > mFramesWithLayers;
PRPackedBool mInvalidateAllThebesContent;
PRPackedBool mInvalidateAllLayers;
};
@@ -299,28 +304,36 @@ protected:
nsTArray<nsRefPtr<ColorLayer> > mRecycledColorLayers;
PRUint32 mNextFreeRecycledThebesLayer;
PRUint32 mNextFreeRecycledColorLayer;
PRPackedBool mInvalidateAllThebesContent;
};
/**
* The address of gThebesDisplayItemLayerUserData is used as the user
- * data pointer for ThebesLayers created by FrameLayerBuilder.
+ * data key for ThebesLayers created by FrameLayerBuilder.
* It identifies ThebesLayers used to draw non-layer content, which are
* therefore eligible for recycling. We want display items to be able to
* create their own dedicated ThebesLayers in BuildLayer, if necessary,
* and we wouldn't want to accidentally recycle those.
+ * The user data is null.
*/
-static PRUint8 gThebesDisplayItemLayerUserData;
+PRUint8 gThebesDisplayItemLayerUserData;
/**
* The address of gColorLayerUserData is used as the user
- * data pointer for ColorLayers
+ * data key for ColorLayers created by FrameLayerBuilder.
+ * The user data is null.
*/
-static PRUint8 gColorLayerUserData;
+PRUint8 gColorLayerUserData;
+/**
+ * The address of gLayerManagerUserData is used as the user
+ * data key for retained LayerManagers managed by FrameLayerBuilder.
+ * The user data is a LayerManagerData.
+ */
+PRUint8 gLayerManagerUserData;
} // anonymous namespace
PRBool
FrameLayerBuilder::DisplayItemDataEntry::HasContainerLayer()
{
for (PRUint32 i = 0; i < mData.Length(); ++i) {
if (mData[i].mLayer->GetType() == Layer::TYPE_CONTAINER)
@@ -337,22 +350,21 @@ FrameLayerBuilder::InternalDestroyDispla
nsRefPtr<LayerManager> managerRef;
nsTArray<DisplayItemData>* array =
reinterpret_cast<nsTArray<DisplayItemData>*>(&aPropertyValue);
NS_ASSERTION(!array->IsEmpty(), "Empty arrays should not be stored");
if (aRemoveFromFramesWithLayers) {
LayerManager* manager = array->ElementAt(0).mLayer->Manager();
LayerManagerData* data = static_cast<LayerManagerData*>
- (manager->GetUserData());
+ (manager->GetUserData(&gLayerManagerUserData));
NS_ASSERTION(data, "Frame with layer should have been recorded");
data->mFramesWithLayers.RemoveEntry(aFrame);
if (data->mFramesWithLayers.Count() == 0) {
- delete data;
- manager->SetUserData(nsnull);
+ manager->RemoveUserData(&gLayerManagerUserData);
// Consume the reference we added when we set the user data
// in DidEndTransaction. But don't actually release until we've
// released all the layers in the DisplayItemData array below!
managerRef = manager;
NS_RELEASE(manager);
}
}
@@ -366,17 +378,17 @@ FrameLayerBuilder::DestroyDisplayItemDat
InternalDestroyDisplayItemData(aFrame, aPropertyValue, PR_TRUE);
}
void
FrameLayerBuilder::WillBeginRetainedLayerTransaction(LayerManager* aManager)
{
mRetainingManager = aManager;
LayerManagerData* data = static_cast<LayerManagerData*>
- (aManager->GetUserData());
+ (aManager->GetUserData(&gLayerManagerUserData));
if (data) {
mInvalidateAllThebesContent = data->mInvalidateAllThebesContent;
mInvalidateAllLayers = data->mInvalidateAllLayers;
}
}
/**
* A helper function to remove the mThebesLayerItems entries for every
@@ -415,23 +427,23 @@ FrameLayerBuilder::WillEndTransaction(La
return;
// We need to save the data we'll need to support retaining. We do this
// before we paint so that invalidation triggered by painting will
// be able to update the ThebesLayerInvalidRegionProperty values
// correctly and the NS_FRAME_HAS_CONTAINER_LAYER bits will be set
// correctly.
LayerManagerData* data = static_cast<LayerManagerData*>
- (mRetainingManager->GetUserData());
+ (mRetainingManager->GetUserData(&gLayerManagerUserData));
if (data) {
// Update all the frames that used to have layers.
data->mFramesWithLayers.EnumerateEntries(UpdateDisplayItemDataForFrame, this);
} else {
data = new LayerManagerData();
- mRetainingManager->SetUserData(data);
+ mRetainingManager->SetUserData(&gLayerManagerUserData, data);
// Addref mRetainingManager. We'll release it when 'data' is
// removed.
NS_ADDREF(mRetainingManager);
}
// Now go through all the frames that didn't have any retained
// display items before, and record those retained display items.
// This also empties mNewDisplayItemData.
mNewDisplayItemData.EnumerateEntries(StoreNewDisplayItemData, data);
@@ -590,17 +602,17 @@ ContainerState::CreateOrRecycleColorLaye
// reapply any necessary clipping.
layer->SetClipRect(nsnull);
} else {
// Create a new layer
layer = mManager->CreateColorLayer();
if (!layer)
return nsnull;
// Mark this layer as being used for Thebes-painting display items
- layer->SetUserData(&gColorLayerUserData);
+ layer->SetUserData(&gColorLayerUserData, nsnull);
}
return layer.forget();
}
already_AddRefed<ThebesLayer>
ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
{
// We need a new thebes layer
@@ -632,17 +644,17 @@ ContainerState::CreateOrRecycleThebesLay
// InvalidateAllThebesLayerContents has ensured
// the area is invalidated in the widget.
} else {
// Create a new thebes layer
layer = mManager->CreateThebesLayer();
if (!layer)
return nsnull;
// Mark this layer as being used for Thebes-painting display items
- layer->SetUserData(&gThebesDisplayItemLayerUserData);
+ layer->SetUserData(&gThebesDisplayItemLayerUserData, nsnull);
}
// Set up transform so that 0,0 in the Thebes layer corresponds to the
// (pixel-snapped) top-left of the aActiveScrolledRoot.
nsPoint offset = mBuilder->ToReferenceFrame(aActiveScrolledRoot);
nsIntPoint pixOffset = offset.ToNearestPixels(
aActiveScrolledRoot->PresContext()->AppUnitsPerDevPixel());
gfxMatrix matrix;
@@ -960,17 +972,17 @@ ContainerState::ProcessDisplayItems(cons
nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager);
if (!ownLayer) {
InvalidateForLayerChange(item, ownLayer);
continue;
}
// Update that layer's clip and visible rects.
NS_ASSERTION(ownLayer->Manager() == mManager, "Wrong manager");
- NS_ASSERTION(ownLayer->GetUserData() != &gThebesDisplayItemLayerUserData,
+ NS_ASSERTION(!ownLayer->HasUserData(&gLayerManagerUserData),
"We shouldn't have a FrameLayerBuilder-managed layer here!");
// It has its own layer. Update that layer's clip and visible rects.
if (aClipRect) {
ownLayer->IntersectClipRect(
aClipRect->ToNearestPixels(appUnitsPerDevPixel));
}
ThebesLayerData* data = GetTopThebesLayerData();
if (data) {
@@ -1114,20 +1126,19 @@ FrameLayerBuilder::AddLayerDisplayItem(L
}
}
void
ContainerState::CollectOldLayers()
{
for (Layer* layer = mContainerLayer->GetFirstChild(); layer;
layer = layer->GetNextSibling()) {
- void* data = layer->GetUserData();
- if (data == &gColorLayerUserData) {
+ if (layer->HasUserData(&gColorLayerUserData)) {
mRecycledColorLayers.AppendElement(static_cast<ColorLayer*>(layer));
- } else if (data == &gThebesDisplayItemLayerUserData) {
+ } else if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
NS_ASSERTION(layer->AsThebesLayer(), "Wrong layer type");
mRecycledThebesLayers.AppendElement(static_cast<ThebesLayer*>(layer));
}
}
}
void
ContainerState::Finish()
@@ -1198,17 +1209,17 @@ FrameLayerBuilder::BuildContainerLayerFo
aContainerItem->GetUnderlyingFrame() == aContainerFrame,
"Container display item must match given frame");
nsRefPtr<ContainerLayer> containerLayer;
if (aManager == mRetainingManager) {
Layer* oldLayer = GetOldLayerFor(aContainerFrame, containerDisplayItemKey);
if (oldLayer) {
NS_ASSERTION(oldLayer->Manager() == aManager, "Wrong manager");
- if (oldLayer->GetUserData() == &gThebesDisplayItemLayerUserData) {
+ if (oldLayer->HasUserData(&gThebesDisplayItemLayerUserData)) {
// The old layer for this item is actually our ThebesLayer
// because we rendered its layer into that ThebesLayer. So we
// don't actually have a retained container layer.
} else {
NS_ASSERTION(oldLayer->GetType() == Layer::TYPE_CONTAINER,
"Wrong layer type");
containerLayer = static_cast<ContainerLayer*>(oldLayer);
// Clear clip rect; the caller will set it if necessary.
@@ -1271,17 +1282,17 @@ FrameLayerBuilder::GetLeafLayerFor(nsDis
if (aManager != mRetainingManager)
return nsnull;
nsIFrame* f = aItem->GetUnderlyingFrame();
NS_ASSERTION(f, "Can only call GetLeafLayerFor on items that have a frame");
Layer* layer = GetOldLayerFor(f, aItem->GetPerFrameKey());
if (!layer)
return nsnull;
- if (layer->GetUserData() == &gThebesDisplayItemLayerUserData) {
+ if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
// This layer was created to render Thebes-rendered content for this
// display item. The display item should not use it for its own
// layer rendering.
return nsnull;
}
// Clear clip rect; the caller is responsible for setting it.
layer->SetClipRect(nsnull);
return layer;
@@ -1347,27 +1358,27 @@ FrameLayerBuilder::InvalidateThebesLayer
{
InternalInvalidateThebesLayersInSubtree(aFrame);
}
/* static */ void
FrameLayerBuilder::InvalidateAllThebesLayerContents(LayerManager* aManager)
{
LayerManagerData* data = static_cast<LayerManagerData*>
- (aManager->GetUserData());
+ (aManager->GetUserData(&gLayerManagerUserData));
if (data) {
data->mInvalidateAllThebesContent = PR_TRUE;
}
}
/* static */ void
FrameLayerBuilder::InvalidateAllLayers(LayerManager* aManager)
{
LayerManagerData* data = static_cast<LayerManagerData*>
- (aManager->GetUserData());
+ (aManager->GetUserData(&gLayerManagerUserData));
if (data) {
data->mInvalidateAllLayers = PR_TRUE;
}
}
/* static */
PRBool
FrameLayerBuilder::HasDedicatedLayer(nsIFrame* aFrame, PRUint32 aDisplayItemKey)
@@ -1375,19 +1386,19 @@ FrameLayerBuilder::HasDedicatedLayer(nsI
void* propValue = aFrame->Properties().Get(DisplayItemDataProperty());
if (!propValue)
return PR_FALSE;
nsTArray<DisplayItemData>* array =
(reinterpret_cast<nsTArray<DisplayItemData>*>(&propValue));
for (PRUint32 i = 0; i < array->Length(); ++i) {
if (array->ElementAt(i).mDisplayItemKey == aDisplayItemKey) {
- void* layerUserData = array->ElementAt(i).mLayer->GetUserData();
- if (layerUserData != &gColorLayerUserData &&
- layerUserData != &gThebesDisplayItemLayerUserData)
+ Layer* layer = array->ElementAt(i).mLayer;
+ if (!layer->HasUserData(&gColorLayerUserData) &&
+ !layer->HasUserData(&gThebesDisplayItemLayerUserData))
return PR_TRUE;
}
}
return PR_FALSE;
}
/* static */ void
FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer,