diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 5156d37beec..b780c50d1da 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -314,6 +314,13 @@ public: bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; } + /** + * Returns true if this LayerManager can properly support layers with + * SURFACE_COMPONENT_ALPHA. This can include disabling component + * alpha if required. + */ + virtual bool AreComponentAlphaLayersEnabled() { return true; } + /** * CONSTRUCTION PHASE ONLY * Set the root layer. The root layer is initially null. If there is diff --git a/gfx/layers/basic/BasicLayers.h b/gfx/layers/basic/BasicLayers.h index 3beb55e8497..33a52ee8319 100644 --- a/gfx/layers/basic/BasicLayers.h +++ b/gfx/layers/basic/BasicLayers.h @@ -95,6 +95,8 @@ public: void* aCallbackData, EndTransactionFlags aFlags = END_DEFAULT); + virtual bool AreComponentAlphaLayersEnabled() { return HasShadowManager(); } + virtual void SetRoot(Layer* aLayer); virtual already_AddRefed CreateThebesLayer(); diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 39e571b3b00..1466981dcc3 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -89,6 +89,10 @@ public: CollectOldLayers(); } + enum ProcessDisplayItemsFlags { + NO_COMPONENT_ALPHA = 0x01, + }; + /** * This is the method that actually walks a display list and builds * the child layers. We invoke it recursively to process clipped sublists. @@ -96,7 +100,8 @@ public: * if no clipping is required */ void ProcessDisplayItems(const nsDisplayList& aList, - FrameLayerBuilder::Clip& aClip); + FrameLayerBuilder::Clip& aClip, + PRUint32 aFlags); /** * This finalizes all the open ThebesLayers by popping every element off * mThebesLayerDataStack, then sets the children of the container layer @@ -729,8 +734,11 @@ FrameLayerBuilder::UpdateDisplayItemDataForFrame(DisplayItemDataEntry* aEntry, LayerManagerData* managerData = static_cast (builder->GetRetainingLayerManager()->GetUserData(&gLayerManagerUserData)); LayerManagerData* data = static_cast(props.Get(LayerManagerDataProperty())); - if (!newDisplayItems) { + if (!newDisplayItems || newDisplayItems->mData.IsEmpty()) { // This frame was visible, but isn't anymore. + if (newDisplayItems) { + builder->mNewDisplayItemData.RawRemoveEntry(newDisplayItems); + } if (data == managerData) { props.Remove(LayerManagerDataProperty()); } @@ -1697,7 +1705,8 @@ PaintInactiveLayer(nsDisplayListBuilder* aBuilder, */ void ContainerState::ProcessDisplayItems(const nsDisplayList& aList, - FrameLayerBuilder::Clip& aClip) + FrameLayerBuilder::Clip& aClip, + PRUint32 aFlags) { SAMPLE_LABEL("ContainerState", "ProcessDisplayItems"); for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) { @@ -1705,7 +1714,7 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList, if (type == nsDisplayItem::TYPE_CLIP || type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) { FrameLayerBuilder::Clip childClip(aClip, item); - ProcessDisplayItems(*item->GetList(), childClip); + ProcessDisplayItems(*item->GetList(), childClip, aFlags); continue; } @@ -1727,13 +1736,22 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList, LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters); - nsIFrame* activeScrolledRoot = - nsLayoutUtils::GetActiveScrolledRootFor(item, mBuilder); + nsIFrame* activeScrolledRoot; + bool forceInactive = false; + if (aFlags & NO_COMPONENT_ALPHA) { + activeScrolledRoot = + nsLayoutUtils::GetActiveScrolledRootFor(mContainerFrame, + mBuilder->ReferenceFrame()); + forceInactive = true; + } else { + activeScrolledRoot = nsLayoutUtils::GetActiveScrolledRootFor(item, mBuilder); + } // Assign the item to a layer if (layerState == LAYER_ACTIVE_FORCE || - layerState == LAYER_ACTIVE_EMPTY || - layerState == LAYER_ACTIVE) { + (!forceInactive && + (layerState == LAYER_ACTIVE_EMPTY || + layerState == LAYER_ACTIVE))) { // LAYER_ACTIVE_EMPTY means the layer is created just for its metadata. // We should never see an empty layer with any visible content! @@ -1976,10 +1994,12 @@ FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer, ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer); if (entry) { entry->mContainerLayerFrame = aContainerLayerFrame; + entry->mContainerLayerGeneration = mContainerLayerGeneration; NS_ASSERTION(aItem->GetUnderlyingFrame(), "Must have frame"); if (tempManager) { FrameLayerBuilder* layerBuilder = new FrameLayerBuilder(); layerBuilder->Init(mDisplayListBuilder); + layerBuilder->mMaxContainerLayerGeneration = mMaxContainerLayerGeneration; // LayerManager user data took ownership of the FrameLayerBuilder tempManager->SetUserData(&gLayerManagerLayerBuilder, new LayerManagerLayerBuilder(layerBuilder, true)); @@ -2001,11 +2021,12 @@ FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer, // If BuildLayer didn't call BuildContainerLayerFor, then our new layer won't have been // stored in layerBuilder. Manually add it now. - DisplayItemData data(layer, aItem->GetPerFrameKey(), LAYER_ACTIVE); + DisplayItemData data(layer, aItem->GetPerFrameKey(), LAYER_ACTIVE, mContainerLayerGeneration); layerBuilder->StoreDataForFrame(aItem->GetUnderlyingFrame(), data); tempManager->SetRoot(layer); layerBuilder->WillEndTransaction(); + mMaxContainerLayerGeneration = layerBuilder->mMaxContainerLayerGeneration; nsIntRect invalid = props->ComputeDifferences(layer, nsnull); if (aLayerState == LAYER_SVG_EFFECTS) { @@ -2015,7 +2036,8 @@ FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer, aLayer->InvalidateRegion(invalid); } ClippedDisplayItem* cdi = - entry->mItems.AppendElement(ClippedDisplayItem(aItem, aClip)); + entry->mItems.AppendElement(ClippedDisplayItem(aItem, aClip, + mContainerLayerGeneration)); cdi->mInactiveLayer = tempManager; } } @@ -2029,6 +2051,7 @@ FrameLayerBuilder::StoreDataForFrame(nsIFrame* aFrame, DisplayItemData& aData) } entry = mNewDisplayItemData.PutEntry(aFrame); if (entry) { + entry->mContainerLayerGeneration = mContainerLayerGeneration; DisplayItemData *data = entry->mData.AppendElement(); *data = aData; } @@ -2066,8 +2089,9 @@ FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer, } } #endif + entry->mContainerLayerGeneration = mContainerLayerGeneration; DisplayItemData* data = entry->mData.AppendElement(); - DisplayItemData did(aLayer, aItem->GetPerFrameKey(), aLayerState); + DisplayItemData did(aLayer, aItem->GetPerFrameKey(), aLayerState, mContainerLayerGeneration); *data = did; ThebesLayer *t = aLayer->AsThebesLayer(); @@ -2092,8 +2116,11 @@ nsIntPoint FrameLayerBuilder::GetLastPaintOffset(ThebesLayer* aLayer) { ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer); - if (entry && entry->mHasExplicitLastPaintOffset) - return entry->mLastPaintOffset; + if (entry) { + entry->mContainerLayerGeneration = mContainerLayerGeneration; + if (entry->mHasExplicitLastPaintOffset) + return entry->mLastPaintOffset; + } return GetTranslationForThebesLayer(aLayer); } @@ -2102,6 +2129,7 @@ FrameLayerBuilder::SaveLastPaintOffset(ThebesLayer* aLayer) { ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer); if (entry) { + entry->mContainerLayerGeneration = mContainerLayerGeneration; entry->mLastPaintOffset = GetTranslationForThebesLayer(aLayer); entry->mHasExplicitLastPaintOffset = true; } @@ -2279,6 +2307,44 @@ ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder, return result; } +/* static */ PLDHashOperator +FrameLayerBuilder::RestoreDisplayItemData(DisplayItemDataEntry* aEntry, void* aUserArg) +{ + PRUint32 *generation = static_cast(aUserArg); + + if (aEntry->mContainerLayerGeneration >= *generation) { + return PL_DHASH_REMOVE; + } + + for (PRUint32 i = 0; i < aEntry->mData.Length(); i++) { + if (aEntry->mData[i].mContainerLayerGeneration >= *generation) { + aEntry->mData.TruncateLength(i); + return PL_DHASH_NEXT; + } + } + + return PL_DHASH_NEXT; +} + +/* static */ PLDHashOperator +FrameLayerBuilder::RestoreThebesLayerItemEntries(ThebesLayerItemsEntry* aEntry, void* aUserArg) +{ + PRUint32 *generation = static_cast(aUserArg); + + if (aEntry->mContainerLayerGeneration >= *generation) { + return PL_DHASH_REMOVE; + } + + for (PRUint32 i = 0; i < aEntry->mItems.Length(); i++) { + if (aEntry->mItems[i].mContainerLayerGeneration >= *generation) { + aEntry->mItems.TruncateLength(i); + return PL_DHASH_NEXT; + } + } + + return PL_DHASH_NEXT; +} + already_AddRefed FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder, LayerManager* aManager, @@ -2333,34 +2399,66 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder, ContainerParameters scaleParameters = ChooseScaleAndSetTransform(this, aContainerFrame, aTransform, aParameters, containerLayer); - ContainerState state(aBuilder, aManager, GetLayerBuilderForManager(aManager), - aContainerFrame, containerLayer, scaleParameters); + + PRUint32 oldGeneration = mContainerLayerGeneration; + mContainerLayerGeneration = ++mMaxContainerLayerGeneration; + + LayerManagerData* data = static_cast + (aManager->GetUserData(&gLayerManagerUserData)); if (mRetainingManager) { DisplayItemDataEntry* entry = mNewDisplayItemData.PutEntry(aContainerFrame); if (entry) { DisplayItemData *data = entry->mData.AppendElement(); DisplayItemData did(containerLayer, containerDisplayItemKey, - LAYER_ACTIVE); + LAYER_ACTIVE, mContainerLayerGeneration); *data = did; + entry->mContainerLayerGeneration = mContainerLayerGeneration; } } - Clip clip; - state.ProcessDisplayItems(aChildren, clip); - - LayerManagerData* data = static_cast - (aManager->GetUserData(&gLayerManagerUserData)); + nsRect bounds; + nsIntRect pixBounds; + PRInt32 appUnitsPerDevPixel; - // Set CONTENT_COMPONENT_ALPHA if any of our children have it. - // This is suboptimal ... a child could have text that's over transparent - // pixels in its own layer, but over opaque parts of previous siblings. + PRUint32 stateFlags = + (aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) ? + ContainerState::NO_COMPONENT_ALPHA : 0; PRUint32 flags; - state.Finish(&flags, data); + while (true) { + ContainerState state(aBuilder, aManager, GetLayerBuilderForManager(aManager), + aContainerFrame, containerLayer, scaleParameters); + Clip clip; + state.ProcessDisplayItems(aChildren, clip, stateFlags); + + // Set CONTENT_COMPONENT_ALPHA if any of our children have it. + // This is suboptimal ... a child could have text that's over transparent + // pixels in its own layer, but over opaque parts of previous siblings. + state.Finish(&flags, data); + bounds = state.GetChildrenBounds(); + pixBounds = state.ScaleToOutsidePixels(bounds, false); + appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel(); + + if ((flags & Layer::CONTENT_COMPONENT_ALPHA) && + mRetainingManager && + !mRetainingManager->AreComponentAlphaLayersEnabled() && + !stateFlags) { + // Since we don't want any component alpha layers on BasicLayers, we repeat + // the layer building process with this explicitely forced off. + // We restore the previous FrameLayerBuilder state since the first set + // of layer building will have changed it. + stateFlags = ContainerState::NO_COMPONENT_ALPHA; + mNewDisplayItemData.EnumerateEntries(RestoreDisplayItemData, + &mContainerLayerGeneration); + mThebesLayerItems.EnumerateEntries(RestoreThebesLayerItemEntries, + &mContainerLayerGeneration); + aContainerFrame->AddStateBits(NS_FRAME_NO_COMPONENT_ALPHA); + continue; + } + break; + } - nsRect bounds = state.GetChildrenBounds(); NS_ASSERTION(bounds.IsEqualInterior(aChildren.GetBounds(aBuilder)), "Wrong bounds"); - nsIntRect pixBounds = state.ScaleToOutsidePixels(bounds, false); if (aContainerItem) { nsIntRect itemVisibleRect = aContainerItem->GetVisibleRect().ToOutsidePixels(AppUnitsPerDevPixel(aContainerItem)); @@ -2372,7 +2470,7 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder, // we won't paint if (aChildren.IsOpaque() && !aChildren.NeedsTransparentSurface()) { bounds.ScaleRoundIn(scaleParameters.mXScale, scaleParameters.mYScale); - if (bounds.Contains(pixBounds.ToAppUnits(state.GetAppUnitsPerDevPixel()))) { + if (bounds.Contains(pixBounds.ToAppUnits(appUnitsPerDevPixel))) { // Clear CONTENT_COMPONENT_ALPHA flags = Layer::CONTENT_OPAQUE; } @@ -2381,6 +2479,7 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder, containerLayer->SetUserData(&gNotifySubDocInvalidationData, nsnull); + mContainerLayerGeneration = oldGeneration; return containerLayer.forget(); } diff --git a/layout/base/FrameLayerBuilder.h b/layout/base/FrameLayerBuilder.h index 7ee0892ad92..78fb378fdd0 100644 --- a/layout/base/FrameLayerBuilder.h +++ b/layout/base/FrameLayerBuilder.h @@ -110,7 +110,9 @@ public: FrameLayerBuilder() : mRetainingManager(nsnull), mDetectedDOMModification(false), - mInvalidateAllLayers(false) + mInvalidateAllLayers(false), + mContainerLayerGeneration(0), + mMaxContainerLayerGeneration(0) { MOZ_COUNT_CTOR(FrameLayerBuilder); mNewDisplayItemData.Init(); @@ -443,8 +445,13 @@ protected: */ class DisplayItemData { public: - DisplayItemData(Layer* aLayer, PRUint32 aKey, LayerState aLayerState) - : mLayer(aLayer), mDisplayItemKey(aKey), mLayerState(aLayerState), mUsed(false) {} + DisplayItemData(Layer* aLayer, PRUint32 aKey, LayerState aLayerState, PRUint32 aGeneration) + : mLayer(aLayer) + , mDisplayItemKey(aKey) + , mContainerLayerGeneration(aGeneration) + , mLayerState(aLayerState) + , mUsed(false) + {} DisplayItemData() : mUsed(false) @@ -457,6 +464,7 @@ protected: mInactiveManager = toCopy.mInactiveManager; mGeometry = toCopy.mGeometry; mDisplayItemKey = toCopy.mDisplayItemKey; + mContainerLayerGeneration = toCopy.mContainerLayerGeneration; mLayerState = toCopy.mLayerState; mUsed = toCopy.mUsed; } @@ -465,6 +473,7 @@ protected: nsRefPtr mInactiveManager; nsAutoPtr mGeometry; PRUint32 mDisplayItemKey; + PRUint32 mContainerLayerGeneration; LayerState mLayerState; /** @@ -495,12 +504,14 @@ protected: // This isn't actually a copy-constructor; notice that it steals toCopy's // array. Be careful. mData.SwapElements(toCopy.mData); + mContainerLayerGeneration = toCopy.mContainerLayerGeneration; } ~DisplayItemDataEntry() { MOZ_COUNT_DTOR(DisplayItemDataEntry); } bool HasNonEmptyContainerLayer(); nsAutoTArray mData; + PRUint32 mContainerLayerGeneration; enum { ALLOW_MEMMOVE = false }; }; @@ -555,8 +566,8 @@ protected: * mItem always has an underlying frame. */ struct ClippedDisplayItem { - ClippedDisplayItem(nsDisplayItem* aItem, const Clip& aClip) - : mItem(aItem), mClip(aClip) + ClippedDisplayItem(nsDisplayItem* aItem, const Clip& aClip, PRUint32 aGeneration) + : mItem(aItem), mClip(aClip), mContainerLayerGeneration(aGeneration) { } @@ -572,6 +583,7 @@ protected: nsRefPtr mInactiveLayer; Clip mClip; + PRUint32 mContainerLayerGeneration; }; /** @@ -595,6 +607,7 @@ public: // The translation set on this ThebesLayer before we started updating the // layer tree. nsIntPoint mLastPaintOffset; + PRUint32 mContainerLayerGeneration; bool mHasExplicitLastPaintOffset; /** * The first mCommonClipCount rounded rectangle clips are identical for @@ -625,6 +638,12 @@ protected: static PLDHashOperator StoreNewDisplayItemData(DisplayItemDataEntry* aEntry, void* aUserArg); + static PLDHashOperator RestoreDisplayItemData(DisplayItemDataEntry* aEntry, + void *aUserArg); + + static PLDHashOperator RestoreThebesLayerItemEntries(ThebesLayerItemsEntry* aEntry, + void *aUserArg); + /** * Returns true if the DOM has been modified since we started painting, * in which case we should bail out and not paint anymore. This should @@ -670,6 +689,9 @@ protected: * during this paint. */ bool mInvalidateAllLayers; + + PRUint32 mContainerLayerGeneration; + PRUint32 mMaxContainerLayerGeneration; }; } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 01537c12b3f..3e3f753d4fa 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -2051,7 +2051,7 @@ public: LayerManager* aManager, const ContainerParameters& aParameters) { - return mozilla::LAYER_ACTIVE; + return mozilla::LAYER_ACTIVE_FORCE; } virtual bool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index bbaf7834edc..fc06f7c3dbd 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1635,6 +1635,10 @@ PresShell::InitialReflow(nscoord aWidth, nscoord aHeight) return NS_ERROR_OUT_OF_MEMORY; } + for (nsIFrame* f = rootFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { + f->RemoveStateBits(NS_FRAME_NO_COMPONENT_ALPHA); + } + Element *root = mDocument->GetRootElement(); if (root) { diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index edede0e2faa..f376fe5ccfa 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -239,6 +239,11 @@ typedef PRUint64 nsFrameState; // This bit acts as a loop flag for recursive paint server drawing. #define NS_FRAME_DRAWING_AS_PAINTSERVER NS_FRAME_STATE_BIT(33) +// This bit is set on frames that create ContainerLayers with component +// alpha children. With BasicLayers we avoid creating these, so we mark +// the frames for future reference. +#define NS_FRAME_NO_COMPONENT_ALPHA NS_FRAME_STATE_BIT(34) + // Frame's overflow area was clipped by the 'clip' property. #define NS_FRAME_HAS_CLIP NS_FRAME_STATE_BIT(35) diff --git a/layout/ipc/RenderFrameParent.h b/layout/ipc/RenderFrameParent.h index b516c1d631b..4974974de68 100644 --- a/layout/ipc/RenderFrameParent.h +++ b/layout/ipc/RenderFrameParent.h @@ -131,7 +131,7 @@ public: virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerParameters& aParameters) - { return mozilla::LAYER_ACTIVE; } + { return mozilla::LAYER_ACTIVE_FORCE; } NS_OVERRIDE virtual already_AddRefed