summaryrefslogtreecommitdiffstats
path: root/layout/base/FrameLayerBuilder.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /layout/base/FrameLayerBuilder.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/base/FrameLayerBuilder.cpp')
-rw-r--r--layout/base/FrameLayerBuilder.cpp6378
1 files changed, 6378 insertions, 0 deletions
diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp
new file mode 100644
index 000000000..9269c2ab6
--- /dev/null
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -0,0 +1,6378 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/DebugOnly.h"
+
+#include "FrameLayerBuilder.h"
+
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+#include "mozilla/gfx/Matrix.h"
+#include "ActiveLayerTracker.h"
+#include "BasicLayers.h"
+#include "DisplayItemScrollClip.h"
+#include "ImageContainer.h"
+#include "ImageLayers.h"
+#include "LayerTreeInvalidation.h"
+#include "Layers.h"
+#include "LayerUserData.h"
+#include "MaskLayerImageCache.h"
+#include "UnitTransforms.h"
+#include "Units.h"
+#include "gfx2DGlue.h"
+#include "gfxEnv.h"
+#include "gfxUtils.h"
+#include "nsAutoPtr.h"
+#include "nsAnimationManager.h"
+#include "nsDisplayList.h"
+#include "nsDocShell.h"
+#include "nsIScrollableFrame.h"
+#include "nsImageFrame.h"
+#include "nsLayoutUtils.h"
+#include "nsPresContext.h"
+#include "nsPrintfCString.h"
+#include "nsRenderingContext.h"
+#include "nsSVGIntegrationUtils.h"
+#include "nsTransitionManager.h"
+#include "mozilla/LayerTimelineMarker.h"
+
+#include "mozilla/EffectCompositor.h"
+#include "mozilla/Move.h"
+#include "mozilla/ReverseIterator.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureWrapperImage.h"
+#include "mozilla/Unused.h"
+#include "GeckoProfiler.h"
+#include "LayersLogging.h"
+#include "gfxPrefs.h"
+
+#include <algorithm>
+
+using namespace mozilla::layers;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+
+class PaintedDisplayItemLayerUserData;
+
+static nsTHashtable<nsPtrHashKey<FrameLayerBuilder::DisplayItemData>>* sAliveDisplayItemDatas;
+
+/**
+ * The address of gPaintedDisplayItemLayerUserData is used as the user
+ * data key for PaintedLayers created by FrameLayerBuilder.
+ * It identifies PaintedLayers used to draw non-layer content, which are
+ * therefore eligible for recycling. We want display items to be able to
+ * create their own dedicated PaintedLayers in BuildLayer, if necessary,
+ * and we wouldn't want to accidentally recycle those.
+ * The user data is a PaintedDisplayItemLayerUserData.
+ */
+uint8_t gPaintedDisplayItemLayerUserData;
+/**
+ * The address of gColorLayerUserData is used as the user
+ * data key for ColorLayers created by FrameLayerBuilder.
+ * The user data is null.
+ */
+uint8_t gColorLayerUserData;
+/**
+ * The address of gImageLayerUserData is used as the user
+ * data key for ImageLayers created by FrameLayerBuilder.
+ * The user data is null.
+ */
+uint8_t gImageLayerUserData;
+/**
+ * The address of gLayerManagerUserData is used as the user
+ * data key for retained LayerManagers managed by FrameLayerBuilder.
+ * The user data is a LayerManagerData.
+ */
+uint8_t gLayerManagerUserData;
+/**
+ * The address of gMaskLayerUserData is used as the user
+ * data key for mask layers managed by FrameLayerBuilder.
+ * The user data is a MaskLayerUserData.
+ */
+uint8_t gMaskLayerUserData;
+/**
+ * The address of gCSSMaskLayerUserData is used as the user
+ * data key for mask layers of css masking managed by FrameLayerBuilder.
+ * The user data is a CSSMaskLayerUserData.
+ */
+uint8_t gCSSMaskLayerUserData;
+
+// a global cache of image containers used for mask layers
+static MaskLayerImageCache* gMaskLayerImageCache = nullptr;
+
+static inline MaskLayerImageCache* GetMaskLayerImageCache()
+{
+ if (!gMaskLayerImageCache) {
+ gMaskLayerImageCache = new MaskLayerImageCache();
+ }
+
+ return gMaskLayerImageCache;
+}
+
+FrameLayerBuilder::FrameLayerBuilder()
+ : mRetainingManager(nullptr)
+ , mDetectedDOMModification(false)
+ , mInvalidateAllLayers(false)
+ , mInLayerTreeCompressionMode(false)
+ , mContainerLayerGeneration(0)
+ , mMaxContainerLayerGeneration(0)
+{
+ MOZ_COUNT_CTOR(FrameLayerBuilder);
+}
+
+FrameLayerBuilder::~FrameLayerBuilder()
+{
+ GetMaskLayerImageCache()->Sweep();
+ MOZ_COUNT_DTOR(FrameLayerBuilder);
+}
+
+FrameLayerBuilder::DisplayItemData::DisplayItemData(LayerManagerData* aParent, uint32_t aKey,
+ Layer* aLayer, nsIFrame* aFrame)
+
+ : mParent(aParent)
+ , mLayer(aLayer)
+ , mDisplayItemKey(aKey)
+ , mItem(nullptr)
+ , mUsed(true)
+ , mIsInvalid(false)
+{
+ MOZ_COUNT_CTOR(FrameLayerBuilder::DisplayItemData);
+
+ if (!sAliveDisplayItemDatas) {
+ sAliveDisplayItemDatas = new nsTHashtable<nsPtrHashKey<FrameLayerBuilder::DisplayItemData>>();
+ }
+ MOZ_RELEASE_ASSERT(!sAliveDisplayItemDatas->Contains(this));
+ sAliveDisplayItemDatas->PutEntry(this);
+
+ MOZ_RELEASE_ASSERT(mLayer);
+ if (aFrame) {
+ AddFrame(aFrame);
+ }
+
+}
+
+void
+FrameLayerBuilder::DisplayItemData::AddFrame(nsIFrame* aFrame)
+{
+ MOZ_RELEASE_ASSERT(mLayer);
+ mFrameList.AppendElement(aFrame);
+
+ nsTArray<DisplayItemData*>* array =
+ aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty());
+ if (!array) {
+ array = new nsTArray<DisplayItemData*>();
+ aFrame->Properties().Set(FrameLayerBuilder::LayerManagerDataProperty(), array);
+ }
+ array->AppendElement(this);
+}
+
+void
+FrameLayerBuilder::DisplayItemData::RemoveFrame(nsIFrame* aFrame)
+{
+ MOZ_RELEASE_ASSERT(mLayer);
+ bool result = mFrameList.RemoveElement(aFrame);
+ MOZ_RELEASE_ASSERT(result, "Can't remove a frame that wasn't added!");
+
+ nsTArray<DisplayItemData*>* array =
+ aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty());
+ MOZ_RELEASE_ASSERT(array, "Must be already stored on the frame!");
+ array->RemoveElement(this);
+}
+
+void
+FrameLayerBuilder::DisplayItemData::EndUpdate()
+{
+ MOZ_RELEASE_ASSERT(mLayer);
+ MOZ_ASSERT(!mItem);
+ mIsInvalid = false;
+ mUsed = false;
+}
+
+void
+FrameLayerBuilder::DisplayItemData::EndUpdate(nsAutoPtr<nsDisplayItemGeometry> aGeometry)
+{
+ MOZ_RELEASE_ASSERT(mLayer);
+ MOZ_ASSERT(mItem);
+ MOZ_ASSERT(mGeometry || aGeometry);
+
+ if (aGeometry) {
+ mGeometry = aGeometry;
+ }
+ mClip = mItem->GetClip();
+ mFrameListChanges.Clear();
+
+ mItem = nullptr;
+ EndUpdate();
+}
+
+void
+FrameLayerBuilder::DisplayItemData::BeginUpdate(Layer* aLayer, LayerState aState,
+ uint32_t aContainerLayerGeneration,
+ nsDisplayItem* aItem /* = nullptr */)
+{
+ MOZ_RELEASE_ASSERT(mLayer);
+ MOZ_RELEASE_ASSERT(aLayer);
+ mLayer = aLayer;
+ mOptLayer = nullptr;
+ mInactiveManager = nullptr;
+ mLayerState = aState;
+ mContainerLayerGeneration = aContainerLayerGeneration;
+ mUsed = true;
+
+ if (aLayer->AsPaintedLayer()) {
+ mItem = aItem;
+ }
+
+ if (!aItem) {
+ return;
+ }
+
+ // We avoid adding or removing element unnecessarily
+ // since we have to modify userdata each time
+ AutoTArray<nsIFrame*, 4> copy(mFrameList);
+ if (!copy.RemoveElement(aItem->Frame())) {
+ AddFrame(aItem->Frame());
+ mFrameListChanges.AppendElement(aItem->Frame());
+ }
+
+ AutoTArray<nsIFrame*,4> mergedFrames;
+ aItem->GetMergedFrames(&mergedFrames);
+ for (uint32_t i = 0; i < mergedFrames.Length(); ++i) {
+ if (!copy.RemoveElement(mergedFrames[i])) {
+ AddFrame(mergedFrames[i]);
+ mFrameListChanges.AppendElement(mergedFrames[i]);
+ }
+ }
+
+ for (uint32_t i = 0; i < copy.Length(); i++) {
+ RemoveFrame(copy[i]);
+ mFrameListChanges.AppendElement(copy[i]);
+ }
+}
+
+static const nsIFrame* sDestroyedFrame = nullptr;
+FrameLayerBuilder::DisplayItemData::~DisplayItemData()
+{
+ MOZ_COUNT_DTOR(FrameLayerBuilder::DisplayItemData);
+ MOZ_RELEASE_ASSERT(mLayer);
+ for (uint32_t i = 0; i < mFrameList.Length(); i++) {
+ nsIFrame* frame = mFrameList[i];
+ if (frame == sDestroyedFrame) {
+ continue;
+ }
+ nsTArray<DisplayItemData*> *array =
+ reinterpret_cast<nsTArray<DisplayItemData*>*>(frame->Properties().Get(LayerManagerDataProperty()));
+ array->RemoveElement(this);
+ }
+
+ MOZ_RELEASE_ASSERT(sAliveDisplayItemDatas && sAliveDisplayItemDatas->Contains(this));
+ sAliveDisplayItemDatas->RemoveEntry(this);
+ if (sAliveDisplayItemDatas->Count() == 0) {
+ delete sAliveDisplayItemDatas;
+ sAliveDisplayItemDatas = nullptr;
+ }
+}
+
+void
+FrameLayerBuilder::DisplayItemData::ClearAnimationCompositorState()
+{
+ if (mDisplayItemKey != nsDisplayItem::TYPE_TRANSFORM &&
+ mDisplayItemKey != nsDisplayItem::TYPE_OPACITY) {
+ return;
+ }
+
+ for (nsIFrame* frame : mFrameList) {
+ nsCSSPropertyID prop = mDisplayItemKey == nsDisplayItem::TYPE_TRANSFORM ?
+ eCSSProperty_transform : eCSSProperty_opacity;
+ EffectCompositor::ClearIsRunningOnCompositor(frame, prop);
+ }
+}
+
+const nsTArray<nsIFrame*>&
+FrameLayerBuilder::DisplayItemData::GetFrameListChanges()
+{
+ return mFrameListChanges;
+}
+
+/**
+ * This is the userdata we associate with a layer manager.
+ */
+class LayerManagerData : public LayerUserData {
+public:
+ explicit LayerManagerData(LayerManager *aManager)
+ : mLayerManager(aManager)
+#ifdef DEBUG_DISPLAY_ITEM_DATA
+ , mParent(nullptr)
+#endif
+ , mInvalidateAllLayers(false)
+ {
+ MOZ_COUNT_CTOR(LayerManagerData);
+ }
+ ~LayerManagerData() {
+ MOZ_COUNT_DTOR(LayerManagerData);
+ }
+
+#ifdef DEBUG_DISPLAY_ITEM_DATA
+ void Dump(const char *aPrefix = "") {
+ printf_stderr("%sLayerManagerData %p\n", aPrefix, this);
+
+ for (auto iter = mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
+ FrameLayerBuilder::DisplayItemData* data = iter.Get()->GetKey();
+
+ nsAutoCString prefix;
+ prefix += aPrefix;
+ prefix += " ";
+
+ const char* layerState;
+ switch (data->mLayerState) {
+ case LAYER_NONE:
+ layerState = "LAYER_NONE"; break;
+ case LAYER_INACTIVE:
+ layerState = "LAYER_INACTIVE"; break;
+ case LAYER_ACTIVE:
+ layerState = "LAYER_ACTIVE"; break;
+ case LAYER_ACTIVE_FORCE:
+ layerState = "LAYER_ACTIVE_FORCE"; break;
+ case LAYER_ACTIVE_EMPTY:
+ layerState = "LAYER_ACTIVE_EMPTY"; break;
+ case LAYER_SVG_EFFECTS:
+ layerState = "LAYER_SVG_EFFECTS"; break;
+ }
+ uint32_t mask = (1 << nsDisplayItem::TYPE_BITS) - 1;
+
+ nsAutoCString str;
+ str += prefix;
+ str += nsPrintfCString("Frame %p ", data->mFrameList[0]);
+ str += nsDisplayItem::DisplayItemTypeName(static_cast<nsDisplayItem::Type>(data->mDisplayItemKey & mask));
+ if ((data->mDisplayItemKey >> nsDisplayItem::TYPE_BITS)) {
+ str += nsPrintfCString("(%i)", data->mDisplayItemKey >> nsDisplayItem::TYPE_BITS);
+ }
+ str += nsPrintfCString(", %s, Layer %p", layerState, data->mLayer.get());
+ if (data->mOptLayer) {
+ str += nsPrintfCString(", OptLayer %p", data->mOptLayer.get());
+ }
+ if (data->mInactiveManager) {
+ str += nsPrintfCString(", InactiveLayerManager %p", data->mInactiveManager.get());
+ }
+ str += "\n";
+
+ printf_stderr("%s", str.get());
+
+ if (data->mInactiveManager) {
+ prefix += " ";
+ printf_stderr("%sDumping inactive layer info:\n", prefix.get());
+ LayerManagerData* lmd = static_cast<LayerManagerData*>
+ (data->mInactiveManager->GetUserData(&gLayerManagerUserData));
+ lmd->Dump(prefix.get());
+ }
+ }
+ }
+#endif
+
+ /**
+ * Tracks which frames have layers associated with them.
+ */
+ LayerManager *mLayerManager;
+#ifdef DEBUG_DISPLAY_ITEM_DATA
+ LayerManagerData *mParent;
+#endif
+ nsTHashtable<nsRefPtrHashKey<FrameLayerBuilder::DisplayItemData> > mDisplayItems;
+ bool mInvalidateAllLayers;
+};
+
+/* static */ void
+FrameLayerBuilder::DestroyDisplayItemDataFor(nsIFrame* aFrame)
+{
+ FrameProperties props = aFrame->Properties();
+ props.Delete(LayerManagerDataProperty());
+}
+
+struct AssignedDisplayItem
+{
+ AssignedDisplayItem(nsDisplayItem* aItem,
+ const DisplayItemClip& aClip,
+ LayerState aLayerState)
+ : mItem(aItem)
+ , mClip(aClip)
+ , mLayerState(aLayerState)
+ {}
+
+ nsDisplayItem* mItem;
+ DisplayItemClip mClip;
+ LayerState mLayerState;
+};
+
+/**
+ * We keep a stack of these to represent the PaintedLayers that are
+ * currently available to have display items added to.
+ * We use a stack here because as much as possible we want to
+ * assign display items to existing PaintedLayers, and to the lowest
+ * PaintedLayer in z-order. This reduces the number of layers and
+ * makes it more likely a display item will be rendered to an opaque
+ * layer, giving us the best chance of getting subpixel AA.
+ */
+class PaintedLayerData {
+public:
+ PaintedLayerData() :
+ mAnimatedGeometryRoot(nullptr),
+ mScrollClip(nullptr),
+ mReferenceFrame(nullptr),
+ mLayer(nullptr),
+ mSolidColor(NS_RGBA(0, 0, 0, 0)),
+ mIsSolidColorInVisibleRegion(false),
+ mFontSmoothingBackgroundColor(NS_RGBA(0,0,0,0)),
+ mSingleItemFixedToViewport(false),
+ mNeedComponentAlpha(false),
+ mForceTransparentSurface(false),
+ mHideAllLayersBelow(false),
+ mOpaqueForAnimatedGeometryRootParent(false),
+ mDisableFlattening(false),
+ mBackfaceHidden(false),
+ mImage(nullptr),
+ mCommonClipCount(-1),
+ mNewChildLayersIndex(-1)
+ {}
+
+#ifdef MOZ_DUMP_PAINTING
+ /**
+ * Keep track of important decisions for debugging.
+ */
+ nsCString mLog;
+
+ #define FLB_LOG_PAINTED_LAYER_DECISION(pld, ...) \
+ if (gfxPrefs::LayersDumpDecision()) { \
+ pld->mLog.AppendPrintf("\t\t\t\t"); \
+ pld->mLog.AppendPrintf(__VA_ARGS__); \
+ }
+#else
+ #define FLB_LOG_PAINTED_LAYER_DECISION(...)
+#endif
+
+ /**
+ * Record that an item has been added to the PaintedLayer, so we
+ * need to update our regions.
+ * @param aVisibleRect the area of the item that's visible
+ * @param aSolidColor if non-null, the visible area of the item is
+ * a constant color given by *aSolidColor
+ */
+ void Accumulate(ContainerState* aState,
+ nsDisplayItem* aItem,
+ const nsIntRegion& aClippedOpaqueRegion,
+ const nsIntRect& aVisibleRect,
+ const DisplayItemClip& aClip,
+ LayerState aLayerState);
+ AnimatedGeometryRoot* GetAnimatedGeometryRoot() { return mAnimatedGeometryRoot; }
+
+ /**
+ * A region including the horizontal pan, vertical pan, and no action regions.
+ */
+ nsRegion CombinedTouchActionRegion();
+
+ /**
+ * Add the given hit regions to the hit regions to the hit retions for this
+ * PaintedLayer.
+ */
+ void AccumulateEventRegions(ContainerState* aState, nsDisplayLayerEventRegions* aEventRegions);
+
+ /**
+ * If this represents only a nsDisplayImage, and the image type supports being
+ * optimized to an ImageLayer, returns true.
+ */
+ bool CanOptimizeToImageLayer(nsDisplayListBuilder* aBuilder);
+
+ /**
+ * If this represents only a nsDisplayImage, and the image type supports being
+ * optimized to an ImageLayer, returns an ImageContainer for the underlying
+ * image if one is available.
+ */
+ already_AddRefed<ImageContainer> GetContainerForImageLayer(nsDisplayListBuilder* aBuilder);
+
+ bool VisibleAboveRegionIntersects(const nsIntRegion& aRegion) const
+ { return !mVisibleAboveRegion.Intersect(aRegion).IsEmpty(); }
+ bool VisibleRegionIntersects(const nsIntRegion& aRegion) const
+ { return !mVisibleRegion.Intersect(aRegion).IsEmpty(); }
+
+ /**
+ * The region of visible content in the layer, relative to the
+ * container layer (which is at the snapped top-left of the display
+ * list reference frame).
+ */
+ nsIntRegion mVisibleRegion;
+ /**
+ * The region of visible content in the layer that is opaque.
+ * Same coordinate system as mVisibleRegion.
+ */
+ nsIntRegion mOpaqueRegion;
+ /**
+ * The definitely-hit region for this PaintedLayer.
+ */
+ nsRegion mHitRegion;
+ /**
+ * The maybe-hit region for this PaintedLayer.
+ */
+ nsRegion mMaybeHitRegion;
+ /**
+ * The dispatch-to-content hit region for this PaintedLayer.
+ */
+ nsRegion mDispatchToContentHitRegion;
+ /**
+ * The region for this PaintedLayer that is sensitive to events
+ * but disallows panning and zooming. This is an approximation
+ * and any deviation from the true region will be part of the
+ * mDispatchToContentHitRegion.
+ */
+ nsRegion mNoActionRegion;
+ /**
+ * The region for this PaintedLayer that is sensitive to events and
+ * allows horizontal panning but not zooming. This is an approximation
+ * and any deviation from the true region will be part of the
+ * mDispatchToContentHitRegion.
+ */
+ nsRegion mHorizontalPanRegion;
+ /**
+ * The region for this PaintedLayer that is sensitive to events and
+ * allows vertical panning but not zooming. This is an approximation
+ * and any deviation from the true region will be part of the
+ * mDispatchToContentHitRegion.
+ */
+ nsRegion mVerticalPanRegion;
+ /**
+ * Scaled versions of the bounds of mHitRegion and mMaybeHitRegion.
+ * We store these because FindPaintedLayerFor() needs to consume them
+ * in this form, and it's a hot code path so we don't want to scale
+ * them inside that function.
+ */
+ nsIntRect mScaledHitRegionBounds;
+ nsIntRect mScaledMaybeHitRegionBounds;
+ /**
+ * The "active scrolled root" for all content in the layer. Must
+ * be non-null; all content in a PaintedLayer must have the same
+ * active scrolled root.
+ */
+ AnimatedGeometryRoot* mAnimatedGeometryRoot;
+ /**
+ * The scroll clip for this layer.
+ */
+ const DisplayItemScrollClip* mScrollClip;
+ /**
+ * The offset between mAnimatedGeometryRoot and the reference frame.
+ */
+ nsPoint mAnimatedGeometryRootOffset;
+ /**
+ * If non-null, the frame from which we'll extract "fixed positioning"
+ * metadata for this layer. This can be a position:fixed frame or a viewport
+ * frame; the latter case is used for background-attachment:fixed content.
+ */
+ const nsIFrame* mReferenceFrame;
+ PaintedLayer* mLayer;
+ /**
+ * If mIsSolidColorInVisibleRegion is true, this is the color of the visible
+ * region.
+ */
+ nscolor mSolidColor;
+ /**
+ * True if every pixel in mVisibleRegion will have color mSolidColor.
+ */
+ bool mIsSolidColorInVisibleRegion;
+ /**
+ * The target background color for smoothing fonts that are drawn on top of
+ * transparent parts of the layer.
+ */
+ nscolor mFontSmoothingBackgroundColor;
+ /**
+ * True if the layer contains exactly one item that returned true for
+ * ShouldFixToViewport.
+ */
+ bool mSingleItemFixedToViewport;
+ /**
+ * True if there is any text visible in the layer that's over
+ * transparent pixels in the layer.
+ */
+ bool mNeedComponentAlpha;
+ /**
+ * Set if the layer should be treated as transparent, even if its entire
+ * area is covered by opaque display items. For example, this needs to
+ * be set if something is going to "punch holes" in the layer by clearing
+ * part of its surface.
+ */
+ bool mForceTransparentSurface;
+ /**
+ * Set if all layers below this PaintedLayer should be hidden.
+ */
+ bool mHideAllLayersBelow;
+ /**
+ * Set if the opaque region for this layer can be applied to the parent
+ * animated geometry root of this layer's animated geometry root.
+ * We set this when a PaintedLayer's animated geometry root is a scrollframe
+ * and the PaintedLayer completely fills the displayport of the scrollframe.
+ */
+ bool mOpaqueForAnimatedGeometryRootParent;
+ /**
+ * Set if there is content in the layer that must avoid being flattened.
+ */
+ bool mDisableFlattening;
+ /**
+ * Set if the backface of this region is hidden to the user.
+ * Content that backface is hidden should not be draw on the layer
+ * with visible backface.
+ */
+ bool mBackfaceHidden;
+ /**
+ * Stores the pointer to the nsDisplayImage if we want to
+ * convert this to an ImageLayer.
+ */
+ nsDisplayImageContainer* mImage;
+ /**
+ * Stores the clip that we need to apply to the image or, if there is no
+ * image, a clip for SOME item in the layer. There is no guarantee which
+ * item's clip will be stored here and mItemClip should not be used to clip
+ * the whole layer - only some part of the clip should be used, as determined
+ * by PaintedDisplayItemLayerUserData::GetCommonClipCount() - which may even be
+ * no part at all.
+ */
+ DisplayItemClip mItemClip;
+ /**
+ * The first mCommonClipCount rounded rectangle clips are identical for
+ * all items in the layer.
+ * -1 if there are no items in the layer; must be >=0 by the time that this
+ * data is popped from the stack.
+ */
+ int32_t mCommonClipCount;
+ /**
+ * Index of this layer in mNewChildLayers.
+ */
+ int32_t mNewChildLayersIndex;
+ /*
+ * Updates mCommonClipCount by checking for rounded rect clips in common
+ * between the clip on a new item (aCurrentClip) and the common clips
+ * on items already in the layer (the first mCommonClipCount rounded rects
+ * in mItemClip).
+ */
+ void UpdateCommonClipCount(const DisplayItemClip& aCurrentClip);
+ /**
+ * The union of all the bounds of the display items in this layer.
+ */
+ nsIntRect mBounds;
+ /**
+ * The region of visible content above the layer and below the
+ * next PaintedLayerData currently in the stack, if any.
+ * This is a conservative approximation: it contains the true region.
+ */
+ nsIntRegion mVisibleAboveRegion;
+ /**
+ * All the display items that have been assigned to this painted layer.
+ * These items get added by Accumulate().
+ */
+ nsTArray<AssignedDisplayItem> mAssignedDisplayItems;
+
+};
+
+struct NewLayerEntry {
+ NewLayerEntry()
+ : mAnimatedGeometryRoot(nullptr)
+ , mScrollClip(nullptr)
+ , mLayerContentsVisibleRect(0, 0, -1, -1)
+ , mLayerState(LAYER_INACTIVE)
+ , mHideAllLayersBelow(false)
+ , mOpaqueForAnimatedGeometryRootParent(false)
+ , mPropagateComponentAlphaFlattening(true)
+ , mUntransformedVisibleRegion(false)
+ {}
+ // mLayer is null if the previous entry is for a PaintedLayer that hasn't
+ // been optimized to some other form (yet).
+ RefPtr<Layer> mLayer;
+ AnimatedGeometryRoot* mAnimatedGeometryRoot;
+ const DisplayItemScrollClip* mScrollClip;
+ // If non-null, this ScrollMetadata is set to the be the first ScrollMetadata
+ // on the layer.
+ UniquePtr<ScrollMetadata> mBaseScrollMetadata;
+ // The following are only used for retained layers (for occlusion
+ // culling of those layers). These regions are all relative to the
+ // container reference frame.
+ nsIntRegion mVisibleRegion;
+ nsIntRegion mOpaqueRegion;
+ // This rect is in the layer's own coordinate space. The computed visible
+ // region for the layer cannot extend beyond this rect.
+ nsIntRect mLayerContentsVisibleRect;
+ LayerState mLayerState;
+ bool mHideAllLayersBelow;
+ // When mOpaqueForAnimatedGeometryRootParent is true, the opaque region of
+ // this layer is opaque in the same position even subject to the animation of
+ // geometry of mAnimatedGeometryRoot. For example when mAnimatedGeometryRoot
+ // is a scrolled frame and the scrolled content is opaque everywhere in the
+ // displayport, we can set this flag.
+ // When this flag is set, we can treat this opaque region as covering
+ // content whose animated geometry root is the animated geometry root for
+ // mAnimatedGeometryRoot->GetParent().
+ bool mOpaqueForAnimatedGeometryRootParent;
+
+ // If true, then the content flags for this layer should contribute
+ // to our decision to flatten component alpha layers, false otherwise.
+ bool mPropagateComponentAlphaFlattening;
+ // mVisibleRegion is relative to the associated frame before
+ // transform.
+ bool mUntransformedVisibleRegion;
+};
+
+class PaintedLayerDataTree;
+
+/**
+ * This is tree node type for PaintedLayerDataTree.
+ * Each node corresponds to a different animated geometry root, and contains
+ * a stack of PaintedLayerDatas, in bottom-to-top order.
+ * There is at most one node per animated geometry root. The ancestor and
+ * descendant relations in PaintedLayerDataTree tree mirror those in the frame
+ * tree.
+ * Each node can have clip that describes the potential extents that items in
+ * this node can cover. If mHasClip is false, it means that the node's contents
+ * can move anywhere.
+ * Testing against the clip instead of the node's actual contents has the
+ * advantage that the node's contents can move or animate without affecting
+ * content in other nodes. So we don't need to re-layerize during animations
+ * (sync or async), and during async animations everything is guaranteed to
+ * look correct.
+ * The contents of a node's PaintedLayerData stack all share the node's
+ * animated geometry root. The child nodes are on top of the PaintedLayerData
+ * stack, in z-order, and the clip rects of the child nodes are allowed to
+ * intersect with the visible region or visible above region of their parent
+ * node's PaintedLayerDatas.
+ */
+class PaintedLayerDataNode {
+public:
+ PaintedLayerDataNode(PaintedLayerDataTree& aTree,
+ PaintedLayerDataNode* aParent,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot);
+ ~PaintedLayerDataNode();
+
+ AnimatedGeometryRoot* GetAnimatedGeometryRoot() const { return mAnimatedGeometryRoot; }
+
+ /**
+ * Whether this node's contents can potentially intersect aRect.
+ * aRect is in our tree's ContainerState's coordinate space.
+ */
+ bool Intersects(const nsIntRect& aRect) const
+ { return !mHasClip || mClipRect.Intersects(aRect); }
+
+ /**
+ * Create a PaintedLayerDataNode for aAnimatedGeometryRoot, add it to our
+ * children, and return it.
+ */
+ PaintedLayerDataNode* AddChildNodeFor(AnimatedGeometryRoot* aAnimatedGeometryRoot);
+
+ /**
+ * Find a PaintedLayerData in our mPaintedLayerDataStack that aItem can be
+ * added to. Creates a new PaintedLayerData by calling
+ * aNewPaintedLayerCallback if necessary.
+ */
+ template<typename NewPaintedLayerCallbackType>
+ PaintedLayerData* FindPaintedLayerFor(const nsIntRect& aVisibleRect,
+ bool aBackfaceHidden,
+ const DisplayItemScrollClip* aScrollClip,
+ NewPaintedLayerCallbackType aNewPaintedLayerCallback);
+
+ /**
+ * Find an opaque background color for aRegion. Pulls a color from the parent
+ * geometry root if appropriate, but only if that color is present underneath
+ * the whole clip of this node, so that this node's contents can animate or
+ * move (possibly async) without having to change the background color.
+ * @param aUnderIndex Searching will start in mPaintedLayerDataStack right
+ * below aUnderIndex.
+ */
+ enum { ABOVE_TOP = -1 };
+ nscolor FindOpaqueBackgroundColor(const nsIntRegion& aRegion,
+ int32_t aUnderIndex = ABOVE_TOP) const;
+ /**
+ * Same as FindOpaqueBackgroundColor, but only returns a color if absolutely
+ * nothing is in between, so that it can be used for a layer that can move
+ * anywhere inside our clip.
+ */
+ nscolor FindOpaqueBackgroundColorCoveringEverything() const;
+
+ /**
+ * Adds aRect to this node's top PaintedLayerData's mVisibleAboveRegion,
+ * or mVisibleAboveBackgroundRegion if mPaintedLayerDataStack is empty.
+ */
+ void AddToVisibleAboveRegion(const nsIntRect& aRect);
+ /**
+ * Call this if all of our existing content can potentially be covered, so
+ * nothing can merge with it and all new content needs to create new items
+ * on top. This will finish all of our children and pop our whole
+ * mPaintedLayerDataStack.
+ */
+ void SetAllDrawingAbove();
+
+ /**
+ * Finish this node: Finish all children, finish our PaintedLayer contents,
+ * and (if requested) adjust our parent's visible above region to include
+ * our clip.
+ */
+ void Finish(bool aParentNeedsAccurateVisibleAboveRegion);
+
+ /**
+ * Finish any children that intersect aRect.
+ */
+ void FinishChildrenIntersecting(const nsIntRect& aRect);
+
+ /**
+ * Finish all children.
+ */
+ void FinishAllChildren() { FinishAllChildren(true); }
+
+protected:
+ /**
+ * Finish the topmost item in mPaintedLayerDataStack and pop it from the
+ * stack.
+ */
+ void PopPaintedLayerData();
+ /**
+ * Finish all items in mPaintedLayerDataStack and clear the stack.
+ */
+ void PopAllPaintedLayerData();
+ /**
+ * Finish all of our child nodes, but don't touch mPaintedLayerDataStack.
+ */
+ void FinishAllChildren(bool aThisNodeNeedsAccurateVisibleAboveRegion);
+ /**
+ * Pass off opaque background color searching to our parent node, if we have
+ * one.
+ */
+ nscolor FindOpaqueBackgroundColorInParentNode() const;
+
+ PaintedLayerDataTree& mTree;
+ PaintedLayerDataNode* mParent;
+ AnimatedGeometryRoot* mAnimatedGeometryRoot;
+
+ /**
+ * Our contents: a PaintedLayerData stack and our child nodes.
+ */
+ nsTArray<PaintedLayerData> mPaintedLayerDataStack;
+
+ /**
+ * UniquePtr is used here in the sense of "unique ownership", i.e. there is
+ * only one owner. Not in the sense of "this is the only pointer to the
+ * node": There are two other, non-owning, pointers to our child nodes: The
+ * node's respective children point to their parent node with their mParent
+ * pointer, and the tree keeps a map of animated geometry root to node in its
+ * mNodes member. These outside pointers are the reason that mChildren isn't
+ * just an nsTArray<PaintedLayerDataNode> (since the pointers would become
+ * invalid whenever the array expands its capacity).
+ */
+ nsTArray<UniquePtr<PaintedLayerDataNode>> mChildren;
+
+ /**
+ * The region that's covered between our "background" and the bottom of
+ * mPaintedLayerDataStack. This is used to indicate whether we can pull
+ * a background color from our parent node. If mVisibleAboveBackgroundRegion
+ * should be considered infinite, mAllDrawingAboveBackground will be true and
+ * the value of mVisibleAboveBackgroundRegion will be meaningless.
+ */
+ nsIntRegion mVisibleAboveBackgroundRegion;
+
+ /**
+ * Our clip, if we have any. If not, that means we can move anywhere, and
+ * mHasClip will be false and mClipRect will be meaningless.
+ */
+ nsIntRect mClipRect;
+ bool mHasClip;
+
+ /**
+ * Whether mVisibleAboveBackgroundRegion should be considered infinite.
+ */
+ bool mAllDrawingAboveBackground;
+};
+
+class ContainerState;
+
+/**
+ * A tree of PaintedLayerDataNodes. At any point in time, the tree only
+ * contains nodes for animated geometry roots that new items can potentially
+ * merge into. Any time content is added on top that overlaps existing things
+ * in such a way that we no longer want to merge new items with some existing
+ * content, that existing content gets "finished".
+ * The public-facing methods of this class are FindPaintedLayerFor,
+ * AddingOwnLayer, and Finish. The other public methods are for
+ * PaintedLayerDataNode.
+ * The tree calls out to its containing ContainerState for some things.
+ * All coordinates / rects in the tree or the tree nodes are in the
+ * ContainerState's coordinate space, i.e. relative to the reference frame and
+ * in layer pixels.
+ * The clip rects of sibling nodes never overlap. This is ensured by finishing
+ * existing nodes before adding new ones, if this property were to be violated.
+ * The root tree node doesn't get finished until the ContainerState is
+ * finished.
+ * The tree's root node is always the root reference frame of the builder. We
+ * don't stop at the container state's mContainerAnimatedGeometryRoot because
+ * some of our contents can have animated geometry roots that are not
+ * descendants of the container's animated geometry root. Every animated
+ * geometry root we encounter for our contents needs to have a defined place in
+ * the tree.
+ */
+class PaintedLayerDataTree {
+public:
+ PaintedLayerDataTree(ContainerState& aContainerState,
+ nscolor& aBackgroundColor)
+ : mContainerState(aContainerState)
+ , mContainerUniformBackgroundColor(aBackgroundColor)
+ {}
+
+ ~PaintedLayerDataTree()
+ {
+ MOZ_ASSERT(!mRoot);
+ MOZ_ASSERT(mNodes.Count() == 0);
+ }
+
+ /**
+ * Notify our contents that some non-PaintedLayer content has been added.
+ * *aRect needs to be a rectangle that doesn't move with respect to
+ * aAnimatedGeometryRoot and that contains the added item.
+ * If aRect is null, the extents will be considered infinite.
+ * If aOutUniformBackgroundColor is non-null, it will be set to an opaque
+ * color that can be pulled into the background of the added content, or
+ * transparent if that is not possible.
+ */
+ void AddingOwnLayer(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const nsIntRect* aRect,
+ nscolor* aOutUniformBackgroundColor);
+
+ /**
+ * Find a PaintedLayerData for aItem. This can either be an existing
+ * PaintedLayerData from inside a node in our tree, or a new one that gets
+ * created by a call out to aNewPaintedLayerCallback.
+ */
+ template<typename NewPaintedLayerCallbackType>
+ PaintedLayerData* FindPaintedLayerFor(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const DisplayItemScrollClip* aScrollClip,
+ const nsIntRect& aVisibleRect,
+ bool aBackfaceidden,
+ NewPaintedLayerCallbackType aNewPaintedLayerCallback);
+
+ /**
+ * Finish everything.
+ */
+ void Finish();
+
+ /**
+ * Get the parent animated geometry root of aAnimatedGeometryRoot.
+ * That's either aAnimatedGeometryRoot's animated geometry root, or, if
+ * that's aAnimatedGeometryRoot itself, then it's the animated geometry
+ * root for aAnimatedGeometryRoot's cross-doc parent frame.
+ */
+ AnimatedGeometryRoot* GetParentAnimatedGeometryRoot(AnimatedGeometryRoot* aAnimatedGeometryRoot);
+
+ /**
+ * Whether aAnimatedGeometryRoot has an intrinsic clip that doesn't move with
+ * respect to aAnimatedGeometryRoot's parent animated geometry root.
+ * If aAnimatedGeometryRoot is a scroll frame, this will be the scroll frame's
+ * scroll port, otherwise there is no clip.
+ * This method doesn't have much to do with PaintedLayerDataTree, but this is
+ * where we have easy access to a display list builder, which we use to get
+ * the clip rect result into the right coordinate space.
+ */
+ bool IsClippedWithRespectToParentAnimatedGeometryRoot(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ nsIntRect* aOutClip);
+
+ /**
+ * Called by PaintedLayerDataNode when it is finished, so that we can drop
+ * our pointers to it.
+ */
+ void NodeWasFinished(AnimatedGeometryRoot* aAnimatedGeometryRoot);
+
+ nsDisplayListBuilder* Builder() const;
+ ContainerState& ContState() const { return mContainerState; }
+ nscolor UniformBackgroundColor() const { return mContainerUniformBackgroundColor; }
+
+protected:
+ /**
+ * Finish all nodes that potentially intersect *aRect, where *aRect is a rect
+ * that doesn't move with respect to aAnimatedGeometryRoot.
+ * If aRect is null, *aRect will be considered infinite.
+ */
+ void FinishPotentiallyIntersectingNodes(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const nsIntRect* aRect);
+
+ /**
+ * Make sure that there is a node for aAnimatedGeometryRoot and all of its
+ * ancestor geometry roots. Return the node for aAnimatedGeometryRoot.
+ */
+ PaintedLayerDataNode* EnsureNodeFor(AnimatedGeometryRoot* aAnimatedGeometryRoot);
+
+ /**
+ * Find an existing node in the tree for an ancestor of aAnimatedGeometryRoot.
+ * *aOutAncestorChild will be set to the last ancestor that was encountered
+ * in the search up from aAnimatedGeometryRoot; it will be a child animated
+ * geometry root of the result, if neither are null.
+ */
+ PaintedLayerDataNode*
+ FindNodeForAncestorAnimatedGeometryRoot(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ AnimatedGeometryRoot** aOutAncestorChild);
+
+ ContainerState& mContainerState;
+ UniquePtr<PaintedLayerDataNode> mRoot;
+
+ /**
+ * The uniform opaque color from behind this container layer, or
+ * NS_RGBA(0,0,0,0) if the background behind this container layer is not
+ * uniform and opaque. This color can be pulled into PaintedLayers that are
+ * directly above the background.
+ */
+ nscolor mContainerUniformBackgroundColor;
+
+ /**
+ * A hash map for quick access the node belonging to a particular animated
+ * geometry root.
+ */
+ nsDataHashtable<nsPtrHashKey<AnimatedGeometryRoot>, PaintedLayerDataNode*> mNodes;
+};
+
+/**
+ * This is a helper object used to build up the layer children for
+ * a ContainerLayer.
+ */
+class ContainerState {
+public:
+ ContainerState(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ FrameLayerBuilder* aLayerBuilder,
+ nsIFrame* aContainerFrame,
+ nsDisplayItem* aContainerItem,
+ const nsRect& aContainerBounds,
+ ContainerLayer* aContainerLayer,
+ const ContainerLayerParameters& aParameters,
+ bool aFlattenToSingleLayer,
+ nscolor aBackgroundColor,
+ const DisplayItemScrollClip* aContainerScrollClip) :
+ mBuilder(aBuilder), mManager(aManager),
+ mLayerBuilder(aLayerBuilder),
+ mContainerFrame(aContainerFrame),
+ mContainerLayer(aContainerLayer),
+ mContainerBounds(aContainerBounds),
+ mContainerScrollClip(aContainerScrollClip),
+ mScrollClipForPerspectiveChild(aParameters.mScrollClipForPerspectiveChild),
+ mParameters(aParameters),
+ mPaintedLayerDataTree(*this, aBackgroundColor),
+ mFlattenToSingleLayer(aFlattenToSingleLayer)
+ {
+ nsPresContext* presContext = aContainerFrame->PresContext();
+ mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
+ mContainerReferenceFrame =
+ const_cast<nsIFrame*>(aContainerItem ? aContainerItem->ReferenceFrameForChildren() :
+ mBuilder->FindReferenceFrameFor(mContainerFrame));
+ bool isAtRoot = !aContainerItem || (aContainerItem->Frame() == mBuilder->RootReferenceFrame());
+ MOZ_ASSERT_IF(isAtRoot, mContainerReferenceFrame == mBuilder->RootReferenceFrame());
+ mContainerAnimatedGeometryRoot = isAtRoot
+ ? aBuilder->GetRootAnimatedGeometryRoot()
+ : aContainerItem->GetAnimatedGeometryRoot();
+ MOZ_ASSERT(!mBuilder->IsPaintingToWindow() ||
+ nsLayoutUtils::IsAncestorFrameCrossDoc(mBuilder->RootReferenceFrame(),
+ *mContainerAnimatedGeometryRoot));
+ // When AllowResidualTranslation is false, display items will be drawn
+ // scaled with a translation by integer pixels, so we know how the snapping
+ // will work.
+ mSnappingEnabled = aManager->IsSnappingEffectiveTransforms() &&
+ !mParameters.AllowResidualTranslation();
+ CollectOldLayers();
+ }
+
+ /**
+ * This is the method that actually walks a display list and builds
+ * the child layers.
+ */
+ void ProcessDisplayItems(nsDisplayList* aList);
+ /**
+ * This finalizes all the open PaintedLayers by popping every element off
+ * mPaintedLayerDataStack, then sets the children of the container layer
+ * to be all the layers in mNewChildLayers in that order and removes any
+ * layers as children of the container that aren't in mNewChildLayers.
+ * @param aTextContentFlags if any child layer has CONTENT_COMPONENT_ALPHA,
+ * set *aTextContentFlags to CONTENT_COMPONENT_ALPHA
+ */
+ void Finish(uint32_t *aTextContentFlags,
+ const nsIntRect& aContainerPixelBounds,
+ nsDisplayList* aChildItems, bool* aHasComponentAlphaChildren);
+
+ nscoord GetAppUnitsPerDevPixel() { return mAppUnitsPerDevPixel; }
+
+ nsIntRect ScaleToNearestPixels(const nsRect& aRect) const
+ {
+ return aRect.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale,
+ mAppUnitsPerDevPixel);
+ }
+ nsIntRegion ScaleRegionToNearestPixels(const nsRegion& aRegion) const
+ {
+ return aRegion.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale,
+ mAppUnitsPerDevPixel);
+ }
+ nsIntRect ScaleToOutsidePixels(const nsRect& aRect, bool aSnap = false) const
+ {
+ if (aSnap && mSnappingEnabled) {
+ return ScaleToNearestPixels(aRect);
+ }
+ return aRect.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale,
+ mAppUnitsPerDevPixel);
+ }
+ nsIntRect ScaleToInsidePixels(const nsRect& aRect, bool aSnap = false) const
+ {
+ if (aSnap && mSnappingEnabled) {
+ return ScaleToNearestPixels(aRect);
+ }
+ return aRect.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale,
+ mAppUnitsPerDevPixel);
+ }
+
+ nsIntRegion ScaleRegionToInsidePixels(const nsRegion& aRegion, bool aSnap = false) const
+ {
+ if (aSnap && mSnappingEnabled) {
+ return ScaleRegionToNearestPixels(aRegion);
+ }
+ return aRegion.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale,
+ mAppUnitsPerDevPixel);
+ }
+
+ nsIntRegion ScaleRegionToOutsidePixels(const nsRegion& aRegion, bool aSnap = false) const
+ {
+ if (aSnap && mSnappingEnabled) {
+ return ScaleRegionToNearestPixels(aRegion);
+ }
+ return aRegion.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale,
+ mAppUnitsPerDevPixel);
+ }
+
+ nsIFrame* GetContainerFrame() const { return mContainerFrame; }
+ nsDisplayListBuilder* Builder() const { return mBuilder; }
+
+ /**
+ * Check if we are currently inside an inactive layer.
+ */
+ bool IsInInactiveLayer() const {
+ return mLayerBuilder->GetContainingPaintedLayerData();
+ }
+
+ /**
+ * Sets aOuterVisibleRegion as aLayer's visible region.
+ * @param aOuterVisibleRegion
+ * is in the coordinate space of the container reference frame.
+ * @param aLayerContentsVisibleRect, if non-null, is in the layer's own
+ * coordinate system.
+ * @param aOuterUntransformed is true if the given aOuterVisibleRegion
+ * is already untransformed with the matrix of the layer.
+ */
+ void SetOuterVisibleRegionForLayer(Layer* aLayer,
+ const nsIntRegion& aOuterVisibleRegion,
+ const nsIntRect* aLayerContentsVisibleRect = nullptr,
+ bool aOuterUntransformed = false) const;
+
+ /**
+ * Try to determine whether the PaintedLayer aData has a single opaque color
+ * covering aRect. If successful, return that color, otherwise return
+ * NS_RGBA(0,0,0,0).
+ * If aRect turns out not to intersect any content in the layer,
+ * *aOutIntersectsLayer will be set to false.
+ */
+ nscolor FindOpaqueBackgroundColorInLayer(const PaintedLayerData* aData,
+ const nsIntRect& aRect,
+ bool* aOutIntersectsLayer) const;
+
+ /**
+ * Indicate that we are done adding items to the PaintedLayer represented by
+ * aData. Make sure that a real PaintedLayer exists for it, and set the final
+ * visible region and opaque-content.
+ */
+ template<typename FindOpaqueBackgroundColorCallbackType>
+ void FinishPaintedLayerData(PaintedLayerData& aData, FindOpaqueBackgroundColorCallbackType aFindOpaqueBackgroundColor);
+
+protected:
+ friend class PaintedLayerData;
+
+ LayerManager::PaintedLayerCreationHint
+ GetLayerCreationHint(AnimatedGeometryRoot* aAnimatedGeometryRoot);
+
+ /**
+ * Creates a new PaintedLayer and sets up the transform on the PaintedLayer
+ * to account for scrolling.
+ */
+ already_AddRefed<PaintedLayer> CreatePaintedLayer(PaintedLayerData* aData);
+
+ /**
+ * Find a PaintedLayer for recycling, recycle it and prepare it for use, or
+ * return null if no suitable layer was found.
+ */
+ already_AddRefed<PaintedLayer> AttemptToRecyclePaintedLayer(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ nsDisplayItem* aItem,
+ const nsPoint& aTopLeft);
+ /**
+ * Recycle aLayer and do any necessary invalidation.
+ */
+ PaintedDisplayItemLayerUserData* RecyclePaintedLayer(PaintedLayer* aLayer,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ bool& didResetScrollPositionForLayerPixelAlignment);
+
+ /**
+ * Perform the last step of CreatePaintedLayer / AttemptToRecyclePaintedLayer:
+ * Initialize aData, set up the layer's transform for scrolling, and
+ * invalidate the layer for layer pixel alignment changes if necessary.
+ */
+ void PreparePaintedLayerForUse(PaintedLayer* aLayer,
+ PaintedDisplayItemLayerUserData* aData,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const nsIFrame* aReferenceFrame,
+ const nsPoint& aTopLeft,
+ bool aDidResetScrollPositionForLayerPixelAlignment);
+
+ /**
+ * Attempt to prepare an ImageLayer based upon the provided PaintedLayerData.
+ * Returns nullptr on failure.
+ */
+ already_AddRefed<Layer> PrepareImageLayer(PaintedLayerData* aData);
+
+ /**
+ * Attempt to prepare a ColorLayer based upon the provided PaintedLayerData.
+ * Returns nullptr on failure.
+ */
+ already_AddRefed<Layer> PrepareColorLayer(PaintedLayerData* aData);
+
+ /**
+ * Grab the next recyclable ColorLayer, or create one if there are no
+ * more recyclable ColorLayers.
+ */
+ already_AddRefed<ColorLayer> CreateOrRecycleColorLayer(PaintedLayer* aPainted);
+ /**
+ * Grab the next recyclable ImageLayer, or create one if there are no
+ * more recyclable ImageLayers.
+ */
+ already_AddRefed<ImageLayer> CreateOrRecycleImageLayer(PaintedLayer* aPainted);
+ /**
+ * Grab a recyclable ImageLayer for use as a mask layer for aLayer (that is a
+ * mask layer which has been used for aLayer before), or create one if such
+ * a layer doesn't exist.
+ *
+ * Since mask layers can exist either on the layer directly, or as a side-
+ * attachment to FrameMetrics (for ancestor scrollframe clips), we key the
+ * recycle operation on both the originating layer and the mask layer's
+ * index in the layer, if any.
+ */
+ struct MaskLayerKey;
+ already_AddRefed<ImageLayer>
+ CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey,
+ mozilla::function<void(Layer* aLayer)> aSetUserData);
+ /**
+ * Grabs all PaintedLayers and ColorLayers from the ContainerLayer and makes them
+ * available for recycling.
+ */
+ void CollectOldLayers();
+ /**
+ * If aItem used to belong to a PaintedLayer, invalidates the area of
+ * aItem in that layer. If aNewLayer is a PaintedLayer, invalidates the area of
+ * aItem in that layer.
+ */
+ void InvalidateForLayerChange(nsDisplayItem* aItem,
+ PaintedLayer* aNewLayer);
+ /**
+ * Returns true if aItem's opaque area (in aOpaque) covers the entire
+ * scrollable area of its presshell.
+ */
+ bool ItemCoversScrollableArea(nsDisplayItem* aItem, const nsRegion& aOpaque);
+
+ /**
+ * Set FrameMetrics and scroll-induced clipping on aEntry's layer.
+ */
+ void SetupScrollingMetadata(NewLayerEntry* aEntry);
+
+ /**
+ * Applies occlusion culling.
+ * For each layer in mNewChildLayers, remove from its visible region the
+ * opaque regions of the layers at higher z-index, but only if they have
+ * the same animated geometry root and fixed-pos frame ancestor.
+ * The opaque region for the child layers that share the same animated
+ * geometry root as the container frame is returned in
+ * *aOpaqueRegionForContainer.
+ *
+ * Also sets scroll metadata on the layers.
+ */
+ void PostprocessRetainedLayers(nsIntRegion* aOpaqueRegionForContainer);
+
+ /**
+ * Computes the snapped opaque area of aItem. Sets aList's opaque flag
+ * if it covers the entire list bounds. Sets *aHideAllLayersBelow to true
+ * this item covers the entire viewport so that all layers below are
+ * permanently invisible.
+ */
+ nsIntRegion ComputeOpaqueRect(nsDisplayItem* aItem,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const DisplayItemClip& aClip,
+ nsDisplayList* aList,
+ bool* aHideAllLayersBelow,
+ bool* aOpaqueForAnimatedGeometryRootParent);
+
+ /**
+ * Return a PaintedLayerData object that is initialized for a layer that
+ * aItem will be assigned to.
+ * @param aItem The item that is going to be added.
+ * @param aVisibleRect The visible rect of the item.
+ * @param aAnimatedGeometryRoot The item's animated geometry root.
+ * @param aScrollClip The scroll clip for this PaintedLayer.
+ * @param aTopLeft The offset between aAnimatedGeometryRoot and
+ * the reference frame.
+ * @param aShouldFixToViewport If true, aAnimatedGeometryRoot is the
+ * viewport and we will be adding fixed-pos
+ * metadata for this layer because the display
+ * item returned true from ShouldFixToViewport.
+ */
+ PaintedLayerData NewPaintedLayerData(nsDisplayItem* aItem,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const DisplayItemScrollClip* aScrollClip,
+ const nsPoint& aTopLeft,
+ bool aShouldFixToViewport);
+
+ /* Build a mask layer to represent the clipping region. Will return null if
+ * there is no clipping specified or a mask layer cannot be built.
+ * Builds an ImageLayer for the appropriate backend; the mask is relative to
+ * aLayer's visible region.
+ * aLayer is the layer to be clipped.
+ * relative to the container reference frame
+ * aRoundedRectClipCount is used when building mask layers for PaintedLayers,
+ * SetupMaskLayer will build a mask layer for only the first
+ * aRoundedRectClipCount rounded rects in aClip
+ */
+ void SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
+ uint32_t aRoundedRectClipCount = UINT32_MAX);
+
+ /**
+ * If |aClip| has rounded corners, create a mask layer for them, and
+ * add it to |aLayer|'s ancestor mask layers, returning an index into
+ * the array of ancestor mask layers. Returns an empty Maybe if
+ * |aClip| does not have rounded corners, or if no mask layer could
+ * be created.
+ */
+ Maybe<size_t> SetupMaskLayerForScrolledClip(Layer* aLayer,
+ const DisplayItemClip& aClip);
+
+ /*
+ * Create/find a mask layer with suitable size for aMaskItem to paint
+ * css-positioned-masking onto.
+ */
+ void SetupMaskLayerForCSSMask(Layer* aLayer, nsDisplayMask* aMaskItem);
+
+ already_AddRefed<Layer> CreateMaskLayer(
+ Layer *aLayer, const DisplayItemClip& aClip,
+ const Maybe<size_t>& aForAncestorMaskLayer,
+ uint32_t aRoundedRectClipCount = UINT32_MAX);
+
+ bool ChooseAnimatedGeometryRoot(const nsDisplayList& aList,
+ AnimatedGeometryRoot **aAnimatedGeometryRoot);
+
+ nsDisplayListBuilder* mBuilder;
+ LayerManager* mManager;
+ FrameLayerBuilder* mLayerBuilder;
+ nsIFrame* mContainerFrame;
+ nsIFrame* mContainerReferenceFrame;
+ AnimatedGeometryRoot* mContainerAnimatedGeometryRoot;
+ ContainerLayer* mContainerLayer;
+ nsRect mContainerBounds;
+ const DisplayItemScrollClip* mContainerScrollClip;
+ const DisplayItemScrollClip* mScrollClipForPerspectiveChild;
+#ifdef DEBUG
+ nsRect mAccumulatedChildBounds;
+#endif
+ ContainerLayerParameters mParameters;
+ /**
+ * The region of PaintedLayers that should be invalidated every time
+ * we recycle one.
+ */
+ nsIntRegion mInvalidPaintedContent;
+ PaintedLayerDataTree mPaintedLayerDataTree;
+ /**
+ * We collect the list of children in here. During ProcessDisplayItems,
+ * the layers in this array either have mContainerLayer as their parent,
+ * or no parent.
+ * PaintedLayers have two entries in this array: the second one is used only if
+ * the PaintedLayer is optimized away to a ColorLayer or ImageLayer.
+ * It's essential that this array is only appended to, since PaintedLayerData
+ * records the index of its PaintedLayer in this array.
+ */
+ typedef AutoTArray<NewLayerEntry,1> AutoLayersArray;
+ AutoLayersArray mNewChildLayers;
+ nsTHashtable<nsRefPtrHashKey<PaintedLayer>> mPaintedLayersAvailableForRecycling;
+ nscoord mAppUnitsPerDevPixel;
+ bool mSnappingEnabled;
+ bool mFlattenToSingleLayer;
+
+ struct MaskLayerKey {
+ MaskLayerKey() : mLayer(nullptr) {}
+ MaskLayerKey(Layer* aLayer, const Maybe<size_t>& aAncestorIndex)
+ : mLayer(aLayer),
+ mAncestorIndex(aAncestorIndex)
+ {}
+
+ PLDHashNumber Hash() const {
+ // Hash the layer and add the layer index to the hash.
+ return (NS_PTR_TO_UINT32(mLayer) >> 2)
+ + (mAncestorIndex ? (*mAncestorIndex + 1) : 0);
+ }
+ bool operator ==(const MaskLayerKey& aOther) const {
+ return mLayer == aOther.mLayer &&
+ mAncestorIndex == aOther.mAncestorIndex;
+ }
+
+ Layer* mLayer;
+ Maybe<size_t> mAncestorIndex;
+ };
+
+ nsDataHashtable<nsGenericHashKey<MaskLayerKey>, RefPtr<ImageLayer>>
+ mRecycledMaskImageLayers;
+};
+
+class PaintedDisplayItemLayerUserData : public LayerUserData
+{
+public:
+ PaintedDisplayItemLayerUserData() :
+ mMaskClipCount(0),
+ mForcedBackgroundColor(NS_RGBA(0,0,0,0)),
+ mFontSmoothingBackgroundColor(NS_RGBA(0,0,0,0)),
+ mXScale(1.f), mYScale(1.f),
+ mAppUnitsPerDevPixel(0),
+ mTranslation(0, 0),
+ mAnimatedGeometryRootPosition(0, 0) {}
+
+ /**
+ * Record the number of clips in the PaintedLayer's mask layer.
+ * Should not be reset when the layer is recycled since it is used to track
+ * changes in the use of mask layers.
+ */
+ uint32_t mMaskClipCount;
+
+ /**
+ * A color that should be painted over the bounds of the layer's visible
+ * region before any other content is painted.
+ */
+ nscolor mForcedBackgroundColor;
+
+ /**
+ * The target background color for smoothing fonts that are drawn on top of
+ * transparent parts of the layer.
+ */
+ nscolor mFontSmoothingBackgroundColor;
+
+ /**
+ * The resolution scale used.
+ */
+ float mXScale, mYScale;
+
+ /**
+ * The appunits per dev pixel for the items in this layer.
+ */
+ nscoord mAppUnitsPerDevPixel;
+
+ /**
+ * The offset from the PaintedLayer's 0,0 to the
+ * reference frame. This isn't necessarily the same as the transform
+ * set on the PaintedLayer since we might also be applying an extra
+ * offset specified by the parent ContainerLayer/
+ */
+ nsIntPoint mTranslation;
+
+ /**
+ * We try to make 0,0 of the PaintedLayer be the top-left of the
+ * border-box of the "active scrolled root" frame (i.e. the nearest ancestor
+ * frame for the display items that is being actively scrolled). But
+ * we force the PaintedLayer transform to be an integer translation, and we may
+ * have a resolution scale, so we have to snap the PaintedLayer transform, so
+ * 0,0 may not be exactly the top-left of the active scrolled root. Here we
+ * store the coordinates in PaintedLayer space of the top-left of the
+ * active scrolled root.
+ */
+ gfxPoint mAnimatedGeometryRootPosition;
+
+ nsIntRegion mRegionToInvalidate;
+
+ // The offset between the active scrolled root of this layer
+ // and the root of the container for the previous and current
+ // paints respectively.
+ nsPoint mLastAnimatedGeometryRootOrigin;
+ nsPoint mAnimatedGeometryRootOrigin;
+
+ RefPtr<ColorLayer> mColorLayer;
+ RefPtr<ImageLayer> mImageLayer;
+
+ // The region for which display item visibility for this layer has already
+ // been calculated. Used to reduce the number of calls to
+ // RecomputeVisibilityForItems if it is known in advance that a larger
+ // region will be painted during a transaction than in a single call to
+ // DrawPaintedLayer, for example when progressive paint is enabled.
+ nsIntRegion mVisibilityComputedRegion;
+};
+
+/*
+ * User data for layers which will be used as masks.
+ */
+struct MaskLayerUserData : public LayerUserData
+{
+ MaskLayerUserData()
+ : mScaleX(-1.0f)
+ , mScaleY(-1.0f)
+ , mAppUnitsPerDevPixel(-1)
+ { }
+ MaskLayerUserData(const DisplayItemClip& aClip,
+ uint32_t aRoundedRectClipCount,
+ int32_t aAppUnitsPerDevPixel,
+ const ContainerLayerParameters& aParams)
+ : mScaleX(aParams.mXScale)
+ , mScaleY(aParams.mYScale)
+ , mOffset(aParams.mOffset)
+ , mAppUnitsPerDevPixel(aAppUnitsPerDevPixel)
+ {
+ aClip.AppendRoundedRects(&mRoundedClipRects, aRoundedRectClipCount);
+ }
+
+ void operator=(MaskLayerUserData&& aOther)
+ {
+ mScaleX = aOther.mScaleX;
+ mScaleY = aOther.mScaleY;
+ mOffset = aOther.mOffset;
+ mAppUnitsPerDevPixel = aOther.mAppUnitsPerDevPixel;
+ mRoundedClipRects.SwapElements(aOther.mRoundedClipRects);
+ }
+
+ bool
+ operator== (const MaskLayerUserData& aOther) const
+ {
+ return mRoundedClipRects == aOther.mRoundedClipRects &&
+ mScaleX == aOther.mScaleX &&
+ mScaleY == aOther.mScaleY &&
+ mOffset == aOther.mOffset &&
+ mAppUnitsPerDevPixel == aOther.mAppUnitsPerDevPixel;
+ }
+
+ // Keeps a MaskLayerImageKey alive by managing its mLayerCount member-var
+ MaskLayerImageCache::MaskLayerImageKeyRef mImageKey;
+ // properties of the mask layer; the mask layer may be re-used if these
+ // remain unchanged.
+ nsTArray<DisplayItemClip::RoundedRect> mRoundedClipRects;
+ // scale from the masked layer which is applied to the mask
+ float mScaleX, mScaleY;
+ // The ContainerLayerParameters offset which is applied to the mask's transform.
+ nsIntPoint mOffset;
+ int32_t mAppUnitsPerDevPixel;
+};
+
+/*
+ * User data for layers which will be used as masks for css positioned mask.
+ */
+struct CSSMaskLayerUserData : public LayerUserData
+{
+ CSSMaskLayerUserData()
+ : mImageLayers(nsStyleImageLayers::LayerType::Mask)
+ { }
+
+ CSSMaskLayerUserData(nsIFrame* aFrame, const nsIntRect& aBounds)
+ : mImageLayers(aFrame->StyleSVGReset()->mMask),
+ mContentRect(aFrame->GetContentRectRelativeToSelf()),
+ mPaddingRect(aFrame->GetPaddingRectRelativeToSelf()),
+ mBorderRect(aFrame->GetRectRelativeToSelf()),
+ mMarginRect(aFrame->GetMarginRectRelativeToSelf()),
+ mBounds(aBounds)
+ {
+ Hash(aFrame);
+ }
+
+ CSSMaskLayerUserData& operator=(const CSSMaskLayerUserData& aOther)
+ {
+ mImageLayers = aOther.mImageLayers;
+
+ mContentRect = aOther.mContentRect;
+ mPaddingRect = aOther.mPaddingRect;
+ mBorderRect = aOther.mBorderRect;
+ mMarginRect = aOther.mMarginRect;
+
+ mBounds = aOther.mBounds;
+
+ mHash = aOther.mHash;
+
+ return *this;
+ }
+
+ bool
+ operator==(const CSSMaskLayerUserData& aOther) const
+ {
+ if (mHash != aOther.mHash) {
+ return false;
+ }
+
+ if (mImageLayers.mLayers != aOther.mImageLayers.mLayers) {
+ return false;
+ }
+
+ if (!mContentRect.IsEqualEdges(aOther.mContentRect) ||
+ !mPaddingRect.IsEqualEdges(aOther.mPaddingRect) ||
+ !mBorderRect.IsEqualEdges(aOther.mBorderRect) ||
+ !mMarginRect.IsEqualEdges(aOther.mMarginRect)) {
+ return false;
+ }
+
+ if (!mBounds.IsEqualEdges(aOther.mBounds)) {
+ return false;
+ }
+
+ return true;
+ }
+
+private:
+ void Hash(nsIFrame* aFrame)
+ {
+ uint32_t hash = 0;
+
+ const nsStyleImageLayers& imageLayers = aFrame->StyleSVGReset()->mMask;
+ for (uint32_t i = 0; i < imageLayers.mLayers.Length(); i++) {
+ const nsStyleImageLayers::Layer& newLayer = imageLayers.mLayers[i];
+ hash = AddToHash(hash, HashBytes(&newLayer, sizeof(newLayer)));
+ }
+
+ hash = AddToHash(hash, HashBytes(&mContentRect, sizeof(mContentRect)));
+ hash = AddToHash(hash, HashBytes(&mPaddingRect, sizeof(mPaddingRect)));
+ hash = AddToHash(hash, HashBytes(&mBorderRect, sizeof(mBorderRect)));
+ hash = AddToHash(hash, HashBytes(&mMarginRect, sizeof(mMarginRect)));
+
+ hash = AddToHash(hash, HashBytes(&mBounds, sizeof(mBounds)));
+
+ mHash = hash;
+ }
+
+ nsStyleImageLayers mImageLayers;
+
+ nsRect mContentRect;
+ nsRect mPaddingRect;
+ nsRect mBorderRect;
+ nsRect mMarginRect;
+
+ nsIntRect mBounds;
+
+ uint32_t mHash;
+};
+
+/*
+ * A helper object to create a draw target for painting mask and create a
+ * image container to hold the drawing result. The caller can then bind this
+ * image container with a image mask layer via ImageLayer::SetContainer.
+ */
+class MaskImageData
+{
+public:
+ MaskImageData(const gfx::IntSize& aSize, LayerManager* aLayerManager)
+ : mTextureClientLocked(false)
+ , mSize(aSize)
+ , mLayerManager(aLayerManager)
+ {
+ MOZ_ASSERT(!mSize.IsEmpty());
+ MOZ_ASSERT(mLayerManager);
+ }
+
+ ~MaskImageData()
+ {
+ if (mTextureClientLocked) {
+ MOZ_ASSERT(mTextureClient);
+ // Clear DrawTarget before Unlock.
+ mDrawTarget = nullptr;
+ mTextureClient->Unlock();
+ }
+ }
+
+ gfx::DrawTarget* CreateDrawTarget()
+ {
+ if (mDrawTarget) {
+ return mDrawTarget;
+ }
+
+ if (mLayerManager->GetBackendType() == LayersBackend::LAYERS_BASIC) {
+ mDrawTarget = mLayerManager->CreateOptimalMaskDrawTarget(mSize);
+ return mDrawTarget;
+ }
+
+ MOZ_ASSERT(mLayerManager->GetBackendType() == LayersBackend::LAYERS_CLIENT);
+
+ ShadowLayerForwarder* fwd = mLayerManager->AsShadowForwarder();
+ if (!fwd) {
+ return nullptr;
+ }
+ mTextureClient =
+ TextureClient::CreateForDrawing(fwd,
+ SurfaceFormat::A8,
+ mSize,
+ BackendSelector::Content,
+ TextureFlags::DISALLOW_BIGIMAGE,
+ TextureAllocationFlags::ALLOC_CLEAR_BUFFER);
+ if (!mTextureClient) {
+ return nullptr;
+ }
+
+ mTextureClientLocked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+ if (!mTextureClientLocked) {
+ return nullptr;
+ }
+
+ mDrawTarget = mTextureClient->BorrowDrawTarget();
+ return mDrawTarget;
+ }
+
+ already_AddRefed<ImageContainer> CreateImageAndImageContainer()
+ {
+ RefPtr<ImageContainer> container = mLayerManager->CreateImageContainer();
+ RefPtr<Image> image = CreateImage();
+
+ if (!image) {
+ return nullptr;
+ }
+ container->SetCurrentImageInTransaction(image);
+
+ return container.forget();
+ }
+
+private:
+ already_AddRefed<Image> CreateImage()
+ {
+ if (mLayerManager->GetBackendType() == LayersBackend::LAYERS_BASIC &&
+ mDrawTarget) {
+ RefPtr<SourceSurface> surface = mDrawTarget->Snapshot();
+ RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(mSize, surface);
+ // Disallow BIGIMAGE (splitting into multiple textures) for mask
+ // layer images
+ image->SetTextureFlags(TextureFlags::DISALLOW_BIGIMAGE);
+ return image.forget();
+ }
+
+ if (mLayerManager->GetBackendType() == LayersBackend::LAYERS_CLIENT &&
+ mTextureClient &&
+ mDrawTarget) {
+ RefPtr<TextureWrapperImage> image =
+ new TextureWrapperImage(mTextureClient, gfx::IntRect(gfx::IntPoint(0, 0), mSize));
+ return image.forget();
+ }
+
+ return nullptr;
+ }
+
+ bool mTextureClientLocked;
+ gfx::IntSize mSize;
+ LayerManager* mLayerManager;
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+/**
+ * Helper functions for getting user data and casting it to the correct type.
+ * aLayer is the layer where the user data is stored.
+ */
+MaskLayerUserData* GetMaskLayerUserData(Layer* aLayer)
+{
+ return static_cast<MaskLayerUserData*>(aLayer->GetUserData(&gMaskLayerUserData));
+}
+
+PaintedDisplayItemLayerUserData* GetPaintedDisplayItemLayerUserData(Layer* aLayer)
+{
+ return static_cast<PaintedDisplayItemLayerUserData*>(
+ aLayer->GetUserData(&gPaintedDisplayItemLayerUserData));
+}
+
+/* static */ void
+FrameLayerBuilder::Shutdown()
+{
+ if (gMaskLayerImageCache) {
+ delete gMaskLayerImageCache;
+ gMaskLayerImageCache = nullptr;
+ }
+}
+
+void
+FrameLayerBuilder::Init(nsDisplayListBuilder* aBuilder, LayerManager* aManager,
+ PaintedLayerData* aLayerData)
+{
+ mDisplayListBuilder = aBuilder;
+ mRootPresContext = aBuilder->RootReferenceFrame()->PresContext()->GetRootPresContext();
+ if (mRootPresContext) {
+ mInitialDOMGeneration = mRootPresContext->GetDOMGeneration();
+ }
+ mContainingPaintedLayer = aLayerData;
+ aManager->SetUserData(&gLayerManagerLayerBuilder, this);
+}
+
+void
+FrameLayerBuilder::FlashPaint(gfxContext *aContext)
+{
+ float r = float(rand()) / RAND_MAX;
+ float g = float(rand()) / RAND_MAX;
+ float b = float(rand()) / RAND_MAX;
+ aContext->SetColor(Color(r, g, b, 0.4f));
+ aContext->Paint();
+}
+
+static FrameLayerBuilder::DisplayItemData*
+AssertDisplayItemData(FrameLayerBuilder::DisplayItemData* aData)
+{
+ MOZ_RELEASE_ASSERT(aData);
+ MOZ_RELEASE_ASSERT(sAliveDisplayItemDatas && sAliveDisplayItemDatas->Contains(aData));
+ MOZ_RELEASE_ASSERT(aData->mLayer);
+ return aData;
+}
+
+FrameLayerBuilder::DisplayItemData*
+FrameLayerBuilder::GetDisplayItemData(nsIFrame* aFrame, uint32_t aKey)
+{
+ const nsTArray<DisplayItemData*>* array =
+ aFrame->Properties().Get(LayerManagerDataProperty());
+ if (array) {
+ for (uint32_t i = 0; i < array->Length(); i++) {
+ DisplayItemData* item = AssertDisplayItemData(array->ElementAt(i));
+ if (item->mDisplayItemKey == aKey &&
+ item->mLayer->Manager() == mRetainingManager) {
+ return item;
+ }
+ }
+ }
+ return nullptr;
+}
+
+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 nsIntRegion& r,
+ const char* pfx="", const char* sfx="")
+{
+ s += pfx;
+
+ s += "< ";
+ for (auto iter = r.RectIter(); !iter.Done(); iter.Next()) {
+ AppendToString(s, iter.Get()) += "; ";
+ }
+ s += ">";
+
+ return s += sfx;
+}
+
+/**
+ * Invalidate aRegion in aLayer. aLayer is in the coordinate system
+ * *after* aTranslation has been applied, so we need to
+ * apply the inverse of that transform before calling InvalidateRegion.
+ */
+static void
+InvalidatePostTransformRegion(PaintedLayer* aLayer, const nsIntRegion& aRegion,
+ const nsIntPoint& aTranslation)
+{
+ // Convert the region from the coordinates of the container layer
+ // (relative to the snapped top-left of the display list reference frame)
+ // to the PaintedLayer's own coordinates
+ nsIntRegion rgn = aRegion;
+ rgn.MoveBy(-aTranslation);
+ aLayer->InvalidateRegion(rgn);
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ nsAutoCString str;
+ AppendToString(str, rgn);
+ printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
+ }
+#endif
+}
+
+static void
+InvalidatePostTransformRegion(PaintedLayer* aLayer, const nsRect& aRect,
+ const DisplayItemClip& aClip,
+ const nsIntPoint& aTranslation)
+{
+ PaintedDisplayItemLayerUserData* data =
+ static_cast<PaintedDisplayItemLayerUserData*>(aLayer->GetUserData(&gPaintedDisplayItemLayerUserData));
+
+ nsRect rect = aClip.ApplyNonRoundedIntersection(aRect);
+
+ nsIntRect pixelRect = rect.ScaleToOutsidePixels(data->mXScale, data->mYScale, data->mAppUnitsPerDevPixel);
+ InvalidatePostTransformRegion(aLayer, pixelRect, aTranslation);
+}
+
+
+static nsIntPoint
+GetTranslationForPaintedLayer(PaintedLayer* aLayer)
+{
+ PaintedDisplayItemLayerUserData* data =
+ static_cast<PaintedDisplayItemLayerUserData*>
+ (aLayer->GetUserData(&gPaintedDisplayItemLayerUserData));
+ NS_ASSERTION(data, "Must be a tracked painted layer!");
+
+ return data->mTranslation;
+}
+
+/**
+ * Some frames can have multiple, nested, retaining layer managers
+ * associated with them (normal manager, inactive managers, SVG effects).
+ * In these cases we store the 'outermost' LayerManager data property
+ * on the frame since we can walk down the chain from there.
+ *
+ * If one of these frames has just been destroyed, we will free the inner
+ * layer manager when removing the entry from mFramesWithLayers. Destroying
+ * the layer manager destroys the LayerManagerData and calls into
+ * the DisplayItemData destructor. If the inner layer manager had any
+ * items with the same frame, then we attempt to retrieve properties
+ * from the deleted frame.
+ *
+ * Cache the destroyed frame pointer here so we can avoid crashing in this case.
+ */
+
+/* static */ void
+FrameLayerBuilder::RemoveFrameFromLayerManager(const nsIFrame* aFrame,
+ nsTArray<DisplayItemData*>* aArray)
+{
+ MOZ_RELEASE_ASSERT(!sDestroyedFrame);
+ sDestroyedFrame = aFrame;
+
+ // Hold a reference to all the items so that they don't get
+ // deleted from under us.
+ nsTArray<RefPtr<DisplayItemData> > arrayCopy;
+ for (DisplayItemData* data : *aArray) {
+ arrayCopy.AppendElement(data);
+ }
+
+#ifdef DEBUG_DISPLAY_ITEM_DATA
+ if (aArray->Length()) {
+ LayerManagerData *rootData = aArray->ElementAt(0)->mParent;
+ while (rootData->mParent) {
+ rootData = rootData->mParent;
+ }
+ printf_stderr("Removing frame %p - dumping display data\n", aFrame);
+ rootData->Dump();
+ }
+#endif
+
+ for (DisplayItemData* data : *aArray) {
+ PaintedLayer* t = data->mLayer->AsPaintedLayer();
+ if (t) {
+ PaintedDisplayItemLayerUserData* paintedData =
+ static_cast<PaintedDisplayItemLayerUserData*>(t->GetUserData(&gPaintedDisplayItemLayerUserData));
+ if (paintedData) {
+ nsRegion old = data->mGeometry->ComputeInvalidationRegion();
+ nsIntRegion rgn = old.ScaleToOutsidePixels(paintedData->mXScale, paintedData->mYScale, paintedData->mAppUnitsPerDevPixel);
+ rgn.MoveBy(-GetTranslationForPaintedLayer(t));
+ paintedData->mRegionToInvalidate.Or(paintedData->mRegionToInvalidate, rgn);
+ paintedData->mRegionToInvalidate.SimplifyOutward(8);
+ }
+ }
+
+ data->mParent->mDisplayItems.RemoveEntry(data);
+ }
+
+ arrayCopy.Clear();
+ delete aArray;
+ sDestroyedFrame = nullptr;
+}
+
+void
+FrameLayerBuilder::DidBeginRetainedLayerTransaction(LayerManager* aManager)
+{
+ mRetainingManager = aManager;
+ LayerManagerData* data = static_cast<LayerManagerData*>
+ (aManager->GetUserData(&gLayerManagerUserData));
+ if (data) {
+ mInvalidateAllLayers = data->mInvalidateAllLayers;
+ } else {
+ data = new LayerManagerData(aManager);
+ aManager->SetUserData(&gLayerManagerUserData, data);
+ }
+}
+
+void
+FrameLayerBuilder::StoreOptimizedLayerForFrame(nsDisplayItem* aItem, Layer* aLayer)
+{
+ if (!mRetainingManager) {
+ return;
+ }
+
+ DisplayItemData* data = GetDisplayItemDataForManager(aItem, aLayer->Manager());
+ NS_ASSERTION(data, "Must have already stored data for this item!");
+ data->mOptLayer = aLayer;
+}
+
+void
+FrameLayerBuilder::DidEndTransaction()
+{
+ GetMaskLayerImageCache()->Sweep();
+}
+
+void
+FrameLayerBuilder::WillEndTransaction()
+{
+ if (!mRetainingManager) {
+ return;
+ }
+
+ // We need to save the data we'll need to support retaining.
+ LayerManagerData* data = static_cast<LayerManagerData*>
+ (mRetainingManager->GetUserData(&gLayerManagerUserData));
+ NS_ASSERTION(data, "Must have data!");
+
+ // Update all the frames that used to have layers.
+ for (auto iter = data->mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
+ DisplayItemData* data = iter.Get()->GetKey();
+ if (!data->mUsed) {
+ // This item was visible, but isn't anymore.
+ PaintedLayer* t = data->mLayer->AsPaintedLayer();
+ if (t && data->mGeometry) {
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ printf_stderr("Invalidating unused display item (%i) belonging to frame %p from layer %p\n", data->mDisplayItemKey, data->mFrameList[0], t);
+ }
+#endif
+ InvalidatePostTransformRegion(t,
+ data->mGeometry->ComputeInvalidationRegion(),
+ data->mClip,
+ GetLastPaintOffset(t));
+ }
+
+ data->ClearAnimationCompositorState();
+ iter.Remove();
+ } else {
+ ComputeGeometryChangeForItem(data);
+ }
+ }
+
+ data->mInvalidateAllLayers = false;
+}
+
+/* static */ FrameLayerBuilder::DisplayItemData*
+FrameLayerBuilder::GetDisplayItemDataForManager(nsDisplayItem* aItem,
+ LayerManager* aManager)
+{
+ const nsTArray<DisplayItemData*>* array =
+ aItem->Frame()->Properties().Get(LayerManagerDataProperty());
+ if (array) {
+ for (uint32_t i = 0; i < array->Length(); i++) {
+ DisplayItemData* item = AssertDisplayItemData(array->ElementAt(i));
+ if (item->mDisplayItemKey == aItem->GetPerFrameKey() &&
+ item->mLayer->Manager() == aManager) {
+ return item;
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool
+FrameLayerBuilder::HasRetainedDataFor(nsIFrame* aFrame, uint32_t aDisplayItemKey)
+{
+ const nsTArray<DisplayItemData*>* array =
+ aFrame->Properties().Get(LayerManagerDataProperty());
+ if (array) {
+ for (uint32_t i = 0; i < array->Length(); i++) {
+ if (AssertDisplayItemData(array->ElementAt(i))->mDisplayItemKey == aDisplayItemKey) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void
+FrameLayerBuilder::IterateRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback)
+{
+ const nsTArray<DisplayItemData*>* array =
+ aFrame->Properties().Get(LayerManagerDataProperty());
+ if (!array) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < array->Length(); i++) {
+ DisplayItemData* data = AssertDisplayItemData(array->ElementAt(i));
+ if (data->mDisplayItemKey != nsDisplayItem::TYPE_ZERO) {
+ aCallback(aFrame, data);
+ }
+ }
+}
+
+FrameLayerBuilder::DisplayItemData*
+FrameLayerBuilder::GetOldLayerForFrame(nsIFrame* aFrame, uint32_t aDisplayItemKey)
+{
+ // If we need to build a new layer tree, then just refuse to recycle
+ // anything.
+ if (!mRetainingManager || mInvalidateAllLayers)
+ return nullptr;
+
+ DisplayItemData *data = GetDisplayItemData(aFrame, aDisplayItemKey);
+
+ if (data && data->mLayer->Manager() == mRetainingManager) {
+ return data;
+ }
+ return nullptr;
+}
+
+Layer*
+FrameLayerBuilder::GetOldLayerFor(nsDisplayItem* aItem,
+ nsDisplayItemGeometry** aOldGeometry,
+ DisplayItemClip** aOldClip)
+{
+ uint32_t key = aItem->GetPerFrameKey();
+ nsIFrame* frame = aItem->Frame();
+
+ DisplayItemData* oldData = GetOldLayerForFrame(frame, key);
+ if (oldData) {
+ if (aOldGeometry) {
+ *aOldGeometry = oldData->mGeometry.get();
+ }
+ if (aOldClip) {
+ *aOldClip = &oldData->mClip;
+ }
+ return oldData->mLayer;
+ }
+
+ return nullptr;
+}
+
+void
+FrameLayerBuilder::ClearCachedGeometry(nsDisplayItem* aItem)
+{
+ uint32_t key = aItem->GetPerFrameKey();
+ nsIFrame* frame = aItem->Frame();
+
+ DisplayItemData* oldData = GetOldLayerForFrame(frame, key);
+ if (oldData) {
+ oldData->mGeometry = nullptr;
+ }
+}
+
+/* static */ Layer*
+FrameLayerBuilder::GetDebugOldLayerFor(nsIFrame* aFrame, uint32_t aDisplayItemKey)
+{
+ const nsTArray<DisplayItemData*>* array =
+ aFrame->Properties().Get(LayerManagerDataProperty());
+
+ if (!array) {
+ return nullptr;
+ }
+
+ for (uint32_t i = 0; i < array->Length(); i++) {
+ DisplayItemData *data = AssertDisplayItemData(array->ElementAt(i));
+
+ if (data->mDisplayItemKey == aDisplayItemKey) {
+ return data->mLayer;
+ }
+ }
+ return nullptr;
+}
+
+/* static */ PaintedLayer*
+FrameLayerBuilder::GetDebugSingleOldPaintedLayerForFrame(nsIFrame* aFrame)
+{
+ const nsTArray<DisplayItemData*>* array =
+ aFrame->Properties().Get(LayerManagerDataProperty());
+
+ if (!array) {
+ return nullptr;
+ }
+
+ Layer* layer = nullptr;
+ for (DisplayItemData* data : *array) {
+ AssertDisplayItemData(data);
+ if (!data->mLayer->AsPaintedLayer()) {
+ continue;
+ }
+ if (layer && layer != data->mLayer) {
+ // More than one layer assigned, bail.
+ return nullptr;
+ }
+ layer = data->mLayer;
+ }
+
+ if (!layer) {
+ return nullptr;
+ }
+
+ return layer->AsPaintedLayer();
+}
+
+// Reset state that should not persist when a layer is recycled.
+static void
+ResetLayerStateForRecycling(Layer* aLayer) {
+ // Currently, this clears the mask layer and ancestor mask layers.
+ // Other cleanup may be added here.
+ aLayer->SetMaskLayer(nullptr);
+ aLayer->SetAncestorMaskLayers({});
+}
+
+already_AddRefed<ColorLayer>
+ContainerState::CreateOrRecycleColorLayer(PaintedLayer *aPainted)
+{
+ PaintedDisplayItemLayerUserData* data =
+ static_cast<PaintedDisplayItemLayerUserData*>(aPainted->GetUserData(&gPaintedDisplayItemLayerUserData));
+ RefPtr<ColorLayer> layer = data->mColorLayer;
+ if (layer) {
+ ResetLayerStateForRecycling(layer);
+ layer->ClearExtraDumpInfo();
+ } else {
+ // Create a new layer
+ layer = mManager->CreateColorLayer();
+ if (!layer)
+ return nullptr;
+ // Mark this layer as being used for painting display items
+ data->mColorLayer = layer;
+ layer->SetUserData(&gColorLayerUserData, nullptr);
+
+ // Remove other layer types we might have stored for this PaintedLayer
+ data->mImageLayer = nullptr;
+ }
+ return layer.forget();
+}
+
+already_AddRefed<ImageLayer>
+ContainerState::CreateOrRecycleImageLayer(PaintedLayer *aPainted)
+{
+ PaintedDisplayItemLayerUserData* data =
+ static_cast<PaintedDisplayItemLayerUserData*>(aPainted->GetUserData(&gPaintedDisplayItemLayerUserData));
+ RefPtr<ImageLayer> layer = data->mImageLayer;
+ if (layer) {
+ ResetLayerStateForRecycling(layer);
+ layer->ClearExtraDumpInfo();
+ } else {
+ // Create a new layer
+ layer = mManager->CreateImageLayer();
+ if (!layer)
+ return nullptr;
+ // Mark this layer as being used for painting display items
+ data->mImageLayer = layer;
+ layer->SetUserData(&gImageLayerUserData, nullptr);
+
+ // Remove other layer types we might have stored for this PaintedLayer
+ data->mColorLayer = nullptr;
+ }
+ return layer.forget();
+}
+
+already_AddRefed<ImageLayer>
+ContainerState::CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey,
+ mozilla::function<void(Layer* aLayer)> aSetUserData)
+{
+ RefPtr<ImageLayer> result = mRecycledMaskImageLayers.Get(aKey);
+ if (result) {
+ mRecycledMaskImageLayers.Remove(aKey);
+ aKey.mLayer->ClearExtraDumpInfo();
+ // XXX if we use clip on mask layers, null it out here
+ } else {
+ // Create a new layer
+ result = mManager->CreateImageLayer();
+ if (!result)
+ return nullptr;
+ aSetUserData(result);
+ }
+
+ return result.forget();
+}
+
+static const double SUBPIXEL_OFFSET_EPSILON = 0.02;
+
+/**
+ * This normally computes NSToIntRoundUp(aValue). However, if that would
+ * give a residual near 0.5 while aOldResidual is near -0.5, or
+ * it would give a residual near -0.5 while aOldResidual is near 0.5, then
+ * instead we return the integer in the other direction so that the residual
+ * is close to aOldResidual.
+ */
+static int32_t
+RoundToMatchResidual(double aValue, double aOldResidual)
+{
+ int32_t v = NSToIntRoundUp(aValue);
+ double residual = aValue - v;
+ if (aOldResidual < 0) {
+ if (residual > 0 && fabs(residual - 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) {
+ // Round up instead
+ return int32_t(ceil(aValue));
+ }
+ } else if (aOldResidual > 0) {
+ if (residual < 0 && fabs(residual + 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) {
+ // Round down instead
+ return int32_t(floor(aValue));
+ }
+ }
+ return v;
+}
+
+static void
+ResetScrollPositionForLayerPixelAlignment(AnimatedGeometryRoot* aAnimatedGeometryRoot)
+{
+ nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot);
+ if (sf) {
+ sf->ResetScrollPositionForLayerPixelAlignment();
+ }
+}
+
+static void
+InvalidateEntirePaintedLayer(PaintedLayer* aLayer, AnimatedGeometryRoot* aAnimatedGeometryRoot, const char *aReason)
+{
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ printf_stderr("Invalidating entire layer %p: %s\n", aLayer, aReason);
+ }
+#endif
+ nsIntRect invalidate = aLayer->GetValidRegion().GetBounds();
+ aLayer->InvalidateRegion(invalidate);
+ aLayer->SetInvalidRectToVisibleRegion();
+ ResetScrollPositionForLayerPixelAlignment(aAnimatedGeometryRoot);
+}
+
+LayerManager::PaintedLayerCreationHint
+ContainerState::GetLayerCreationHint(AnimatedGeometryRoot* aAnimatedGeometryRoot)
+{
+ // Check whether the layer will be scrollable. This is used as a hint to
+ // influence whether tiled layers are used or not.
+
+ // Check creation hint inherited from our parent.
+ if (mParameters.mLayerCreationHint == LayerManager::SCROLLABLE) {
+ return LayerManager::SCROLLABLE;
+ }
+
+ // Check whether there's any active scroll frame on the animated geometry
+ // root chain.
+ for (AnimatedGeometryRoot* agr = aAnimatedGeometryRoot;
+ agr && agr != mContainerAnimatedGeometryRoot;
+ agr = agr->mParentAGR) {
+ nsIFrame* fParent = nsLayoutUtils::GetCrossDocParentFrame(*agr);
+ if (!fParent) {
+ break;
+ }
+ nsIScrollableFrame* scrollable = do_QueryFrame(fParent);
+ if (scrollable
+ #ifdef MOZ_B2G
+ && scrollable->WantAsyncScroll()
+ #endif
+ ) {
+ // WantAsyncScroll() returns false when the frame has overflow:hidden,
+ // so we won't create tiled layers for overflow:hidden frames even if
+ // they have a display port. The main purpose of the WantAsyncScroll check
+ // is to allow the B2G camera app to use hardware composer for compositing.
+ return LayerManager::SCROLLABLE;
+ }
+ }
+ return LayerManager::NONE;
+}
+
+already_AddRefed<PaintedLayer>
+ContainerState::AttemptToRecyclePaintedLayer(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ nsDisplayItem* aItem,
+ const nsPoint& aTopLeft)
+{
+ Layer* oldLayer = mLayerBuilder->GetOldLayerFor(aItem);
+ if (!oldLayer || !oldLayer->AsPaintedLayer() ||
+ !mPaintedLayersAvailableForRecycling.Contains(oldLayer->AsPaintedLayer())) {
+ return nullptr;
+ }
+
+ // Try to recycle a layer
+ RefPtr<PaintedLayer> layer = oldLayer->AsPaintedLayer();
+ mPaintedLayersAvailableForRecycling.RemoveEntry(layer);
+
+ // Check if the layer hint has changed and whether or not the layer should
+ // be recreated because of it.
+ if (!layer->IsOptimizedFor(GetLayerCreationHint(aAnimatedGeometryRoot))) {
+ return nullptr;
+ }
+
+ bool didResetScrollPositionForLayerPixelAlignment = false;
+ PaintedDisplayItemLayerUserData* data =
+ RecyclePaintedLayer(layer, aAnimatedGeometryRoot,
+ didResetScrollPositionForLayerPixelAlignment);
+ PreparePaintedLayerForUse(layer, data, aAnimatedGeometryRoot, aItem->ReferenceFrame(),
+ aTopLeft,
+ didResetScrollPositionForLayerPixelAlignment);
+
+ return layer.forget();
+}
+
+already_AddRefed<PaintedLayer>
+ContainerState::CreatePaintedLayer(PaintedLayerData* aData)
+{
+ LayerManager::PaintedLayerCreationHint creationHint =
+ GetLayerCreationHint(aData->mAnimatedGeometryRoot);
+
+ // Create a new painted layer
+ RefPtr<PaintedLayer> layer = mManager->CreatePaintedLayerWithHint(creationHint);
+ if (!layer) {
+ return nullptr;
+ }
+
+ // Mark this layer as being used for painting display items
+ PaintedDisplayItemLayerUserData* userData = new PaintedDisplayItemLayerUserData();
+ layer->SetUserData(&gPaintedDisplayItemLayerUserData, userData);
+ ResetScrollPositionForLayerPixelAlignment(aData->mAnimatedGeometryRoot);
+
+ PreparePaintedLayerForUse(layer, userData, aData->mAnimatedGeometryRoot,
+ aData->mReferenceFrame,
+ aData->mAnimatedGeometryRootOffset, true);
+
+ return layer.forget();
+}
+
+PaintedDisplayItemLayerUserData*
+ContainerState::RecyclePaintedLayer(PaintedLayer* aLayer,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ bool& didResetScrollPositionForLayerPixelAlignment)
+{
+ // Clear clip rect and mask layer so we don't accidentally stay clipped.
+ // We will reapply any necessary clipping.
+ ResetLayerStateForRecycling(aLayer);
+ aLayer->ClearExtraDumpInfo();
+
+ PaintedDisplayItemLayerUserData* data =
+ static_cast<PaintedDisplayItemLayerUserData*>(
+ aLayer->GetUserData(&gPaintedDisplayItemLayerUserData));
+ NS_ASSERTION(data, "Recycled PaintedLayers must have user data");
+
+ // This gets called on recycled PaintedLayers that are going to be in the
+ // final layer tree, so it's a convenient time to invalidate the
+ // content that changed where we don't know what PaintedLayer it belonged
+ // to, or if we need to invalidate the entire layer, we can do that.
+ // This needs to be done before we update the PaintedLayer to its new
+ // transform. See nsGfxScrollFrame::InvalidateInternal, where
+ // we ensure that mInvalidPaintedContent is updated according to the
+ // scroll position as of the most recent paint.
+ if (!FuzzyEqual(data->mXScale, mParameters.mXScale, 0.00001f) ||
+ !FuzzyEqual(data->mYScale, mParameters.mYScale, 0.00001f) ||
+ data->mAppUnitsPerDevPixel != mAppUnitsPerDevPixel) {
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ printf_stderr("Recycled layer %p changed scale\n", aLayer);
+ }
+#endif
+ InvalidateEntirePaintedLayer(aLayer, aAnimatedGeometryRoot, "recycled layer changed state");
+ didResetScrollPositionForLayerPixelAlignment = true;
+ }
+ if (!data->mRegionToInvalidate.IsEmpty()) {
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ printf_stderr("Invalidating deleted frame content from layer %p\n", aLayer);
+ }
+#endif
+ aLayer->InvalidateRegion(data->mRegionToInvalidate);
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ nsAutoCString str;
+ AppendToString(str, data->mRegionToInvalidate);
+ printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
+ }
+#endif
+ data->mRegionToInvalidate.SetEmpty();
+ }
+ return data;
+}
+
+void
+ContainerState::PreparePaintedLayerForUse(PaintedLayer* aLayer,
+ PaintedDisplayItemLayerUserData* aData,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const nsIFrame* aReferenceFrame,
+ const nsPoint& aTopLeft,
+ bool didResetScrollPositionForLayerPixelAlignment)
+{
+ aData->mXScale = mParameters.mXScale;
+ aData->mYScale = mParameters.mYScale;
+ aData->mLastAnimatedGeometryRootOrigin = aData->mAnimatedGeometryRootOrigin;
+ aData->mAnimatedGeometryRootOrigin = aTopLeft;
+ aData->mAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
+ aLayer->SetAllowResidualTranslation(mParameters.AllowResidualTranslation());
+
+ mLayerBuilder->SavePreviousDataForLayer(aLayer, aData->mMaskClipCount);
+
+ // Set up transform so that 0,0 in the PaintedLayer corresponds to the
+ // (pixel-snapped) top-left of the aAnimatedGeometryRoot.
+ nsPoint offset = (*aAnimatedGeometryRoot)->GetOffsetToCrossDoc(aReferenceFrame);
+ nscoord appUnitsPerDevPixel = (*aAnimatedGeometryRoot)->PresContext()->AppUnitsPerDevPixel();
+ gfxPoint scaledOffset(
+ NSAppUnitsToDoublePixels(offset.x, appUnitsPerDevPixel)*mParameters.mXScale,
+ NSAppUnitsToDoublePixels(offset.y, appUnitsPerDevPixel)*mParameters.mYScale);
+ // We call RoundToMatchResidual here so that the residual after rounding
+ // is close to aData->mAnimatedGeometryRootPosition if possible.
+ nsIntPoint pixOffset(RoundToMatchResidual(scaledOffset.x, aData->mAnimatedGeometryRootPosition.x),
+ RoundToMatchResidual(scaledOffset.y, aData->mAnimatedGeometryRootPosition.y));
+ aData->mTranslation = pixOffset;
+ pixOffset += mParameters.mOffset;
+ Matrix matrix = Matrix::Translation(pixOffset.x, pixOffset.y);
+ aLayer->SetBaseTransform(Matrix4x4::From2D(matrix));
+
+ aData->mVisibilityComputedRegion.SetEmpty();
+
+ // FIXME: Temporary workaround for bug 681192 and bug 724786.
+#ifndef MOZ_WIDGET_ANDROID
+ // Calculate exact position of the top-left of the active scrolled root.
+ // This might not be 0,0 due to the snapping in ScaleToNearestPixels.
+ gfxPoint animatedGeometryRootTopLeft = scaledOffset - ThebesPoint(matrix.GetTranslation()) + mParameters.mOffset;
+ // If it has changed, then we need to invalidate the entire layer since the
+ // pixels in the layer buffer have the content at a (subpixel) offset
+ // from what we need.
+ if (!animatedGeometryRootTopLeft.WithinEpsilonOf(aData->mAnimatedGeometryRootPosition, SUBPIXEL_OFFSET_EPSILON)) {
+ aData->mAnimatedGeometryRootPosition = animatedGeometryRootTopLeft;
+ InvalidateEntirePaintedLayer(aLayer, aAnimatedGeometryRoot, "subpixel offset");
+ } else if (didResetScrollPositionForLayerPixelAlignment) {
+ aData->mAnimatedGeometryRootPosition = animatedGeometryRootTopLeft;
+ }
+#else
+ Unused << didResetScrollPositionForLayerPixelAlignment;
+#endif
+}
+
+#if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
+/**
+ * Returns the appunits per dev pixel for the item's frame
+ */
+static int32_t
+AppUnitsPerDevPixel(nsDisplayItem* aItem)
+{
+ // The underlying frame for zoom items is the root frame of the subdocument.
+ // But zoom display items report their bounds etc using the parent document's
+ // APD because zoom items act as a conversion layer between the two different
+ // APDs.
+ if (aItem->GetType() == nsDisplayItem::TYPE_ZOOM) {
+ return static_cast<nsDisplayZoom*>(aItem)->GetParentAppUnitsPerDevPixel();
+ }
+ return aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
+}
+#endif
+
+/**
+ * Set the visible region for aLayer.
+ * aOuterVisibleRegion is the visible region relative to the parent layer.
+ * aLayerContentsVisibleRect, if non-null, is a rectangle in the layer's
+ * own coordinate system to which the layer's visible region is restricted.
+ * Consumes *aOuterVisibleRegion.
+ */
+static void
+SetOuterVisibleRegion(Layer* aLayer, nsIntRegion* aOuterVisibleRegion,
+ const nsIntRect* aLayerContentsVisibleRect = nullptr,
+ bool aOuterUntransformed = false)
+{
+ Matrix4x4 transform = aLayer->GetTransform();
+ Matrix transform2D;
+ if (aOuterUntransformed) {
+ if (aLayerContentsVisibleRect) {
+ aOuterVisibleRegion->And(*aOuterVisibleRegion,
+ *aLayerContentsVisibleRect);
+ }
+ } else if (transform.Is2D(&transform2D) && !transform2D.HasNonIntegerTranslation()) {
+ aOuterVisibleRegion->MoveBy(-int(transform2D._31), -int(transform2D._32));
+ if (aLayerContentsVisibleRect) {
+ aOuterVisibleRegion->And(*aOuterVisibleRegion, *aLayerContentsVisibleRect);
+ }
+ } else {
+ nsIntRect outerRect = aOuterVisibleRegion->GetBounds();
+ // if 'transform' is not invertible, then nothing will be displayed
+ // for the layer, so it doesn't really matter what we do here
+ Rect outerVisible(outerRect.x, outerRect.y, outerRect.width, outerRect.height);
+ transform.Invert();
+
+ Rect layerContentsVisible(-float(INT32_MAX) / 2, -float(INT32_MAX) / 2,
+ float(INT32_MAX), float(INT32_MAX));
+ if (aLayerContentsVisibleRect) {
+ NS_ASSERTION(aLayerContentsVisibleRect->width >= 0 &&
+ aLayerContentsVisibleRect->height >= 0,
+ "Bad layer contents rectangle");
+ // restrict to aLayerContentsVisibleRect before call GfxRectToIntRect,
+ // in case layerVisible is extremely large (as it can be when
+ // projecting through the inverse of a 3D transform)
+ layerContentsVisible = Rect(
+ aLayerContentsVisibleRect->x, aLayerContentsVisibleRect->y,
+ aLayerContentsVisibleRect->width, aLayerContentsVisibleRect->height);
+ }
+ gfxRect layerVisible = ThebesRect(transform.ProjectRectBounds(outerVisible, layerContentsVisible));
+ layerVisible.RoundOut();
+ nsIntRect visRect;
+ if (gfxUtils::GfxRectToIntRect(layerVisible, &visRect)) {
+ *aOuterVisibleRegion = visRect;
+ } else {
+ aOuterVisibleRegion->SetEmpty();
+ }
+ }
+
+ aLayer->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(*aOuterVisibleRegion));
+}
+
+void
+ContainerState::SetOuterVisibleRegionForLayer(Layer* aLayer,
+ const nsIntRegion& aOuterVisibleRegion,
+ const nsIntRect* aLayerContentsVisibleRect,
+ bool aOuterUntransformed) const
+{
+ nsIntRegion visRegion = aOuterVisibleRegion;
+ if (!aOuterUntransformed) {
+ visRegion.MoveBy(mParameters.mOffset);
+ }
+ SetOuterVisibleRegion(aLayer, &visRegion, aLayerContentsVisibleRect,
+ aOuterUntransformed);
+}
+
+nscolor
+ContainerState::FindOpaqueBackgroundColorInLayer(const PaintedLayerData* aData,
+ const nsIntRect& aRect,
+ bool* aOutIntersectsLayer) const
+{
+ *aOutIntersectsLayer = true;
+
+ // Scan the candidate's display items.
+ nsIntRect deviceRect = aRect;
+ nsRect appUnitRect = ToAppUnits(deviceRect, mAppUnitsPerDevPixel);
+ appUnitRect.ScaleInverseRoundOut(mParameters.mXScale, mParameters.mYScale);
+
+ for (auto& assignedItem : Reversed(aData->mAssignedDisplayItems)) {
+ nsDisplayItem* item = assignedItem.mItem;
+ bool snap;
+ nsRect bounds = item->GetBounds(mBuilder, &snap);
+ if (snap && mSnappingEnabled) {
+ nsIntRect snappedBounds = ScaleToNearestPixels(bounds);
+ if (!snappedBounds.Intersects(deviceRect))
+ continue;
+
+ if (!snappedBounds.Contains(deviceRect))
+ return NS_RGBA(0,0,0,0);
+
+ } else {
+ // The layer's visible rect is already (close enough to) pixel
+ // aligned, so no need to round out and in here.
+ if (!bounds.Intersects(appUnitRect))
+ continue;
+
+ if (!bounds.Contains(appUnitRect))
+ return NS_RGBA(0,0,0,0);
+ }
+
+ if (item->IsInvisibleInRect(appUnitRect)) {
+ continue;
+ }
+
+ if (assignedItem.mClip.IsRectAffectedByClip(deviceRect,
+ mParameters.mXScale,
+ mParameters.mYScale,
+ mAppUnitsPerDevPixel)) {
+ return NS_RGBA(0,0,0,0);
+ }
+
+ Maybe<nscolor> color = item->IsUniform(mBuilder);
+ if (color && NS_GET_A(*color) == 255)
+ return *color;
+
+ return NS_RGBA(0,0,0,0);
+ }
+
+ *aOutIntersectsLayer = false;
+ return NS_RGBA(0,0,0,0);
+}
+
+nscolor
+PaintedLayerDataNode::FindOpaqueBackgroundColor(const nsIntRegion& aTargetVisibleRegion,
+ int32_t aUnderIndex) const
+{
+ if (aUnderIndex == ABOVE_TOP) {
+ aUnderIndex = mPaintedLayerDataStack.Length();
+ }
+ for (int32_t i = aUnderIndex - 1; i >= 0; --i) {
+ const PaintedLayerData* candidate = &mPaintedLayerDataStack[i];
+ if (candidate->VisibleAboveRegionIntersects(aTargetVisibleRegion)) {
+ // Some non-PaintedLayer content between target and candidate; this is
+ // hopeless
+ return NS_RGBA(0,0,0,0);
+ }
+
+ if (!candidate->VisibleRegionIntersects(aTargetVisibleRegion)) {
+ // The layer doesn't intersect our target, ignore it and move on
+ continue;
+ }
+
+ bool intersectsLayer = true;
+ nsIntRect rect = aTargetVisibleRegion.GetBounds();
+ nscolor color = mTree.ContState().FindOpaqueBackgroundColorInLayer(
+ candidate, rect, &intersectsLayer);
+ if (!intersectsLayer) {
+ continue;
+ }
+ return color;
+ }
+ if (mAllDrawingAboveBackground ||
+ !mVisibleAboveBackgroundRegion.Intersect(aTargetVisibleRegion).IsEmpty()) {
+ // Some non-PaintedLayer content is between this node's background and target.
+ return NS_RGBA(0,0,0,0);
+ }
+ return FindOpaqueBackgroundColorInParentNode();
+}
+
+nscolor
+PaintedLayerDataNode::FindOpaqueBackgroundColorCoveringEverything() const
+{
+ if (!mPaintedLayerDataStack.IsEmpty() ||
+ mAllDrawingAboveBackground ||
+ !mVisibleAboveBackgroundRegion.IsEmpty()) {
+ return NS_RGBA(0,0,0,0);
+ }
+ return FindOpaqueBackgroundColorInParentNode();
+}
+
+nscolor
+PaintedLayerDataNode::FindOpaqueBackgroundColorInParentNode() const
+{
+ if (mParent) {
+ if (mHasClip) {
+ // Check whether our parent node has uniform content behind our whole
+ // clip.
+ // There's one tricky case here: If our parent node is also a scrollable,
+ // and is currently scrolled in such a way that this inner one is
+ // clipped by it, then it's not really clear how we should determine
+ // whether we have a uniform background in the parent: There might be
+ // non-uniform content in the parts that our scroll port covers in the
+ // parent and that are currently outside the parent's clip.
+ // For now, we'll fail to pull a background color in that case.
+ return mParent->FindOpaqueBackgroundColor(mClipRect);
+ }
+ return mParent->FindOpaqueBackgroundColorCoveringEverything();
+ }
+ // We are the root.
+ return mTree.UniformBackgroundColor();
+}
+
+void
+PaintedLayerData::UpdateCommonClipCount(
+ const DisplayItemClip& aCurrentClip)
+{
+ if (mCommonClipCount >= 0) {
+ mCommonClipCount = mItemClip.GetCommonRoundedRectCount(aCurrentClip, mCommonClipCount);
+ } else {
+ // first item in the layer
+ mCommonClipCount = aCurrentClip.GetRoundedRectCount();
+ }
+}
+
+bool
+PaintedLayerData::CanOptimizeToImageLayer(nsDisplayListBuilder* aBuilder)
+{
+ if (!mImage) {
+ return false;
+ }
+
+ return mImage->CanOptimizeToImageLayer(mLayer->Manager(), aBuilder);
+}
+
+already_AddRefed<ImageContainer>
+PaintedLayerData::GetContainerForImageLayer(nsDisplayListBuilder* aBuilder)
+{
+ if (!mImage) {
+ return nullptr;
+ }
+
+ return mImage->GetContainer(mLayer->Manager(), aBuilder);
+}
+
+PaintedLayerDataNode::PaintedLayerDataNode(PaintedLayerDataTree& aTree,
+ PaintedLayerDataNode* aParent,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot)
+ : mTree(aTree)
+ , mParent(aParent)
+ , mAnimatedGeometryRoot(aAnimatedGeometryRoot)
+ , mAllDrawingAboveBackground(false)
+{
+ MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(mTree.Builder()->RootReferenceFrame(), *mAnimatedGeometryRoot));
+ mHasClip = mTree.IsClippedWithRespectToParentAnimatedGeometryRoot(mAnimatedGeometryRoot, &mClipRect);
+}
+
+PaintedLayerDataNode::~PaintedLayerDataNode()
+{
+ MOZ_ASSERT(mPaintedLayerDataStack.IsEmpty());
+ MOZ_ASSERT(mChildren.IsEmpty());
+}
+
+PaintedLayerDataNode*
+PaintedLayerDataNode::AddChildNodeFor(AnimatedGeometryRoot* aAnimatedGeometryRoot)
+{
+ MOZ_ASSERT(aAnimatedGeometryRoot->mParentAGR == mAnimatedGeometryRoot);
+ UniquePtr<PaintedLayerDataNode> child =
+ MakeUnique<PaintedLayerDataNode>(mTree, this, aAnimatedGeometryRoot);
+ mChildren.AppendElement(Move(child));
+ return mChildren.LastElement().get();
+}
+
+template<typename NewPaintedLayerCallbackType>
+PaintedLayerData*
+PaintedLayerDataNode::FindPaintedLayerFor(const nsIntRect& aVisibleRect,
+ bool aBackfaceHidden,
+ const DisplayItemScrollClip* aScrollClip,
+ NewPaintedLayerCallbackType aNewPaintedLayerCallback)
+{
+ if (!mPaintedLayerDataStack.IsEmpty()) {
+ PaintedLayerData* lowestUsableLayer = nullptr;
+ for (auto& data : Reversed(mPaintedLayerDataStack)) {
+ if (data.mVisibleAboveRegion.Intersects(aVisibleRect)) {
+ break;
+ }
+ if (data.mBackfaceHidden == aBackfaceHidden &&
+ data.mScrollClip == aScrollClip) {
+ lowestUsableLayer = &data;
+ }
+ nsIntRegion visibleRegion = data.mVisibleRegion;
+ // Also check whether the event-regions intersect the visible rect,
+ // unless we're in an inactive layer, in which case the event-regions
+ // will be hoisted out into their own layer.
+ // For performance reasons, we check the intersection with the bounds
+ // of the event-regions.
+ if (!mTree.ContState().IsInInactiveLayer() &&
+ (data.mScaledHitRegionBounds.Intersects(aVisibleRect) ||
+ data.mScaledMaybeHitRegionBounds.Intersects(aVisibleRect))) {
+ break;
+ }
+ if (visibleRegion.Intersects(aVisibleRect)) {
+ break;
+ }
+ }
+ if (lowestUsableLayer) {
+ return lowestUsableLayer;
+ }
+ }
+ return mPaintedLayerDataStack.AppendElement(aNewPaintedLayerCallback());
+}
+
+void
+PaintedLayerDataNode::FinishChildrenIntersecting(const nsIntRect& aRect)
+{
+ for (int32_t i = mChildren.Length() - 1; i >= 0; i--) {
+ if (mChildren[i]->Intersects(aRect)) {
+ mChildren[i]->Finish(true);
+ mChildren.RemoveElementAt(i);
+ }
+ }
+}
+
+void
+PaintedLayerDataNode::FinishAllChildren(bool aThisNodeNeedsAccurateVisibleAboveRegion)
+{
+ for (int32_t i = mChildren.Length() - 1; i >= 0; i--) {
+ mChildren[i]->Finish(aThisNodeNeedsAccurateVisibleAboveRegion);
+ }
+ mChildren.Clear();
+}
+
+void
+PaintedLayerDataNode::Finish(bool aParentNeedsAccurateVisibleAboveRegion)
+{
+ // Skip "visible above region" maintenance, because this node is going away.
+ FinishAllChildren(false);
+
+ PopAllPaintedLayerData();
+
+ if (mParent && aParentNeedsAccurateVisibleAboveRegion) {
+ if (mHasClip) {
+ mParent->AddToVisibleAboveRegion(mClipRect);
+ } else {
+ mParent->SetAllDrawingAbove();
+ }
+ }
+ mTree.NodeWasFinished(mAnimatedGeometryRoot);
+}
+
+void
+PaintedLayerDataNode::AddToVisibleAboveRegion(const nsIntRect& aRect)
+{
+ nsIntRegion& visibleAboveRegion = mPaintedLayerDataStack.IsEmpty()
+ ? mVisibleAboveBackgroundRegion
+ : mPaintedLayerDataStack.LastElement().mVisibleAboveRegion;
+ visibleAboveRegion.Or(visibleAboveRegion, aRect);
+ visibleAboveRegion.SimplifyOutward(8);
+}
+
+void
+PaintedLayerDataNode::SetAllDrawingAbove()
+{
+ PopAllPaintedLayerData();
+ mAllDrawingAboveBackground = true;
+ mVisibleAboveBackgroundRegion.SetEmpty();
+}
+
+void
+PaintedLayerDataNode::PopPaintedLayerData()
+{
+ MOZ_ASSERT(!mPaintedLayerDataStack.IsEmpty());
+ size_t lastIndex = mPaintedLayerDataStack.Length() - 1;
+ PaintedLayerData& data = mPaintedLayerDataStack[lastIndex];
+ mTree.ContState().FinishPaintedLayerData(data, [this, &data, lastIndex]() {
+ return this->FindOpaqueBackgroundColor(data.mVisibleRegion, lastIndex);
+ });
+ mPaintedLayerDataStack.RemoveElementAt(lastIndex);
+}
+
+void
+PaintedLayerDataNode::PopAllPaintedLayerData()
+{
+ while (!mPaintedLayerDataStack.IsEmpty()) {
+ PopPaintedLayerData();
+ }
+}
+
+nsDisplayListBuilder*
+PaintedLayerDataTree::Builder() const
+{
+ return mContainerState.Builder();
+}
+
+void
+PaintedLayerDataTree::Finish()
+{
+ if (mRoot) {
+ mRoot->Finish(false);
+ }
+ MOZ_ASSERT(mNodes.Count() == 0);
+ mRoot = nullptr;
+}
+
+void
+PaintedLayerDataTree::NodeWasFinished(AnimatedGeometryRoot* aAnimatedGeometryRoot)
+{
+ mNodes.Remove(aAnimatedGeometryRoot);
+}
+
+void
+PaintedLayerDataTree::AddingOwnLayer(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const nsIntRect* aRect,
+ nscolor* aOutUniformBackgroundColor)
+{
+ FinishPotentiallyIntersectingNodes(aAnimatedGeometryRoot, aRect);
+ PaintedLayerDataNode* node = EnsureNodeFor(aAnimatedGeometryRoot);
+ if (aRect) {
+ if (aOutUniformBackgroundColor) {
+ *aOutUniformBackgroundColor = node->FindOpaqueBackgroundColor(*aRect);
+ }
+ node->AddToVisibleAboveRegion(*aRect);
+ } else {
+ if (aOutUniformBackgroundColor) {
+ *aOutUniformBackgroundColor = node->FindOpaqueBackgroundColorCoveringEverything();
+ }
+ node->SetAllDrawingAbove();
+ }
+}
+
+template<typename NewPaintedLayerCallbackType>
+PaintedLayerData*
+PaintedLayerDataTree::FindPaintedLayerFor(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const DisplayItemScrollClip* aScrollClip,
+ const nsIntRect& aVisibleRect,
+ bool aBackfaceHidden,
+ NewPaintedLayerCallbackType aNewPaintedLayerCallback)
+{
+ const nsIntRect* bounds = &aVisibleRect;
+ FinishPotentiallyIntersectingNodes(aAnimatedGeometryRoot, bounds);
+ PaintedLayerDataNode* node = EnsureNodeFor(aAnimatedGeometryRoot);
+
+ PaintedLayerData* data =
+ node->FindPaintedLayerFor(aVisibleRect, aBackfaceHidden, aScrollClip,
+ aNewPaintedLayerCallback);
+ return data;
+}
+
+void
+PaintedLayerDataTree::FinishPotentiallyIntersectingNodes(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const nsIntRect* aRect)
+{
+ AnimatedGeometryRoot* ancestorThatIsChildOfCommonAncestor = nullptr;
+ PaintedLayerDataNode* ancestorNode =
+ FindNodeForAncestorAnimatedGeometryRoot(aAnimatedGeometryRoot,
+ &ancestorThatIsChildOfCommonAncestor);
+ if (!ancestorNode) {
+ // None of our ancestors are in the tree. This should only happen if this
+ // is the very first item we're looking at.
+ MOZ_ASSERT(!mRoot);
+ return;
+ }
+
+ if (ancestorNode->GetAnimatedGeometryRoot() == aAnimatedGeometryRoot) {
+ // aAnimatedGeometryRoot already has a node in the tree.
+ // This is the common case.
+ MOZ_ASSERT(!ancestorThatIsChildOfCommonAncestor);
+ if (aRect) {
+ ancestorNode->FinishChildrenIntersecting(*aRect);
+ } else {
+ ancestorNode->FinishAllChildren();
+ }
+ return;
+ }
+
+ // We have found an existing ancestor, but it's a proper ancestor of our
+ // animated geometry root.
+ // ancestorThatIsChildOfCommonAncestor is the last animated geometry root
+ // encountered on the way up from aAnimatedGeometryRoot to ancestorNode.
+ MOZ_ASSERT(ancestorThatIsChildOfCommonAncestor);
+ MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(*ancestorThatIsChildOfCommonAncestor, *aAnimatedGeometryRoot));
+ MOZ_ASSERT(ancestorThatIsChildOfCommonAncestor->mParentAGR == ancestorNode->GetAnimatedGeometryRoot());
+
+ // ancestorThatIsChildOfCommonAncestor is not in the tree yet!
+ MOZ_ASSERT(!mNodes.Get(ancestorThatIsChildOfCommonAncestor));
+
+ // We're about to add a node for ancestorThatIsChildOfCommonAncestor, so we
+ // finish all intersecting siblings.
+ nsIntRect clip;
+ if (IsClippedWithRespectToParentAnimatedGeometryRoot(ancestorThatIsChildOfCommonAncestor, &clip)) {
+ ancestorNode->FinishChildrenIntersecting(clip);
+ } else {
+ ancestorNode->FinishAllChildren();
+ }
+}
+
+PaintedLayerDataNode*
+PaintedLayerDataTree::EnsureNodeFor(AnimatedGeometryRoot* aAnimatedGeometryRoot)
+{
+ MOZ_ASSERT(aAnimatedGeometryRoot);
+ PaintedLayerDataNode* node = mNodes.Get(aAnimatedGeometryRoot);
+ if (node) {
+ return node;
+ }
+
+ AnimatedGeometryRoot* parentAnimatedGeometryRoot = aAnimatedGeometryRoot->mParentAGR;
+ if (!parentAnimatedGeometryRoot) {
+ MOZ_ASSERT(!mRoot);
+ MOZ_ASSERT(*aAnimatedGeometryRoot == Builder()->RootReferenceFrame());
+ mRoot = MakeUnique<PaintedLayerDataNode>(*this, nullptr, aAnimatedGeometryRoot);
+ node = mRoot.get();
+ } else {
+ PaintedLayerDataNode* parentNode = EnsureNodeFor(parentAnimatedGeometryRoot);
+ MOZ_ASSERT(parentNode);
+ node = parentNode->AddChildNodeFor(aAnimatedGeometryRoot);
+ }
+ MOZ_ASSERT(node);
+ mNodes.Put(aAnimatedGeometryRoot, node);
+ return node;
+}
+
+bool
+PaintedLayerDataTree::IsClippedWithRespectToParentAnimatedGeometryRoot(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ nsIntRect* aOutClip)
+{
+ nsIScrollableFrame* scrollableFrame = nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot);
+ if (!scrollableFrame) {
+ return false;
+ }
+ nsIFrame* scrollFrame = do_QueryFrame(scrollableFrame);
+ nsRect scrollPort = scrollableFrame->GetScrollPortRect() + Builder()->ToReferenceFrame(scrollFrame);
+ *aOutClip = mContainerState.ScaleToNearestPixels(scrollPort);
+ return true;
+}
+
+PaintedLayerDataNode*
+PaintedLayerDataTree::FindNodeForAncestorAnimatedGeometryRoot(AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ AnimatedGeometryRoot** aOutAncestorChild)
+{
+ if (!aAnimatedGeometryRoot) {
+ return nullptr;
+ }
+ PaintedLayerDataNode* node = mNodes.Get(aAnimatedGeometryRoot);
+ if (node) {
+ return node;
+ }
+ *aOutAncestorChild = aAnimatedGeometryRoot;
+ return FindNodeForAncestorAnimatedGeometryRoot(aAnimatedGeometryRoot->mParentAGR, aOutAncestorChild);
+}
+
+static bool
+CanOptimizeAwayPaintedLayer(PaintedLayerData* aData,
+ FrameLayerBuilder* aLayerBuilder)
+{
+ if (!aLayerBuilder->IsBuildingRetainedLayers()) {
+ return false;
+ }
+
+ // If there's no painted layer with valid content in it that we can reuse,
+ // always create a color or image layer (and potentially throw away an
+ // existing completely invalid painted layer).
+ if (aData->mLayer->GetValidRegion().IsEmpty()) {
+ return true;
+ }
+
+ // There is an existing painted layer we can reuse. Throwing it away can make
+ // compositing cheaper (see bug 946952), but it might cause us to re-allocate
+ // the painted layer frequently due to an animation. So we only discard it if
+ // we're in tree compression mode, which is triggered at a low frequency.
+ return aLayerBuilder->CheckInLayerTreeCompressionMode();
+}
+
+#ifdef DEBUG
+static int32_t FindIndexOfLayerIn(nsTArray<NewLayerEntry>& aArray,
+ Layer* aLayer)
+{
+ for (uint32_t i = 0; i < aArray.Length(); ++i) {
+ if (aArray[i].mLayer == aLayer) {
+ return i;
+ }
+ }
+ return -1;
+}
+#endif
+
+already_AddRefed<Layer>
+ContainerState::PrepareImageLayer(PaintedLayerData* aData)
+{
+ RefPtr<ImageContainer> imageContainer =
+ aData->GetContainerForImageLayer(mBuilder);
+ if (!imageContainer) {
+ return nullptr;
+ }
+
+ RefPtr<ImageLayer> imageLayer = CreateOrRecycleImageLayer(aData->mLayer);
+ imageLayer->SetContainer(imageContainer);
+ aData->mImage->ConfigureLayer(imageLayer, mParameters);
+ imageLayer->SetPostScale(mParameters.mXScale,
+ mParameters.mYScale);
+
+ if (aData->mItemClip.HasClip()) {
+ ParentLayerIntRect clip =
+ ViewAs<ParentLayerPixel>(ScaleToNearestPixels(aData->mItemClip.GetClipRect()));
+ clip.MoveBy(ViewAs<ParentLayerPixel>(mParameters.mOffset));
+ imageLayer->SetClipRect(Some(clip));
+ } else {
+ imageLayer->SetClipRect(Nothing());
+ }
+
+ mLayerBuilder->StoreOptimizedLayerForFrame(aData->mImage, imageLayer);
+ FLB_LOG_PAINTED_LAYER_DECISION(aData,
+ " Selected image layer=%p\n", imageLayer.get());
+
+ return imageLayer.forget();
+}
+
+already_AddRefed<Layer>
+ContainerState::PrepareColorLayer(PaintedLayerData* aData)
+{
+ RefPtr<ColorLayer> colorLayer = CreateOrRecycleColorLayer(aData->mLayer);
+ colorLayer->SetColor(Color::FromABGR(aData->mSolidColor));
+
+ // Copy transform
+ colorLayer->SetBaseTransform(aData->mLayer->GetBaseTransform());
+ colorLayer->SetPostScale(aData->mLayer->GetPostXScale(),
+ aData->mLayer->GetPostYScale());
+
+ nsIntRect visibleRect = aData->mVisibleRegion.GetBounds();
+ visibleRect.MoveBy(-GetTranslationForPaintedLayer(aData->mLayer));
+ colorLayer->SetBounds(visibleRect);
+ colorLayer->SetClipRect(Nothing());
+
+ FLB_LOG_PAINTED_LAYER_DECISION(aData,
+ " Selected color layer=%p\n", colorLayer.get());
+
+ return colorLayer.forget();
+}
+
+static void
+SetBackfaceHiddenForLayer(bool aBackfaceHidden, Layer* aLayer)
+{
+ if (aBackfaceHidden) {
+ aLayer->SetContentFlags(aLayer->GetContentFlags() |
+ Layer::CONTENT_BACKFACE_HIDDEN);
+ } else {
+ aLayer->SetContentFlags(aLayer->GetContentFlags() &
+ ~Layer::CONTENT_BACKFACE_HIDDEN);
+ }
+}
+
+template<typename FindOpaqueBackgroundColorCallbackType>
+void ContainerState::FinishPaintedLayerData(PaintedLayerData& aData, FindOpaqueBackgroundColorCallbackType aFindOpaqueBackgroundColor)
+{
+ PaintedLayerData* data = &aData;
+
+ if (!data->mLayer) {
+ // No layer was recycled, so we create a new one.
+ RefPtr<PaintedLayer> paintedLayer = CreatePaintedLayer(data);
+ data->mLayer = paintedLayer;
+
+ NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, paintedLayer) < 0,
+ "Layer already in list???");
+ mNewChildLayers[data->mNewChildLayersIndex].mLayer = paintedLayer.forget();
+ }
+
+ for (auto& item : data->mAssignedDisplayItems) {
+ MOZ_ASSERT(item.mItem->GetType() != nsDisplayItem::TYPE_LAYER_EVENT_REGIONS);
+
+ InvalidateForLayerChange(item.mItem, data->mLayer);
+ mLayerBuilder->AddPaintedDisplayItem(data, item.mItem, item.mClip,
+ *this, item.mLayerState,
+ data->mAnimatedGeometryRootOffset);
+ }
+
+ NewLayerEntry* newLayerEntry = &mNewChildLayers[data->mNewChildLayersIndex];
+
+ RefPtr<Layer> layer;
+ bool canOptimizeToImageLayer = data->CanOptimizeToImageLayer(mBuilder);
+
+ FLB_LOG_PAINTED_LAYER_DECISION(data, "Selecting layer for pld=%p\n", data);
+ FLB_LOG_PAINTED_LAYER_DECISION(data, " Solid=%i, hasImage=%c, canOptimizeAwayPaintedLayer=%i\n",
+ data->mIsSolidColorInVisibleRegion, canOptimizeToImageLayer ? 'y' : 'n',
+ CanOptimizeAwayPaintedLayer(data, mLayerBuilder));
+
+ if ((data->mIsSolidColorInVisibleRegion || canOptimizeToImageLayer) &&
+ CanOptimizeAwayPaintedLayer(data, mLayerBuilder)) {
+ NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && canOptimizeToImageLayer),
+ "Can't be a solid color as well as an image!");
+
+ layer = canOptimizeToImageLayer ? PrepareImageLayer(data)
+ : PrepareColorLayer(data);
+
+ if (layer) {
+ NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, layer) < 0,
+ "Layer already in list???");
+ NS_ASSERTION(newLayerEntry->mLayer == data->mLayer,
+ "Painted layer at wrong index");
+ // Store optimized layer in reserved slot
+ newLayerEntry = &mNewChildLayers[data->mNewChildLayersIndex + 1];
+ NS_ASSERTION(!newLayerEntry->mLayer, "Slot already occupied?");
+ newLayerEntry->mLayer = layer;
+ newLayerEntry->mAnimatedGeometryRoot = data->mAnimatedGeometryRoot;
+ newLayerEntry->mScrollClip = data->mScrollClip;
+
+ // Hide the PaintedLayer. We leave it in the layer tree so that we
+ // can find and recycle it later.
+ ParentLayerIntRect emptyRect;
+ data->mLayer->SetClipRect(Some(emptyRect));
+ data->mLayer->SetVisibleRegion(LayerIntRegion());
+ data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion().GetBounds());
+ data->mLayer->SetEventRegions(EventRegions());
+ }
+ }
+
+ if (!layer) {
+ // We couldn't optimize to an image layer or a color layer above.
+ layer = data->mLayer;
+ layer->SetClipRect(Nothing());
+ FLB_LOG_PAINTED_LAYER_DECISION(data, " Selected painted layer=%p\n", layer.get());
+ }
+
+ // If the layer is a fixed background layer, the clip on the fixed background
+ // display item was not applied to the opaque region in
+ // ContainerState::ComputeOpaqueRect(), but was saved in data->mItemClip.
+ // Apply it to the opaque region now. Note that it's important to do this
+ // before the opaque region is propagated to the NewLayerEntry below.
+ if (data->mSingleItemFixedToViewport && data->mItemClip.HasClip()) {
+ nsRect clipRect = data->mItemClip.GetClipRect();
+ nsRect insideRoundedCorners = data->mItemClip.ApproximateIntersectInward(clipRect);
+ nsIntRect insideRoundedCornersScaled = ScaleToInsidePixels(insideRoundedCorners);
+ data->mOpaqueRegion.AndWith(insideRoundedCornersScaled);
+ }
+
+ if (mLayerBuilder->IsBuildingRetainedLayers()) {
+ newLayerEntry->mVisibleRegion = data->mVisibleRegion;
+ newLayerEntry->mOpaqueRegion = data->mOpaqueRegion;
+ newLayerEntry->mHideAllLayersBelow = data->mHideAllLayersBelow;
+ newLayerEntry->mOpaqueForAnimatedGeometryRootParent = data->mOpaqueForAnimatedGeometryRootParent;
+ } else {
+ SetOuterVisibleRegionForLayer(layer, data->mVisibleRegion);
+ }
+
+ nsIntRect layerBounds = data->mBounds;
+ layerBounds.MoveBy(-GetTranslationForPaintedLayer(data->mLayer));
+ layer->SetLayerBounds(layerBounds);
+
+#ifdef MOZ_DUMP_PAINTING
+ if (!data->mLog.IsEmpty()) {
+ if (PaintedLayerData* containingPld = mLayerBuilder->GetContainingPaintedLayerData()) {
+ containingPld->mLayer->AddExtraDumpInfo(nsCString(data->mLog));
+ } else {
+ layer->AddExtraDumpInfo(nsCString(data->mLog));
+ }
+ }
+#endif
+
+ nsIntRegion transparentRegion;
+ transparentRegion.Sub(data->mVisibleRegion, data->mOpaqueRegion);
+ bool isOpaque = transparentRegion.IsEmpty();
+ // For translucent PaintedLayers, try to find an opaque background
+ // color that covers the entire area beneath it so we can pull that
+ // color into this layer to make it opaque.
+ if (layer == data->mLayer) {
+ nscolor backgroundColor = NS_RGBA(0,0,0,0);
+ if (!isOpaque) {
+ backgroundColor = aFindOpaqueBackgroundColor();
+ if (NS_GET_A(backgroundColor) == 255) {
+ isOpaque = true;
+ }
+ }
+
+ // Store the background color
+ PaintedDisplayItemLayerUserData* userData =
+ GetPaintedDisplayItemLayerUserData(data->mLayer);
+ NS_ASSERTION(userData, "where did our user data go?");
+ if (userData->mForcedBackgroundColor != backgroundColor) {
+ // Invalidate the entire target PaintedLayer since we're changing
+ // the background color
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ printf_stderr("Forced background color has changed from #%08X to #%08X on layer %p\n",
+ userData->mForcedBackgroundColor, backgroundColor, data->mLayer);
+ nsAutoCString str;
+ AppendToString(str, data->mLayer->GetValidRegion());
+ printf_stderr("Invalidating layer %p: %s\n", data->mLayer, str.get());
+ }
+#endif
+ data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion());
+ }
+ userData->mForcedBackgroundColor = backgroundColor;
+
+ userData->mFontSmoothingBackgroundColor = data->mFontSmoothingBackgroundColor;
+
+ // use a mask layer for rounded rect clipping.
+ // data->mCommonClipCount may be -1 if we haven't put any actual
+ // drawable items in this layer (i.e. it's only catching events).
+ int32_t commonClipCount;
+ // If the layer contains a single item fixed to the viewport, we removed
+ // its clip in ProcessDisplayItems() and saved it to set on the layer instead.
+ // Set the clip on the layer now.
+ if (data->mSingleItemFixedToViewport && data->mItemClip.HasClip()) {
+ nsIntRect layerClipRect = ScaleToNearestPixels(data->mItemClip.GetClipRect());
+ layerClipRect.MoveBy(mParameters.mOffset);
+ // The clip from such an item becomes part of the layer's scrolled clip,
+ // and the associated mask layer one of the layer's "ancestor mask layers".
+ LayerClip scrolledClip;
+ scrolledClip.SetClipRect(ViewAs<ParentLayerPixel>(layerClipRect));
+ scrolledClip.SetMaskLayerIndex(
+ SetupMaskLayerForScrolledClip(data->mLayer, data->mItemClip));
+ data->mLayer->SetScrolledClip(Some(scrolledClip));
+ // There is only one item, so all of the clips are in common to all items.
+ // data->mCommonClipCount will be zero because we removed the clip from
+ // the display item. (It could also be -1 if we're inside an inactive
+ // layer tree in which we don't call UpdateCommonClipCount() at all.)
+ MOZ_ASSERT(data->mCommonClipCount == -1 || data->mCommonClipCount == 0);
+ commonClipCount = data->mItemClip.GetRoundedRectCount();
+ } else {
+ commonClipCount = std::max(0, data->mCommonClipCount);
+ SetupMaskLayer(layer, data->mItemClip, commonClipCount);
+ }
+ // copy commonClipCount to the entry
+ FrameLayerBuilder::PaintedLayerItemsEntry* entry = mLayerBuilder->
+ GetPaintedLayerItemsEntry(static_cast<PaintedLayer*>(layer.get()));
+ entry->mCommonClipCount = commonClipCount;
+ } else {
+ // mask layer for image and color layers
+ SetupMaskLayer(layer, data->mItemClip);
+ }
+
+ uint32_t flags = 0;
+ nsIWidget* widget = mContainerReferenceFrame->PresContext()->GetRootWidget();
+ // See bug 941095. Not quite ready to disable this.
+ bool hidpi = false && widget && widget->GetDefaultScale().scale >= 2;
+ if (hidpi) {
+ flags |= Layer::CONTENT_DISABLE_SUBPIXEL_AA;
+ }
+ if (isOpaque && !data->mForceTransparentSurface) {
+ flags |= Layer::CONTENT_OPAQUE;
+ } else if (data->mNeedComponentAlpha && !hidpi) {
+ flags |= Layer::CONTENT_COMPONENT_ALPHA;
+ }
+ if (data->mDisableFlattening) {
+ flags |= Layer::CONTENT_DISABLE_FLATTENING;
+ }
+ layer->SetContentFlags(flags);
+
+ PaintedLayerData* containingPaintedLayerData =
+ mLayerBuilder->GetContainingPaintedLayerData();
+ if (containingPaintedLayerData) {
+ if (!data->mDispatchToContentHitRegion.GetBounds().IsEmpty()) {
+ nsRect rect = nsLayoutUtils::TransformFrameRectToAncestor(
+ mContainerReferenceFrame,
+ data->mDispatchToContentHitRegion.GetBounds(),
+ containingPaintedLayerData->mReferenceFrame);
+ containingPaintedLayerData->mDispatchToContentHitRegion.Or(
+ containingPaintedLayerData->mDispatchToContentHitRegion, rect);
+ }
+ if (!data->mMaybeHitRegion.GetBounds().IsEmpty()) {
+ nsRect rect = nsLayoutUtils::TransformFrameRectToAncestor(
+ mContainerReferenceFrame,
+ data->mMaybeHitRegion.GetBounds(),
+ containingPaintedLayerData->mReferenceFrame);
+ containingPaintedLayerData->mMaybeHitRegion.Or(
+ containingPaintedLayerData->mMaybeHitRegion, rect);
+ containingPaintedLayerData->mMaybeHitRegion.SimplifyOutward(8);
+ }
+ Maybe<Matrix4x4> matrixCache;
+ nsLayoutUtils::TransformToAncestorAndCombineRegions(
+ data->mHitRegion,
+ mContainerReferenceFrame,
+ containingPaintedLayerData->mReferenceFrame,
+ &containingPaintedLayerData->mHitRegion,
+ &containingPaintedLayerData->mMaybeHitRegion,
+ &matrixCache);
+ // See the comment in nsDisplayList::AddFrame, where the touch action regions
+ // are handled. The same thing applies here.
+ bool alreadyHadRegions =
+ !containingPaintedLayerData->mNoActionRegion.IsEmpty() ||
+ !containingPaintedLayerData->mHorizontalPanRegion.IsEmpty() ||
+ !containingPaintedLayerData->mVerticalPanRegion.IsEmpty();
+ nsLayoutUtils::TransformToAncestorAndCombineRegions(
+ data->mNoActionRegion,
+ mContainerReferenceFrame,
+ containingPaintedLayerData->mReferenceFrame,
+ &containingPaintedLayerData->mNoActionRegion,
+ &containingPaintedLayerData->mDispatchToContentHitRegion,
+ &matrixCache);
+ nsLayoutUtils::TransformToAncestorAndCombineRegions(
+ data->mHorizontalPanRegion,
+ mContainerReferenceFrame,
+ containingPaintedLayerData->mReferenceFrame,
+ &containingPaintedLayerData->mHorizontalPanRegion,
+ &containingPaintedLayerData->mDispatchToContentHitRegion,
+ &matrixCache);
+ nsLayoutUtils::TransformToAncestorAndCombineRegions(
+ data->mVerticalPanRegion,
+ mContainerReferenceFrame,
+ containingPaintedLayerData->mReferenceFrame,
+ &containingPaintedLayerData->mVerticalPanRegion,
+ &containingPaintedLayerData->mDispatchToContentHitRegion,
+ &matrixCache);
+ if (alreadyHadRegions) {
+ containingPaintedLayerData->mDispatchToContentHitRegion.OrWith(
+ containingPaintedLayerData->CombinedTouchActionRegion());
+ }
+ } else {
+ EventRegions regions;
+ regions.mHitRegion = ScaleRegionToOutsidePixels(data->mHitRegion);
+ regions.mNoActionRegion = ScaleRegionToOutsidePixels(data->mNoActionRegion);
+ regions.mHorizontalPanRegion = ScaleRegionToOutsidePixels(data->mHorizontalPanRegion);
+ regions.mVerticalPanRegion = ScaleRegionToOutsidePixels(data->mVerticalPanRegion);
+ // Points whose hit-region status we're not sure about need to be dispatched
+ // to the content thread. If a point is in both maybeHitRegion and hitRegion
+ // then it's not a "maybe" any more, and doesn't go into the dispatch-to-
+ // content region.
+ nsIntRegion maybeHitRegion = ScaleRegionToOutsidePixels(data->mMaybeHitRegion);
+ regions.mDispatchToContentHitRegion.Sub(maybeHitRegion, regions.mHitRegion);
+ regions.mDispatchToContentHitRegion.OrWith(
+ ScaleRegionToOutsidePixels(data->mDispatchToContentHitRegion));
+ regions.mHitRegion.OrWith(maybeHitRegion);
+
+ Matrix mat = layer->GetTransform().As2D();
+ mat.Invert();
+ regions.ApplyTranslationAndScale(mat._31, mat._32, mat._11, mat._22);
+
+ layer->SetEventRegions(regions);
+ }
+
+ SetBackfaceHiddenForLayer(data->mBackfaceHidden, data->mLayer);
+ if (layer != data->mLayer) {
+ SetBackfaceHiddenForLayer(data->mBackfaceHidden, layer);
+ }
+}
+
+static bool
+IsItemAreaInWindowOpaqueRegion(nsDisplayListBuilder* aBuilder,
+ nsDisplayItem* aItem,
+ const nsRect& aComponentAlphaBounds)
+{
+ if (!aItem->Frame()->PresContext()->IsChrome()) {
+ // Assume that Web content is always in the window opaque region.
+ return true;
+ }
+ if (aItem->ReferenceFrame() != aBuilder->RootReferenceFrame()) {
+ // aItem is probably in some transformed subtree.
+ // We're not going to bother figuring out where this landed, we're just
+ // going to assume it might have landed over a transparent part of
+ // the window.
+ return false;
+ }
+ return aBuilder->GetWindowOpaqueRegion().Contains(aComponentAlphaBounds);
+}
+
+void
+PaintedLayerData::Accumulate(ContainerState* aState,
+ nsDisplayItem* aItem,
+ const nsIntRegion& aClippedOpaqueRegion,
+ const nsIntRect& aVisibleRect,
+ const DisplayItemClip& aClip,
+ LayerState aLayerState)
+{
+ FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating dp=%s(%p), f=%p against pld=%p\n", aItem->Name(), aItem, aItem->Frame(), this);
+
+ bool snap;
+ nsRect itemBounds = aItem->GetBounds(aState->mBuilder, &snap);
+ mBounds = mBounds.Union(aState->ScaleToOutsidePixels(itemBounds, snap));
+
+ if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) {
+ mForceTransparentSurface = true;
+ }
+ if (aState->mParameters.mDisableSubpixelAntialiasingInDescendants) {
+ // Disable component alpha.
+ // Note that the transform (if any) on the PaintedLayer is always an integer translation so
+ // we don't have to factor that in here.
+ aItem->DisableComponentAlpha();
+ }
+
+ bool clipMatches = mItemClip == aClip;
+ mItemClip = aClip;
+
+ mAssignedDisplayItems.AppendElement(AssignedDisplayItem(aItem, aClip, aLayerState));
+
+ if (!mIsSolidColorInVisibleRegion && mOpaqueRegion.Contains(aVisibleRect) &&
+ mVisibleRegion.Contains(aVisibleRect) && !mImage) {
+ // A very common case! Most pages have a PaintedLayer with the page
+ // background (opaque) visible and most or all of the page content over the
+ // top of that background.
+ // The rest of this method won't do anything. mVisibleRegion and mOpaqueRegion
+ // don't need updating. mVisibleRegion contains aVisibleRect already,
+ // mOpaqueRegion contains aVisibleRect and therefore whatever the opaque
+ // region of the item is. mVisibleRegion must contain mOpaqueRegion
+ // and therefore aVisibleRect.
+ return;
+ }
+
+ /* Mark as available for conversion to image layer if this is a nsDisplayImage and
+ * it's the only thing visible in this layer.
+ */
+ if (nsIntRegion(aVisibleRect).Contains(mVisibleRegion) &&
+ aClippedOpaqueRegion.Contains(mVisibleRegion) &&
+ aItem->SupportsOptimizingToImage()) {
+ mImage = static_cast<nsDisplayImageContainer*>(aItem);
+ FLB_LOG_PAINTED_LAYER_DECISION(this, " Tracking image: nsDisplayImageContainer covers the layer\n");
+ } else if (mImage) {
+ FLB_LOG_PAINTED_LAYER_DECISION(this, " No longer tracking image\n");
+ mImage = nullptr;
+ }
+
+ bool isFirstVisibleItem = mVisibleRegion.IsEmpty();
+ if (isFirstVisibleItem) {
+ nscolor fontSmoothingBGColor;
+ if (aItem->ProvidesFontSmoothingBackgroundColor(&fontSmoothingBGColor)) {
+ mFontSmoothingBackgroundColor = fontSmoothingBGColor;
+ }
+ }
+
+ Maybe<nscolor> uniformColor = aItem->IsUniform(aState->mBuilder);
+
+ // Some display items have to exist (so they can set forceTransparentSurface
+ // below) but don't draw anything. They'll return true for isUniform but
+ // a color with opacity 0.
+ if (!uniformColor || NS_GET_A(*uniformColor) > 0) {
+ // Make sure that the visible area is covered by uniform pixels. In
+ // particular this excludes cases where the edges of the item are not
+ // pixel-aligned (thus the item will not be truly uniform).
+ if (uniformColor) {
+ bool snap;
+ nsRect bounds = aItem->GetBounds(aState->mBuilder, &snap);
+ if (!aState->ScaleToInsidePixels(bounds, snap).Contains(aVisibleRect)) {
+ uniformColor = Nothing();
+ FLB_LOG_PAINTED_LAYER_DECISION(this, " Display item does not cover the visible rect\n");
+ }
+ }
+ if (uniformColor) {
+ if (isFirstVisibleItem) {
+ // This color is all we have
+ mSolidColor = *uniformColor;
+ mIsSolidColorInVisibleRegion = true;
+ } else if (mIsSolidColorInVisibleRegion &&
+ mVisibleRegion.IsEqual(nsIntRegion(aVisibleRect)) &&
+ clipMatches) {
+ // we can just blend the colors together
+ mSolidColor = NS_ComposeColors(mSolidColor, *uniformColor);
+ } else {
+ FLB_LOG_PAINTED_LAYER_DECISION(this, " Layer not a solid color: Can't blend colors togethers\n");
+ mIsSolidColorInVisibleRegion = false;
+ }
+ } else {
+ FLB_LOG_PAINTED_LAYER_DECISION(this, " Layer is not a solid color: Display item is not uniform over the visible bound\n");
+ mIsSolidColorInVisibleRegion = false;
+ }
+
+ mVisibleRegion.Or(mVisibleRegion, aVisibleRect);
+ mVisibleRegion.SimplifyOutward(4);
+ }
+
+ if (!aClippedOpaqueRegion.IsEmpty()) {
+ for (auto iter = aClippedOpaqueRegion.RectIter(); !iter.Done(); iter.Next()) {
+ // We don't use SimplifyInward here since it's not defined exactly
+ // what it will discard. For our purposes the most important case
+ // is a large opaque background at the bottom of z-order (e.g.,
+ // a canvas background), so we need to make sure that the first rect
+ // we see doesn't get discarded.
+ nsIntRegion tmp;
+ tmp.Or(mOpaqueRegion, iter.Get());
+ // Opaque display items in chrome documents whose window is partially
+ // transparent are always added to the opaque region. This helps ensure
+ // that we get as much subpixel-AA as possible in the chrome.
+ if (tmp.GetNumRects() <= 4 || aItem->Frame()->PresContext()->IsChrome()) {
+ mOpaqueRegion = Move(tmp);
+ }
+ }
+ }
+
+ if (!aState->mParameters.mDisableSubpixelAntialiasingInDescendants) {
+ nsRect componentAlpha = aItem->GetComponentAlphaBounds(aState->mBuilder);
+ if (!componentAlpha.IsEmpty()) {
+ nsIntRect componentAlphaRect =
+ aState->ScaleToOutsidePixels(componentAlpha, false).Intersect(aVisibleRect);
+ if (!mOpaqueRegion.Contains(componentAlphaRect)) {
+ if (IsItemAreaInWindowOpaqueRegion(aState->mBuilder, aItem,
+ componentAlpha.Intersect(aItem->GetVisibleRect()))) {
+ mNeedComponentAlpha = true;
+ } else {
+ aItem->DisableComponentAlpha();
+ }
+ }
+ }
+ }
+
+ // Ensure animated text does not get flattened, even if it forces other
+ // content in the container to be layerized. The content backend might
+ // not support subpixel positioning of text that animated transforms can
+ // generate. bug 633097
+ if (aState->mParameters.mInActiveTransformedSubtree &&
+ (mNeedComponentAlpha ||
+ !aItem->GetComponentAlphaBounds(aState->mBuilder).IsEmpty())) {
+ mDisableFlattening = true;
+ }
+}
+
+nsRegion
+PaintedLayerData::CombinedTouchActionRegion()
+{
+ nsRegion result;
+ result.Or(mHorizontalPanRegion, mVerticalPanRegion);
+ result.OrWith(mNoActionRegion);
+ return result;
+}
+
+void
+PaintedLayerData::AccumulateEventRegions(ContainerState* aState, nsDisplayLayerEventRegions* aEventRegions)
+{
+ FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating event regions %p against pld=%p\n", aEventRegions, this);
+
+ mHitRegion.OrWith(aEventRegions->HitRegion());
+ mMaybeHitRegion.OrWith(aEventRegions->MaybeHitRegion());
+ mDispatchToContentHitRegion.OrWith(aEventRegions->DispatchToContentHitRegion());
+
+ // See the comment in nsDisplayList::AddFrame, where the touch action regions
+ // are handled. The same thing applies here.
+ bool alreadyHadRegions = !mNoActionRegion.IsEmpty() ||
+ !mHorizontalPanRegion.IsEmpty() ||
+ !mVerticalPanRegion.IsEmpty();
+ mNoActionRegion.OrWith(aEventRegions->NoActionRegion());
+ mHorizontalPanRegion.OrWith(aEventRegions->HorizontalPanRegion());
+ mVerticalPanRegion.OrWith(aEventRegions->VerticalPanRegion());
+ if (alreadyHadRegions) {
+ mDispatchToContentHitRegion.OrWith(CombinedTouchActionRegion());
+ }
+
+ // Calculate scaled versions of the bounds of mHitRegion and mMaybeHitRegion
+ // for quick access in FindPaintedLayerFor().
+ mScaledHitRegionBounds = aState->ScaleToOutsidePixels(mHitRegion.GetBounds());
+ mScaledMaybeHitRegionBounds = aState->ScaleToOutsidePixels(mMaybeHitRegion.GetBounds());
+}
+
+PaintedLayerData
+ContainerState::NewPaintedLayerData(nsDisplayItem* aItem,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const DisplayItemScrollClip* aScrollClip,
+ const nsPoint& aTopLeft,
+ bool aShouldFixToViewport)
+{
+ PaintedLayerData data;
+ data.mAnimatedGeometryRoot = aAnimatedGeometryRoot;
+ data.mScrollClip = aScrollClip;
+ data.mAnimatedGeometryRootOffset = aTopLeft;
+ data.mReferenceFrame = aItem->ReferenceFrame();
+ data.mSingleItemFixedToViewport = aShouldFixToViewport;
+ data.mBackfaceHidden = aItem->Frame()->In3DContextAndBackfaceIsHidden();
+
+ data.mNewChildLayersIndex = mNewChildLayers.Length();
+ NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
+ newLayerEntry->mAnimatedGeometryRoot = aAnimatedGeometryRoot;
+ newLayerEntry->mScrollClip = aScrollClip;
+ // newLayerEntry->mOpaqueRegion is filled in later from
+ // paintedLayerData->mOpaqueRegion, if necessary.
+
+ // Allocate another entry for this layer's optimization to ColorLayer/ImageLayer
+ mNewChildLayers.AppendElement();
+
+ return data;
+}
+
+#ifdef MOZ_DUMP_PAINTING
+static void
+DumpPaintedImage(nsDisplayItem* aItem, SourceSurface* aSurface)
+{
+ nsCString string(aItem->Name());
+ string.Append('-');
+ string.AppendInt((uint64_t)aItem);
+ fprintf_stderr(gfxUtils::sDumpPaintFile, "<script>array[\"%s\"]=\"", string.BeginReading());
+ gfxUtils::DumpAsDataURI(aSurface, gfxUtils::sDumpPaintFile);
+ fprintf_stderr(gfxUtils::sDumpPaintFile, "\";</script>\n");
+}
+#endif
+
+static void
+PaintInactiveLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsDisplayItem* aItem,
+ gfxContext* aContext,
+ nsRenderingContext* aCtx)
+{
+ // This item has an inactive layer. Render it to a PaintedLayer
+ // using a temporary BasicLayerManager.
+ BasicLayerManager* basic = static_cast<BasicLayerManager*>(aManager);
+ RefPtr<gfxContext> context = aContext;
+#ifdef MOZ_DUMP_PAINTING
+ int32_t appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
+ nsIntRect itemVisibleRect =
+ aItem->GetVisibleRect().ToOutsidePixels(appUnitsPerDevPixel);
+
+ RefPtr<DrawTarget> tempDT;
+ if (gfxEnv::DumpPaint()) {
+ tempDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+ itemVisibleRect.Size(),
+ SurfaceFormat::B8G8R8A8);
+ if (tempDT) {
+ context = gfxContext::CreateOrNull(tempDT);
+ if (!context) {
+ // Leave this as crash, it's in the debugging code, we want to know
+ gfxDevCrash(LogReason::InvalidContext) << "PaintInactive context problem " << gfx::hexa(tempDT);
+ return;
+ }
+ context->SetMatrix(gfxMatrix::Translation(-itemVisibleRect.x,
+ -itemVisibleRect.y));
+ }
+ }
+#endif
+ basic->BeginTransaction();
+ basic->SetTarget(context);
+
+ if (aItem->GetType() == nsDisplayItem::TYPE_MASK) {
+ static_cast<nsDisplayMask*>(aItem)->PaintAsLayer(aBuilder, aCtx, basic);
+ if (basic->InTransaction()) {
+ basic->AbortTransaction();
+ }
+ } else if (aItem->GetType() == nsDisplayItem::TYPE_FILTER){
+ static_cast<nsDisplayFilter*>(aItem)->PaintAsLayer(aBuilder, aCtx, basic);
+ if (basic->InTransaction()) {
+ basic->AbortTransaction();
+ }
+ } else {
+ basic->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aBuilder);
+ }
+ FrameLayerBuilder *builder = static_cast<FrameLayerBuilder*>(basic->GetUserData(&gLayerManagerLayerBuilder));
+ if (builder) {
+ builder->DidEndTransaction();
+ }
+
+ basic->SetTarget(nullptr);
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpPaint() && tempDT) {
+ RefPtr<SourceSurface> surface = tempDT->Snapshot();
+ DumpPaintedImage(aItem, surface);
+
+ DrawTarget* drawTarget = aContext->GetDrawTarget();
+ Rect rect(itemVisibleRect.x, itemVisibleRect.y,
+ itemVisibleRect.width, itemVisibleRect.height);
+ drawTarget->DrawSurface(surface, rect, Rect(Point(0,0), rect.Size()));
+
+ aItem->SetPainted();
+ }
+#endif
+}
+
+/**
+ * Chooses a single active scrolled root for the entire display list, used
+ * when we are flattening layers.
+ */
+bool
+ContainerState::ChooseAnimatedGeometryRoot(const nsDisplayList& aList,
+ AnimatedGeometryRoot **aAnimatedGeometryRoot)
+{
+ for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
+ LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
+ // Don't use an item that won't be part of any PaintedLayers to pick the
+ // active scrolled root.
+ if (layerState == LAYER_ACTIVE_FORCE) {
+ continue;
+ }
+
+ // Try using the actual active scrolled root of the backmost item, as that
+ // should result in the least invalidation when scrolling.
+ *aAnimatedGeometryRoot = item->GetAnimatedGeometryRoot();
+ return true;
+ }
+ return false;
+}
+
+nsIntRegion
+ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot,
+ const DisplayItemClip& aClip,
+ nsDisplayList* aList,
+ bool* aHideAllLayersBelow,
+ bool* aOpaqueForAnimatedGeometryRootParent)
+{
+ bool snapOpaque;
+ nsRegion opaque = aItem->GetOpaqueRegion(mBuilder, &snapOpaque);
+ if (opaque.IsEmpty()) {
+ return nsIntRegion();
+ }
+
+ nsIntRegion opaquePixels;
+ nsRegion opaqueClipped;
+ for (auto iter = opaque.RectIter(); !iter.Done(); iter.Next()) {
+ opaqueClipped.Or(opaqueClipped,
+ aClip.ApproximateIntersectInward(iter.Get()));
+ }
+ if (aAnimatedGeometryRoot == mContainerAnimatedGeometryRoot &&
+ opaqueClipped.Contains(mContainerBounds)) {
+ *aHideAllLayersBelow = true;
+ aList->SetIsOpaque();
+ }
+ // Add opaque areas to the "exclude glass" region. Only do this when our
+ // container layer is going to be the rootmost layer, otherwise transforms
+ // etc will mess us up (and opaque contributions from other containers are
+ // not needed).
+ if (!nsLayoutUtils::GetCrossDocParentFrame(mContainerFrame)) {
+ mBuilder->AddWindowOpaqueRegion(opaqueClipped);
+ }
+ opaquePixels = ScaleRegionToInsidePixels(opaqueClipped, snapOpaque);
+
+ if (IsInInactiveLayer()) {
+ return opaquePixels;
+ }
+
+ nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot);
+ if (sf) {
+ nsRect displayport;
+ bool usingDisplayport =
+ nsLayoutUtils::GetDisplayPort((*aAnimatedGeometryRoot)->GetContent(), &displayport,
+ RelativeTo::ScrollFrame);
+ if (!usingDisplayport) {
+ // No async scrolling, so all that matters is that the layer contents
+ // cover the scrollport.
+ displayport = sf->GetScrollPortRect();
+ }
+ nsIFrame* scrollFrame = do_QueryFrame(sf);
+ displayport += scrollFrame->GetOffsetToCrossDoc(mContainerReferenceFrame);
+ if (opaquePixels.Contains(ScaleRegionToNearestPixels(displayport))) {
+ *aOpaqueForAnimatedGeometryRootParent = true;
+ }
+ }
+ return opaquePixels;
+}
+
+static const DisplayItemScrollClip*
+InnermostScrollClipApplicableToAGR(const DisplayItemScrollClip* aItemScrollClip,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot)
+{
+ // "Applicable" scroll clips are those that are for nsIScrollableFrames
+ // that are ancestors of aAnimatedGeometryRoot or ancestors of aContainerScrollClip.
+ // They can be applied to all items sharing this animated geometry root, so
+ // instead of applying to the items individually, they can be applied to the
+ // whole layer.
+ for (const DisplayItemScrollClip* scrollClip = aItemScrollClip;
+ scrollClip;
+ scrollClip = scrollClip->mParent) {
+ nsIFrame* scrolledFrame = scrollClip->mScrollableFrame->GetScrolledFrame();
+ if (nsLayoutUtils::IsAncestorFrameCrossDoc(scrolledFrame, *aAnimatedGeometryRoot)) {
+ // scrollClip and all its ancestors are applicable.
+ return scrollClip;
+ }
+ }
+ return nullptr;
+}
+
+Maybe<size_t>
+ContainerState::SetupMaskLayerForScrolledClip(Layer* aLayer,
+ const DisplayItemClip& aClip)
+{
+ if (aClip.GetRoundedRectCount() > 0) {
+ Maybe<size_t> maskLayerIndex = Some(aLayer->GetAncestorMaskLayerCount());
+ if (RefPtr<Layer> maskLayer = CreateMaskLayer(aLayer, aClip, maskLayerIndex,
+ aClip.GetRoundedRectCount())) {
+ aLayer->AddAncestorMaskLayer(maskLayer);
+ return maskLayerIndex;
+ }
+ // Fall through to |return Nothing()|.
+ }
+ return Nothing();
+}
+
+void
+ContainerState::SetupMaskLayerForCSSMask(Layer* aLayer,
+ nsDisplayMask* aMaskItem)
+{
+ MOZ_ASSERT(mManager->IsCompositingCheap());
+
+ RefPtr<ImageLayer> maskLayer =
+ CreateOrRecycleMaskImageLayerFor(MaskLayerKey(aLayer, Nothing()),
+ [](Layer* aMaskLayer)
+ {
+ aMaskLayer->SetUserData(&gCSSMaskLayerUserData,
+ new CSSMaskLayerUserData());
+ }
+ );
+
+ CSSMaskLayerUserData* oldUserData =
+ static_cast<CSSMaskLayerUserData*>(maskLayer->GetUserData(&gCSSMaskLayerUserData));
+
+ bool snap;
+ nsRect bounds = aMaskItem->GetBounds(mBuilder, &snap);
+ nsIntRect itemRect = ScaleToOutsidePixels(bounds, snap);
+ CSSMaskLayerUserData newUserData(aMaskItem->Frame(), itemRect);
+ if (*oldUserData == newUserData) {
+ aLayer->SetMaskLayer(maskLayer);
+ return;
+ }
+
+ int32_t maxSize = mManager->GetMaxTextureSize();
+ IntSize surfaceSize(std::min(itemRect.width, maxSize),
+ std::min(itemRect.height, maxSize));
+
+ if (surfaceSize.IsEmpty()) {
+ // Return early if we know that the size of this mask surface is empty.
+ return;
+ }
+
+ MaskImageData imageData(surfaceSize, mManager);
+ RefPtr<DrawTarget> dt = imageData.CreateDrawTarget();
+ if (!dt || !dt->IsValid()) {
+ NS_WARNING("Could not create DrawTarget for mask layer.");
+ return;
+ }
+
+ RefPtr<gfxContext> maskCtx = gfxContext::CreateOrNull(dt);
+ maskCtx->SetMatrix(gfxMatrix::Translation(-itemRect.TopLeft()));
+ maskCtx->Multiply(gfxMatrix::Scaling(mParameters.mXScale, mParameters.mYScale));
+
+ if (!aMaskItem->PaintMask(mBuilder, maskCtx)) {
+ // Mostly because of mask resource is not ready.
+ return;
+ }
+
+ // Setup mask layer offset.
+ Matrix4x4 matrix;
+ matrix.PreTranslate(itemRect.x, itemRect.y, 0);
+ matrix.PreTranslate(mParameters.mOffset.x, mParameters.mOffset.y, 0);
+
+ maskLayer->SetBaseTransform(matrix);
+
+ RefPtr<ImageContainer> imgContainer =
+ imageData.CreateImageAndImageContainer();
+ if (!imgContainer) {
+ return;
+ }
+ maskLayer->SetContainer(imgContainer);
+
+ *oldUserData = newUserData;
+ aLayer->SetMaskLayer(maskLayer);
+}
+
+/*
+ * Iterate through the non-clip items in aList and its descendants.
+ * For each item we compute the effective clip rect. Each item is assigned
+ * to a layer. We invalidate the areas in PaintedLayers where an item
+ * has moved from one PaintedLayer to another. Also,
+ * aState->mInvalidPaintedContent is invalidated in every PaintedLayer.
+ * We set the clip rect for items that generated their own layer, and
+ * create a mask layer to do any rounded rect clipping.
+ * (PaintedLayers don't need a clip rect on the layer, we clip the items
+ * individually when we draw them.)
+ * We set the visible rect for all layers, although the actual setting
+ * of visible rects for some PaintedLayers is deferred until the calling
+ * of ContainerState::Finish.
+ */
+void
+ContainerState::ProcessDisplayItems(nsDisplayList* aList)
+{
+ PROFILER_LABEL("ContainerState", "ProcessDisplayItems",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ AnimatedGeometryRoot* lastAnimatedGeometryRoot = mContainerAnimatedGeometryRoot;
+ nsPoint lastAGRTopLeft;
+ nsPoint topLeft(0,0);
+
+ // When NO_COMPONENT_ALPHA is set, items will be flattened into a single
+ // layer, so we need to choose which active scrolled root to use for all
+ // items.
+ if (mFlattenToSingleLayer) {
+ if (ChooseAnimatedGeometryRoot(*aList, &lastAnimatedGeometryRoot)) {
+ lastAGRTopLeft = (*lastAnimatedGeometryRoot)->GetOffsetToCrossDoc(mContainerReferenceFrame);
+ }
+ }
+
+ int32_t maxLayers = gfxPrefs::MaxActiveLayers();
+ int layerCount = 0;
+
+ nsDisplayList savedItems;
+ nsDisplayItem* item;
+ while ((item = aList->RemoveBottom()) != nullptr) {
+ nsDisplayItem::Type itemType = item->GetType();
+
+ // If the item is a event regions item, but is empty (has no regions in it)
+ // then we should just throw it out
+ if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
+ nsDisplayLayerEventRegions* eventRegions =
+ static_cast<nsDisplayLayerEventRegions*>(item);
+ if (eventRegions->IsEmpty()) {
+ item->~nsDisplayItem();
+ continue;
+ }
+ }
+
+ // Peek ahead to the next item and try merging with it or swapping with it
+ // if necessary.
+ nsDisplayItem* aboveItem;
+ while ((aboveItem = aList->GetBottom()) != nullptr) {
+ if (aboveItem->TryMerge(item)) {
+ aList->RemoveBottom();
+ item->~nsDisplayItem();
+ item = aboveItem;
+ itemType = item->GetType();
+ } else {
+ break;
+ }
+ }
+
+ nsDisplayList* itemSameCoordinateSystemChildren
+ = item->GetSameCoordinateSystemChildren();
+ if (item->ShouldFlattenAway(mBuilder)) {
+ aList->AppendToBottom(itemSameCoordinateSystemChildren);
+ item->~nsDisplayItem();
+ continue;
+ }
+
+ savedItems.AppendToTop(item);
+
+ NS_ASSERTION(mAppUnitsPerDevPixel == AppUnitsPerDevPixel(item),
+ "items in a container layer should all have the same app units per dev pixel");
+
+ if (mBuilder->NeedToForceTransparentSurfaceForItem(item)) {
+ aList->SetNeedsTransparentSurface();
+ }
+
+ if (mParameters.mForEventsAndPluginsOnly && !item->GetChildren() &&
+ (itemType != nsDisplayItem::TYPE_LAYER_EVENT_REGIONS &&
+ itemType != nsDisplayItem::TYPE_PLUGIN)) {
+ continue;
+ }
+
+ LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
+ if (layerState == LAYER_INACTIVE &&
+ nsDisplayItem::ForceActiveLayers()) {
+ layerState = LAYER_ACTIVE;
+ }
+
+ bool forceInactive;
+ AnimatedGeometryRoot* animatedGeometryRoot;
+ AnimatedGeometryRoot* animatedGeometryRootForClip = nullptr;
+ if (mFlattenToSingleLayer && layerState != LAYER_ACTIVE_FORCE) {
+ forceInactive = true;
+ animatedGeometryRoot = lastAnimatedGeometryRoot;
+ topLeft = lastAGRTopLeft;
+ } else {
+ forceInactive = false;
+ if (mManager->IsWidgetLayerManager()) {
+ animatedGeometryRoot = item->GetAnimatedGeometryRoot();
+ animatedGeometryRootForClip = item->AnimatedGeometryRootForScrollMetadata();
+ } else {
+ // For inactive layer subtrees, splitting content into PaintedLayers
+ // based on animated geometry roots is pointless. It's more efficient
+ // to build the minimum number of layers.
+ animatedGeometryRoot = mContainerAnimatedGeometryRoot;
+
+ }
+ topLeft = (*animatedGeometryRoot)->GetOffsetToCrossDoc(mContainerReferenceFrame);
+ }
+ if (!animatedGeometryRootForClip) {
+ animatedGeometryRootForClip = animatedGeometryRoot;
+ }
+
+ const DisplayItemScrollClip* itemScrollClip = item->ScrollClip();
+ // Now we need to separate the item's scroll clip chain into those scroll
+ // clips that can be applied to the whole layer (i.e. to all items
+ // sharing the item's animated geometry root), and those that need to be
+ // applied to the item itself.
+ const DisplayItemScrollClip* agrScrollClip =
+ InnermostScrollClipApplicableToAGR(itemScrollClip, animatedGeometryRootForClip);
+ MOZ_ASSERT(DisplayItemScrollClip::IsAncestor(agrScrollClip, itemScrollClip));
+
+ if (agrScrollClip != itemScrollClip) {
+ // Pick up any scroll clips that should apply to the item and apply them.
+ DisplayItemClip clip = item->GetClip();
+ for (const DisplayItemScrollClip* scrollClip = itemScrollClip;
+ scrollClip && scrollClip != agrScrollClip && scrollClip != mContainerScrollClip;
+ scrollClip = scrollClip->mParent) {
+ if (scrollClip->mClip) {
+ clip.IntersectWith(*scrollClip->mClip);
+ }
+ }
+ item->SetClip(mBuilder, clip);
+ }
+
+ bool clipMovesWithLayer = (animatedGeometryRoot == animatedGeometryRootForClip);
+
+ bool shouldFixToViewport = !clipMovesWithLayer &&
+ !(*animatedGeometryRoot)->GetParent() &&
+ item->ShouldFixToViewport(mBuilder);
+
+ // For items that are fixed to the viewport, remove their clip at the
+ // display item level because additional areas could be brought into
+ // view by async scrolling. Save the clip so we can set it on the layer
+ // instead later.
+ DisplayItemClip fixedToViewportClip = DisplayItemClip::NoClip();
+ if (shouldFixToViewport) {
+ fixedToViewportClip = item->GetClip();
+ item->SetClip(mBuilder, DisplayItemClip::NoClip());
+ }
+
+ bool snap;
+ nsRect itemContent = item->GetBounds(mBuilder, &snap);
+ if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
+ nsDisplayLayerEventRegions* eventRegions =
+ static_cast<nsDisplayLayerEventRegions*>(item);
+ itemContent = eventRegions->GetHitRegionBounds(mBuilder, &snap);
+ }
+ nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap);
+ bool prerenderedTransform = itemType == nsDisplayItem::TYPE_TRANSFORM &&
+ static_cast<nsDisplayTransform*>(item)->MayBeAnimated(mBuilder);
+ ParentLayerIntRect clipRect;
+ const DisplayItemClip& itemClip = item->GetClip();
+ if (itemClip.HasClip()) {
+ itemContent.IntersectRect(itemContent, itemClip.GetClipRect());
+ clipRect = ViewAs<ParentLayerPixel>(ScaleToNearestPixels(itemClip.GetClipRect()));
+ if (!prerenderedTransform) {
+ itemDrawRect.IntersectRect(itemDrawRect, clipRect.ToUnknownRect());
+ }
+ clipRect.MoveBy(ViewAs<ParentLayerPixel>(mParameters.mOffset));
+ }
+#ifdef DEBUG
+ nsRect bounds = itemContent;
+ bool dummy;
+ if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
+ bounds = item->GetBounds(mBuilder, &dummy);
+ if (itemClip.HasClip()) {
+ bounds.IntersectRect(bounds, itemClip.GetClipRect());
+ }
+ }
+ bounds = fixedToViewportClip.ApplyNonRoundedIntersection(bounds);
+ if (!bounds.IsEmpty()) {
+ for (const DisplayItemScrollClip* scrollClip = itemScrollClip;
+ scrollClip && scrollClip != mContainerScrollClip;
+ scrollClip = scrollClip->mParent) {
+ if (scrollClip->mClip) {
+ if (scrollClip->mIsAsyncScrollable) {
+ bounds = scrollClip->mClip->GetClipRect();
+ } else {
+ bounds = scrollClip->mClip->ApplyNonRoundedIntersection(bounds);
+ }
+ }
+ }
+ }
+ ((nsRect&)mAccumulatedChildBounds).UnionRect(mAccumulatedChildBounds, bounds);
+#endif
+
+ nsIntRect itemVisibleRect = itemDrawRect;
+ // We haven't computed visibility at this point, so item->GetVisibleRect()
+ // is just the dirty rect that item was initialized with. We intersect it
+ // with the clipped item bounds to get a tighter visible rect.
+ itemVisibleRect = itemVisibleRect.Intersect(
+ ScaleToOutsidePixels(item->GetVisibleRect(), false));
+
+ if (maxLayers != -1 && layerCount >= maxLayers) {
+ forceInactive = true;
+ }
+
+ // Assign the item to a layer
+ if (layerState == LAYER_ACTIVE_FORCE ||
+ (layerState == LAYER_INACTIVE && !mManager->IsWidgetLayerManager()) ||
+ (!forceInactive &&
+ (layerState == LAYER_ACTIVE_EMPTY ||
+ layerState == LAYER_ACTIVE))) {
+
+ layerCount++;
+
+ // LAYER_ACTIVE_EMPTY means the layer is created just for its metadata.
+ // We should never see an empty layer with any visible content!
+ NS_ASSERTION(layerState != LAYER_ACTIVE_EMPTY ||
+ itemVisibleRect.IsEmpty(),
+ "State is LAYER_ACTIVE_EMPTY but visible rect is not.");
+
+ // As long as the new layer isn't going to be a PaintedLayer,
+ // InvalidateForLayerChange doesn't need the new layer pointer.
+ // We also need to check the old data now, because BuildLayer
+ // can overwrite it.
+ InvalidateForLayerChange(item, nullptr);
+
+ // If the item would have its own layer but is invisible, just hide it.
+ // Note that items without their own layers can't be skipped this
+ // way, since their PaintedLayer may decide it wants to draw them
+ // into its buffer even if they're currently covered.
+ if (itemVisibleRect.IsEmpty() &&
+ !item->ShouldBuildLayerEvenIfInvisible(mBuilder)) {
+ continue;
+ }
+
+ if (mScrollClipForPerspectiveChild) {
+ // We are the single transform child item of an nsDisplayPerspective.
+ // Our parent forwarded a scroll clip to us. Pick it up.
+ // We do this after any clipping has been applied, because this
+ // forwarded scroll clip is only used for scrolling (in the form of
+ // APZ frame metrics), not for clipping - the clip still belongs on
+ // the perspective item.
+ MOZ_ASSERT(itemType == nsDisplayItem::TYPE_TRANSFORM);
+ MOZ_ASSERT(!itemScrollClip);
+ MOZ_ASSERT(!agrScrollClip);
+ MOZ_ASSERT(DisplayItemScrollClip::IsAncestor(mContainerScrollClip,
+ mScrollClipForPerspectiveChild));
+ itemScrollClip = mScrollClipForPerspectiveChild;
+ agrScrollClip = mScrollClipForPerspectiveChild;
+ }
+
+ // 3D-transformed layers don't necessarily draw in the order in which
+ // they're added to their parent container layer.
+ bool mayDrawOutOfOrder = itemType == nsDisplayItem::TYPE_TRANSFORM &&
+ (item->Frame()->Combines3DTransformWithAncestors() ||
+ item->Frame()->Extend3DContext());
+
+ // Let mPaintedLayerDataTree know about this item, so that
+ // FindPaintedLayerFor and FindOpaqueBackgroundColor are aware of this
+ // item, even though it's not in any PaintedLayerDataStack.
+ // Ideally we'd only need the "else" case here and have
+ // mPaintedLayerDataTree figure out the right clip from the animated
+ // geometry root that we give it, but it can't easily figure about
+ // overflow:hidden clips on ancestors just by looking at the frame.
+ // So we'll do a little hand holding and pass the clip instead of the
+ // visible rect for the two important cases.
+ nscolor uniformColor = NS_RGBA(0,0,0,0);
+ nscolor* uniformColorPtr = (mayDrawOutOfOrder || IsInInactiveLayer()) ? nullptr :
+ &uniformColor;
+ nsIntRect clipRectUntyped;
+ const DisplayItemClip& layerClip = shouldFixToViewport ? fixedToViewportClip : itemClip;
+ ParentLayerIntRect layerClipRect;
+ nsIntRect* clipPtr = nullptr;
+ if (layerClip.HasClip()) {
+ layerClipRect = ViewAs<ParentLayerPixel>(
+ ScaleToNearestPixels(layerClip.GetClipRect()) + mParameters.mOffset);
+ clipRectUntyped = layerClipRect.ToUnknownRect();
+ clipPtr = &clipRectUntyped;
+ }
+ if (*animatedGeometryRoot == item->Frame() &&
+ *animatedGeometryRoot != mBuilder->RootReferenceFrame()) {
+ // This is the case for scrollbar thumbs, for example. In that case the
+ // clip we care about is the overflow:hidden clip on the scrollbar.
+ mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot->mParentAGR,
+ clipPtr,
+ uniformColorPtr);
+ } else if (prerenderedTransform) {
+ mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot,
+ clipPtr,
+ uniformColorPtr);
+ } else if (shouldFixToViewport) {
+ mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRootForClip,
+ clipPtr,
+ uniformColorPtr);
+ } else {
+ // Using itemVisibleRect here isn't perfect. itemVisibleRect can be
+ // larger or smaller than the potential bounds of item's contents in
+ // animatedGeometryRoot: It's too large if there's a clipped display
+ // port somewhere among item's contents (see bug 1147673), and it can
+ // be too small if the contents can move, because it only looks at the
+ // contents' current bounds and doesn't anticipate any animations.
+ // Time will tell whether this is good enough, or whether we need to do
+ // something more sophisticated here.
+ mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot,
+ &itemVisibleRect, uniformColorPtr);
+ }
+
+ ContainerLayerParameters params = mParameters;
+ params.mBackgroundColor = uniformColor;
+ params.mLayerCreationHint = GetLayerCreationHint(animatedGeometryRoot);
+ params.mScrollClip = agrScrollClip;
+ params.mScrollClipForPerspectiveChild = nullptr;
+
+ if (itemType == nsDisplayItem::TYPE_PERSPECTIVE) {
+ // Perspective items have a single child item, an nsDisplayTransform.
+ // If the perspective item is scrolled, but the perspective-inducing
+ // frame is outside the scroll frame (indicated by this items AGR
+ // being outside that scroll frame), we have to take special care to
+ // make APZ scrolling work properly. APZ needs us to put the scroll
+ // frame's FrameMetrics on our child transform ContainerLayer instead.
+ // Our agrScrollClip is the scroll clip that's applicable to our
+ // perspective frame, so it won't be the scroll clip for the scrolled
+ // frame in the case that we care about, and we'll forward that scroll
+ // clip to our child.
+ params.mScrollClipForPerspectiveChild = itemScrollClip;
+ }
+
+ // Just use its layer.
+ // Set layerContentsVisibleRect.width/height to -1 to indicate we
+ // currently don't know. If BuildContainerLayerFor gets called by
+ // item->BuildLayer, this will be set to a proper rect.
+ nsIntRect layerContentsVisibleRect(0, 0, -1, -1);
+ params.mLayerContentsVisibleRect = &layerContentsVisibleRect;
+ RefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, params);
+ if (!ownLayer) {
+ continue;
+ }
+
+ NS_ASSERTION(!ownLayer->AsPaintedLayer(),
+ "Should never have created a dedicated Painted layer!");
+
+ if (item->BackfaceIsHidden()) {
+ ownLayer->SetContentFlags(ownLayer->GetContentFlags() |
+ Layer::CONTENT_BACKFACE_HIDDEN);
+ } else {
+ ownLayer->SetContentFlags(ownLayer->GetContentFlags() &
+ ~Layer::CONTENT_BACKFACE_HIDDEN);
+ }
+
+ nsRect invalid;
+ if (item->IsInvalid(invalid)) {
+ ownLayer->SetInvalidRectToVisibleRegion();
+ }
+
+ // If it's not a ContainerLayer, we need to apply the scale transform
+ // ourselves.
+ if (!ownLayer->AsContainerLayer()) {
+ ownLayer->SetPostScale(mParameters.mXScale,
+ mParameters.mYScale);
+ }
+
+ // Update that layer's clip and visible rects.
+ NS_ASSERTION(ownLayer->Manager() == mManager, "Wrong manager");
+ NS_ASSERTION(!ownLayer->HasUserData(&gLayerManagerUserData),
+ "We shouldn't have a FrameLayerBuilder-managed layer here!");
+ NS_ASSERTION(layerClip.HasClip() ||
+ layerClip.GetRoundedRectCount() == 0,
+ "If we have rounded rects, we must have a clip rect");
+
+ // It has its own layer. Update that layer's clip and visible rects.
+
+ ownLayer->SetClipRect(Nothing());
+ ownLayer->SetScrolledClip(Nothing());
+ if (layerClip.HasClip()) {
+ // For layers fixed to the viewport, the clip becomes part of the
+ // layer's scrolled clip. Otherwise, it becomes part of the layer clip.
+ if (shouldFixToViewport) {
+ LayerClip scrolledClip;
+ scrolledClip.SetClipRect(layerClipRect);
+ if (layerClip.GetRoundedRectCount() > 0) {
+ scrolledClip.SetMaskLayerIndex(
+ SetupMaskLayerForScrolledClip(ownLayer.get(), layerClip));
+ }
+ ownLayer->SetScrolledClip(Some(scrolledClip));
+ } else {
+ ownLayer->SetClipRect(Some(layerClipRect));
+
+ // rounded rectangle clipping using mask layers
+ // (must be done after visible rect is set on layer)
+ if (layerClip.GetRoundedRectCount() > 0) {
+ SetupMaskLayer(ownLayer, layerClip);
+ }
+ }
+ } else if (item->GetType() == nsDisplayItem::TYPE_MASK) {
+ nsDisplayMask* maskItem = static_cast<nsDisplayMask*>(item);
+ SetupMaskLayerForCSSMask(ownLayer, maskItem);
+ }
+
+ ContainerLayer* oldContainer = ownLayer->GetParent();
+ if (oldContainer && oldContainer != mContainerLayer) {
+ oldContainer->RemoveChild(ownLayer);
+ }
+ NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, ownLayer) < 0,
+ "Layer already in list???");
+
+ NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
+ newLayerEntry->mLayer = ownLayer;
+ newLayerEntry->mAnimatedGeometryRoot = animatedGeometryRoot;
+ newLayerEntry->mScrollClip = agrScrollClip;
+ newLayerEntry->mLayerState = layerState;
+
+ // Don't attempt to flatten compnent alpha layers that are within
+ // a forced active layer, or an active transform;
+ if (itemType == nsDisplayItem::TYPE_TRANSFORM ||
+ layerState == LAYER_ACTIVE_FORCE) {
+ newLayerEntry->mPropagateComponentAlphaFlattening = false;
+ }
+ // nsDisplayTransform::BuildLayer must set layerContentsVisibleRect.
+ // We rely on this to ensure 3D transforms compute a reasonable
+ // layer visible region.
+ NS_ASSERTION(itemType != nsDisplayItem::TYPE_TRANSFORM ||
+ layerContentsVisibleRect.width >= 0,
+ "Transform items must set layerContentsVisibleRect!");
+ if (mLayerBuilder->IsBuildingRetainedLayers()) {
+ newLayerEntry->mLayerContentsVisibleRect = layerContentsVisibleRect;
+ if (itemType == nsDisplayItem::TYPE_PERSPECTIVE ||
+ (itemType == nsDisplayItem::TYPE_TRANSFORM &&
+ (item->Frame()->Extend3DContext() ||
+ item->Frame()->Combines3DTransformWithAncestors() ||
+ item->Frame()->HasPerspective()))) {
+ // Give untransformed visible region as outer visible region
+ // to avoid failure caused by singular transforms.
+ newLayerEntry->mUntransformedVisibleRegion = true;
+ newLayerEntry->mVisibleRegion =
+ item->GetVisibleRectForChildren().ToOutsidePixels(mAppUnitsPerDevPixel);
+ } else {
+ newLayerEntry->mVisibleRegion = itemVisibleRect;
+ }
+ newLayerEntry->mOpaqueRegion = ComputeOpaqueRect(item,
+ animatedGeometryRoot, layerClip, aList,
+ &newLayerEntry->mHideAllLayersBelow,
+ &newLayerEntry->mOpaqueForAnimatedGeometryRootParent);
+ } else {
+ bool useChildrenVisible =
+ itemType == nsDisplayItem::TYPE_TRANSFORM &&
+ (item->Frame()->IsPreserve3DLeaf() ||
+ item->Frame()->HasPerspective());
+ const nsIntRegion &visible = useChildrenVisible ?
+ item->GetVisibleRectForChildren().ToOutsidePixels(mAppUnitsPerDevPixel):
+ itemVisibleRect;
+
+ SetOuterVisibleRegionForLayer(ownLayer, visible,
+ layerContentsVisibleRect.width >= 0 ? &layerContentsVisibleRect : nullptr,
+ useChildrenVisible);
+ }
+ if (itemType == nsDisplayItem::TYPE_SCROLL_INFO_LAYER) {
+ nsDisplayScrollInfoLayer* scrollItem = static_cast<nsDisplayScrollInfoLayer*>(item);
+ newLayerEntry->mOpaqueForAnimatedGeometryRootParent = false;
+ newLayerEntry->mBaseScrollMetadata =
+ scrollItem->ComputeScrollMetadata(ownLayer, mParameters);
+ } else if ((itemType == nsDisplayItem::TYPE_SUBDOCUMENT ||
+ itemType == nsDisplayItem::TYPE_ZOOM ||
+ itemType == nsDisplayItem::TYPE_RESOLUTION) &&
+ gfxPrefs::LayoutUseContainersForRootFrames())
+ {
+ newLayerEntry->mBaseScrollMetadata =
+ static_cast<nsDisplaySubDocument*>(item)->ComputeScrollMetadata(ownLayer, mParameters);
+ }
+
+ /**
+ * No need to allocate geometry for items that aren't
+ * part of a PaintedLayer.
+ */
+ mLayerBuilder->AddLayerDisplayItem(ownLayer, item, layerState, nullptr);
+ } else {
+ PaintedLayerData* paintedLayerData =
+ mPaintedLayerDataTree.FindPaintedLayerFor(animatedGeometryRoot, agrScrollClip,
+ itemVisibleRect,
+ item->Frame()->In3DContextAndBackfaceIsHidden(),
+ [&]() {
+ return NewPaintedLayerData(item, animatedGeometryRoot, agrScrollClip,
+ topLeft, shouldFixToViewport);
+ });
+
+ if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
+ nsDisplayLayerEventRegions* eventRegions =
+ static_cast<nsDisplayLayerEventRegions*>(item);
+ paintedLayerData->AccumulateEventRegions(this, eventRegions);
+ } else {
+ // check to see if the new item has rounded rect clips in common with
+ // other items in the layer
+ if (mManager->IsWidgetLayerManager()) {
+ paintedLayerData->UpdateCommonClipCount(itemClip);
+ }
+ nsIntRegion opaquePixels = ComputeOpaqueRect(item,
+ animatedGeometryRoot, itemClip, aList,
+ &paintedLayerData->mHideAllLayersBelow,
+ &paintedLayerData->mOpaqueForAnimatedGeometryRootParent);
+ MOZ_ASSERT(nsIntRegion(itemDrawRect).Contains(opaquePixels));
+ opaquePixels.AndWith(itemVisibleRect);
+ paintedLayerData->Accumulate(this, item, opaquePixels,
+ itemVisibleRect, itemClip, layerState);
+
+ // If we removed the clip from the display item above because it's
+ // fixed to the viewport, save it on the PaintedLayerData so we can
+ // set it on the layer later.
+ if (fixedToViewportClip.HasClip()) {
+ paintedLayerData->mItemClip = fixedToViewportClip;
+ }
+
+ if (!paintedLayerData->mLayer) {
+ // Try to recycle the old layer of this display item.
+ RefPtr<PaintedLayer> layer =
+ AttemptToRecyclePaintedLayer(animatedGeometryRoot, item, topLeft);
+ if (layer) {
+ paintedLayerData->mLayer = layer;
+
+ NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, layer) < 0,
+ "Layer already in list???");
+ mNewChildLayers[paintedLayerData->mNewChildLayersIndex].mLayer = layer.forget();
+ }
+ }
+ }
+ }
+
+ if (itemSameCoordinateSystemChildren &&
+ itemSameCoordinateSystemChildren->NeedsTransparentSurface()) {
+ aList->SetNeedsTransparentSurface();
+ }
+ }
+
+ aList->AppendToTop(&savedItems);
+}
+
+void
+ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, PaintedLayer* aNewLayer)
+{
+ NS_ASSERTION(aItem->GetPerFrameKey(),
+ "Display items that render using Thebes must have a key");
+ nsDisplayItemGeometry* oldGeometry = nullptr;
+ DisplayItemClip* oldClip = nullptr;
+ Layer* oldLayer = mLayerBuilder->GetOldLayerFor(aItem, &oldGeometry, &oldClip);
+ if (aNewLayer != oldLayer && oldLayer) {
+ // The item has changed layers.
+ // Invalidate the old bounds in the old layer and new bounds in the new layer.
+ PaintedLayer* t = oldLayer->AsPaintedLayer();
+ if (t && oldGeometry) {
+ // Note that whenever the layer's scale changes, we invalidate the whole thing,
+ // so it doesn't matter whether we are using the old scale at last paint
+ // or a new scale here
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ printf_stderr("Display item type %s(%p) changed layers %p to %p!\n", aItem->Name(), aItem->Frame(), t, aNewLayer);
+ }
+#endif
+ InvalidatePostTransformRegion(t,
+ oldGeometry->ComputeInvalidationRegion(),
+ *oldClip,
+ mLayerBuilder->GetLastPaintOffset(t));
+ }
+ // Clear the old geometry so that invalidation thinks the item has been
+ // added this paint.
+ mLayerBuilder->ClearCachedGeometry(aItem);
+ aItem->NotifyRenderingChanged();
+ }
+}
+
+void
+FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData* aData)
+{
+ nsDisplayItem *item = aData->mItem;
+ PaintedLayer* paintedLayer = aData->mLayer->AsPaintedLayer();
+ if (!item || !paintedLayer) {
+ aData->EndUpdate();
+ return;
+ }
+
+ PaintedLayerItemsEntry* entry = mPaintedLayerItems.GetEntry(paintedLayer);
+
+ nsAutoPtr<nsDisplayItemGeometry> geometry;
+
+ PaintedDisplayItemLayerUserData* layerData =
+ static_cast<PaintedDisplayItemLayerUserData*>(aData->mLayer->GetUserData(&gPaintedDisplayItemLayerUserData));
+ nsPoint shift = layerData->mAnimatedGeometryRootOrigin - layerData->mLastAnimatedGeometryRootOrigin;
+
+ const DisplayItemClip& clip = item->GetClip();
+
+ // If the frame is marked as invalidated, and didn't specify a rect to invalidate then we want to
+ // invalidate both the old and new bounds, otherwise we only want to invalidate the changed areas.
+ // If we do get an invalid rect, then we want to add this on top of the change areas.
+ nsRect invalid;
+ nsRegion combined;
+ bool notifyRenderingChanged = true;
+ if (!aData->mGeometry) {
+ // This item is being added for the first time, invalidate its entire area.
+ geometry = item->AllocateGeometry(mDisplayListBuilder);
+ combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ printf_stderr("Display item type %s(%p) added to layer %p!\n", item->Name(), item->Frame(), aData->mLayer.get());
+ }
+#endif
+ } else if (aData->mIsInvalid || (item->IsInvalid(invalid) && invalid.IsEmpty())) {
+ // Layout marked item/frame as needing repainting (without an explicit rect), invalidate the entire old and new areas.
+ geometry = item->AllocateGeometry(mDisplayListBuilder);
+ combined = aData->mClip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
+ combined.MoveBy(shift);
+ combined.Or(combined, clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion()));
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ printf_stderr("Display item type %s(%p) (in layer %p) belongs to an invalidated frame!\n", item->Name(), item->Frame(), aData->mLayer.get());
+ }
+#endif
+ } else {
+ // Let the display item check for geometry changes and decide what needs to be
+ // repainted.
+
+ const nsTArray<nsIFrame*>& changedFrames = aData->GetFrameListChanges();
+ aData->mGeometry->MoveBy(shift);
+ item->ComputeInvalidationRegion(mDisplayListBuilder, aData->mGeometry, &combined);
+
+ // We have an optimization to cache the drawing of background-attachment: fixed canvas
+ // background images so we can scroll and just blit them when they are flattened into
+ // the same layer as scrolling content. NotifyRenderingChanged is only used to tell
+ // the canvas bg image item to purge this cache. We want to be careful not to accidentally
+ // purge the cache if we are just invalidating due to scrolling (ie the background image
+ // moves on the scrolling layer but it's rendering stays the same) so if
+ // AddOffsetAndComputeDifference is the only thing that will invalidate we skip the
+ // NotifyRenderingChanged call (ComputeInvalidationRegion for background images also calls
+ // NotifyRenderingChanged if anything changes).
+ // Only allocate a new geometry object if something actually changed, otherwise the existing
+ // one should be fine. We always reallocate for inactive layers, since these types don't
+ // implement ComputeInvalidateRegion (and rely on the ComputeDifferences call in
+ // AddPaintedDisplayItem instead).
+ if (!combined.IsEmpty() || aData->mLayerState == LAYER_INACTIVE) {
+ geometry = item->AllocateGeometry(mDisplayListBuilder);
+ } else if (aData->mClip == clip && invalid.IsEmpty() && changedFrames.Length() == 0) {
+ notifyRenderingChanged = false;
+ }
+ aData->mClip.AddOffsetAndComputeDifference(entry->mCommonClipCount,
+ shift, aData->mGeometry->ComputeInvalidationRegion(),
+ clip, entry->mLastCommonClipCount,
+ geometry ? geometry->ComputeInvalidationRegion() :
+ aData->mGeometry->ComputeInvalidationRegion(),
+ &combined);
+
+ // Add in any rect that the frame specified
+ combined.Or(combined, invalid);
+
+ for (uint32_t i = 0; i < changedFrames.Length(); i++) {
+ combined.Or(combined, changedFrames[i]->GetVisualOverflowRect());
+ }
+
+ // Restrict invalidation to the clipped region
+ nsRegion clipRegion;
+ if (clip.ComputeRegionInClips(&aData->mClip, shift, &clipRegion)) {
+ combined.And(combined, clipRegion);
+ }
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ if (!combined.IsEmpty()) {
+ printf_stderr("Display item type %s(%p) (in layer %p) changed geometry!\n", item->Name(), item->Frame(), aData->mLayer.get());
+ }
+ }
+#endif
+ }
+ if (!combined.IsEmpty()) {
+ if (notifyRenderingChanged) {
+ item->NotifyRenderingChanged();
+ }
+ InvalidatePostTransformRegion(paintedLayer,
+ combined.ScaleToOutsidePixels(layerData->mXScale, layerData->mYScale, layerData->mAppUnitsPerDevPixel),
+ layerData->mTranslation);
+ }
+
+ aData->EndUpdate(geometry);
+}
+
+void
+FrameLayerBuilder::AddPaintedDisplayItem(PaintedLayerData* aLayerData,
+ nsDisplayItem* aItem,
+ const DisplayItemClip& aClip,
+ ContainerState& aContainerState,
+ LayerState aLayerState,
+ const nsPoint& aTopLeft)
+{
+ PaintedLayer* layer = aLayerData->mLayer;
+ PaintedDisplayItemLayerUserData* paintedData =
+ static_cast<PaintedDisplayItemLayerUserData*>
+ (layer->GetUserData(&gPaintedDisplayItemLayerUserData));
+ RefPtr<BasicLayerManager> tempManager;
+ nsIntRect intClip;
+ bool hasClip = false;
+ if (aLayerState != LAYER_NONE) {
+ DisplayItemData *data = GetDisplayItemDataForManager(aItem, layer->Manager());
+ if (data) {
+ tempManager = data->mInactiveManager;
+ }
+ if (!tempManager) {
+ tempManager = new BasicLayerManager(BasicLayerManager::BLM_INACTIVE);
+ }
+
+ // We need to grab these before calling AddLayerDisplayItem because it will overwrite them.
+ nsRegion clip;
+ DisplayItemClip* oldClip = nullptr;
+ GetOldLayerFor(aItem, nullptr, &oldClip);
+ hasClip = aClip.ComputeRegionInClips(oldClip,
+ aTopLeft - paintedData->mLastAnimatedGeometryRootOrigin,
+ &clip);
+
+ if (hasClip) {
+ intClip = clip.GetBounds().ScaleToOutsidePixels(paintedData->mXScale,
+ paintedData->mYScale,
+ paintedData->mAppUnitsPerDevPixel);
+ }
+ }
+
+ AddLayerDisplayItem(layer, aItem, aLayerState, tempManager);
+
+ PaintedLayerItemsEntry* entry = mPaintedLayerItems.PutEntry(layer);
+ if (entry) {
+ entry->mContainerLayerFrame = aContainerState.GetContainerFrame();
+ if (entry->mContainerLayerGeneration == 0) {
+ entry->mContainerLayerGeneration = mContainerLayerGeneration;
+ }
+ if (tempManager) {
+ FLB_LOG_PAINTED_LAYER_DECISION(aLayerData, "Creating nested FLB for item %p\n", aItem);
+ FrameLayerBuilder* layerBuilder = new FrameLayerBuilder();
+ layerBuilder->Init(mDisplayListBuilder, tempManager, aLayerData);
+
+ tempManager->BeginTransaction();
+ if (mRetainingManager) {
+ layerBuilder->DidBeginRetainedLayerTransaction(tempManager);
+ }
+
+ UniquePtr<LayerProperties> props(LayerProperties::CloneFrom(tempManager->GetRoot()));
+ RefPtr<Layer> tmpLayer =
+ aItem->BuildLayer(mDisplayListBuilder, tempManager, ContainerLayerParameters());
+ // We have no easy way of detecting if this transaction will ever actually get finished.
+ // For now, I've just silenced the warning with nested transactions in BasicLayers.cpp
+ if (!tmpLayer) {
+ tempManager->EndTransaction(nullptr, nullptr);
+ tempManager->SetUserData(&gLayerManagerLayerBuilder, nullptr);
+ return;
+ }
+
+ bool snap;
+ nsRect visibleRect =
+ aItem->GetVisibleRect().Intersect(aItem->GetBounds(mDisplayListBuilder, &snap));
+ nsIntRegion rgn = visibleRect.ToOutsidePixels(paintedData->mAppUnitsPerDevPixel);
+ SetOuterVisibleRegion(tmpLayer, &rgn);
+
+ // If BuildLayer didn't call BuildContainerLayerFor, then our new layer won't have been
+ // stored in layerBuilder. Manually add it now.
+ if (mRetainingManager) {
+#ifdef DEBUG_DISPLAY_ITEM_DATA
+ LayerManagerData* parentLmd = static_cast<LayerManagerData*>
+ (layer->Manager()->GetUserData(&gLayerManagerUserData));
+ LayerManagerData* lmd = static_cast<LayerManagerData*>
+ (tempManager->GetUserData(&gLayerManagerUserData));
+ lmd->mParent = parentLmd;
+#endif
+ layerBuilder->StoreDataForFrame(aItem, tmpLayer, LAYER_ACTIVE);
+ }
+
+ tempManager->SetRoot(tmpLayer);
+ layerBuilder->WillEndTransaction();
+ tempManager->AbortTransaction();
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxUtils::DumpDisplayList() || gfxEnv::DumpPaint()) {
+ fprintf_stderr(gfxUtils::sDumpPaintFile, "Basic layer tree for painting contents of display item %s(%p):\n", aItem->Name(), aItem->Frame());
+ std::stringstream stream;
+ tempManager->Dump(stream, "", gfxEnv::DumpPaintToFile());
+ fprint_stderr(gfxUtils::sDumpPaintFile, stream); // not a typo, fprint_stderr declared in LayersLogging.h
+ }
+#endif
+
+ nsIntPoint offset = GetLastPaintOffset(layer) - GetTranslationForPaintedLayer(layer);
+ props->MoveBy(-offset);
+ // Effective transforms are needed by ComputeDifferences().
+ tmpLayer->ComputeEffectiveTransforms(Matrix4x4());
+ nsIntRegion invalid = props->ComputeDifferences(tmpLayer, nullptr);
+ if (aLayerState == LAYER_SVG_EFFECTS) {
+ invalid = nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(aItem->Frame(),
+ aItem->ToReferenceFrame(),
+ invalid);
+ }
+ if (!invalid.IsEmpty()) {
+#ifdef MOZ_DUMP_PAINTING
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ printf_stderr("Inactive LayerManager(%p) for display item %s(%p) has an invalid region - invalidating layer %p\n", tempManager.get(), aItem->Name(), aItem->Frame(), layer);
+ }
+#endif
+ invalid.ScaleRoundOut(paintedData->mXScale, paintedData->mYScale);
+
+ if (hasClip) {
+ invalid.And(invalid, intClip);
+ }
+
+ InvalidatePostTransformRegion(layer, invalid,
+ GetTranslationForPaintedLayer(layer));
+ }
+ }
+ ClippedDisplayItem* cdi =
+ entry->mItems.AppendElement(ClippedDisplayItem(aItem,
+ mContainerLayerGeneration));
+ cdi->mInactiveLayerManager = tempManager;
+ }
+}
+
+FrameLayerBuilder::DisplayItemData*
+FrameLayerBuilder::StoreDataForFrame(nsDisplayItem* aItem, Layer* aLayer, LayerState aState)
+{
+ DisplayItemData* oldData = GetDisplayItemDataForManager(aItem, mRetainingManager);
+ if (oldData) {
+ if (!oldData->mUsed) {
+ oldData->BeginUpdate(aLayer, aState, mContainerLayerGeneration, aItem);
+ }
+ return oldData;
+ }
+
+ LayerManagerData* lmd = static_cast<LayerManagerData*>
+ (mRetainingManager->GetUserData(&gLayerManagerUserData));
+
+ RefPtr<DisplayItemData> data =
+ new DisplayItemData(lmd, aItem->GetPerFrameKey(), aLayer);
+
+ data->BeginUpdate(aLayer, aState, mContainerLayerGeneration, aItem);
+
+ lmd->mDisplayItems.PutEntry(data);
+ return data;
+}
+
+void
+FrameLayerBuilder::StoreDataForFrame(nsIFrame* aFrame,
+ uint32_t aDisplayItemKey,
+ Layer* aLayer,
+ LayerState aState)
+{
+ DisplayItemData* oldData = GetDisplayItemData(aFrame, aDisplayItemKey);
+ if (oldData && oldData->mFrameList.Length() == 1) {
+ oldData->BeginUpdate(aLayer, aState, mContainerLayerGeneration);
+ return;
+ }
+
+ LayerManagerData* lmd = static_cast<LayerManagerData*>
+ (mRetainingManager->GetUserData(&gLayerManagerUserData));
+
+ RefPtr<DisplayItemData> data =
+ new DisplayItemData(lmd, aDisplayItemKey, aLayer, aFrame);
+
+ data->BeginUpdate(aLayer, aState, mContainerLayerGeneration);
+
+ lmd->mDisplayItems.PutEntry(data);
+}
+
+FrameLayerBuilder::ClippedDisplayItem::ClippedDisplayItem(nsDisplayItem* aItem,
+ uint32_t aGeneration)
+ : mItem(aItem)
+ , mContainerLayerGeneration(aGeneration)
+{
+}
+
+FrameLayerBuilder::ClippedDisplayItem::~ClippedDisplayItem()
+{
+ if (mInactiveLayerManager) {
+ mInactiveLayerManager->SetUserData(&gLayerManagerLayerBuilder, nullptr);
+ }
+}
+
+FrameLayerBuilder::PaintedLayerItemsEntry::PaintedLayerItemsEntry(const PaintedLayer *aKey)
+ : nsPtrHashKey<PaintedLayer>(aKey)
+ , mContainerLayerFrame(nullptr)
+ , mLastCommonClipCount(0)
+ , mContainerLayerGeneration(0)
+ , mHasExplicitLastPaintOffset(false)
+ , mCommonClipCount(0)
+{
+}
+
+FrameLayerBuilder::PaintedLayerItemsEntry::PaintedLayerItemsEntry(const PaintedLayerItemsEntry& aOther)
+ : nsPtrHashKey<PaintedLayer>(aOther.mKey)
+ , mItems(aOther.mItems)
+{
+ NS_ERROR("Should never be called, since we ALLOW_MEMMOVE");
+}
+
+FrameLayerBuilder::PaintedLayerItemsEntry::~PaintedLayerItemsEntry()
+{
+}
+
+void
+FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer,
+ nsDisplayItem* aItem,
+ LayerState aLayerState,
+ BasicLayerManager* aManager)
+{
+ if (aLayer->Manager() != mRetainingManager)
+ return;
+
+ DisplayItemData *data = StoreDataForFrame(aItem, aLayer, aLayerState);
+ data->mInactiveManager = aManager;
+}
+
+nsIntPoint
+FrameLayerBuilder::GetLastPaintOffset(PaintedLayer* aLayer)
+{
+ PaintedLayerItemsEntry* entry = mPaintedLayerItems.PutEntry(aLayer);
+ if (entry) {
+ if (entry->mContainerLayerGeneration == 0) {
+ entry->mContainerLayerGeneration = mContainerLayerGeneration;
+ }
+ if (entry->mHasExplicitLastPaintOffset)
+ return entry->mLastPaintOffset;
+ }
+ return GetTranslationForPaintedLayer(aLayer);
+}
+
+void
+FrameLayerBuilder::SavePreviousDataForLayer(PaintedLayer* aLayer, uint32_t aClipCount)
+{
+ PaintedLayerItemsEntry* entry = mPaintedLayerItems.PutEntry(aLayer);
+ if (entry) {
+ if (entry->mContainerLayerGeneration == 0) {
+ entry->mContainerLayerGeneration = mContainerLayerGeneration;
+ }
+ entry->mLastPaintOffset = GetTranslationForPaintedLayer(aLayer);
+ entry->mHasExplicitLastPaintOffset = true;
+ entry->mLastCommonClipCount = aClipCount;
+ }
+}
+
+bool
+FrameLayerBuilder::CheckInLayerTreeCompressionMode()
+{
+ if (mInLayerTreeCompressionMode) {
+ return true;
+ }
+
+ // If we wanted to be in layer tree compression mode, but weren't, then scheduled
+ // a delayed repaint where we will be.
+ mRootPresContext->PresShell()->GetRootFrame()->SchedulePaint(nsIFrame::PAINT_DELAYED_COMPRESS);
+
+ return false;
+}
+
+void
+ContainerState::CollectOldLayers()
+{
+ for (Layer* layer = mContainerLayer->GetFirstChild(); layer;
+ layer = layer->GetNextSibling()) {
+ NS_ASSERTION(!layer->HasUserData(&gMaskLayerUserData),
+ "Mask layers should not be part of the layer tree.");
+ if (layer->HasUserData(&gPaintedDisplayItemLayerUserData)) {
+ NS_ASSERTION(layer->AsPaintedLayer(), "Wrong layer type");
+ mPaintedLayersAvailableForRecycling.PutEntry(static_cast<PaintedLayer*>(layer));
+ }
+
+ if (Layer* maskLayer = layer->GetMaskLayer()) {
+ NS_ASSERTION(maskLayer->GetType() == Layer::TYPE_IMAGE,
+ "Could not recycle mask layer, unsupported layer type.");
+ mRecycledMaskImageLayers.Put(MaskLayerKey(layer, Nothing()), static_cast<ImageLayer*>(maskLayer));
+ }
+ for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
+ Layer* maskLayer = layer->GetAncestorMaskLayerAt(i);
+
+ NS_ASSERTION(maskLayer->GetType() == Layer::TYPE_IMAGE,
+ "Could not recycle mask layer, unsupported layer type.");
+ mRecycledMaskImageLayers.Put(MaskLayerKey(layer, Some(i)), static_cast<ImageLayer*>(maskLayer));
+ }
+ }
+}
+
+struct OpaqueRegionEntry {
+ AnimatedGeometryRoot* mAnimatedGeometryRoot;
+ nsIntRegion mOpaqueRegion;
+};
+
+static OpaqueRegionEntry*
+FindOpaqueRegionEntry(nsTArray<OpaqueRegionEntry>& aEntries,
+ AnimatedGeometryRoot* aAnimatedGeometryRoot)
+{
+ for (uint32_t i = 0; i < aEntries.Length(); ++i) {
+ OpaqueRegionEntry* d = &aEntries[i];
+ if (d->mAnimatedGeometryRoot == aAnimatedGeometryRoot) {
+ return d;
+ }
+ }
+ return nullptr;
+}
+
+void
+ContainerState::SetupScrollingMetadata(NewLayerEntry* aEntry)
+{
+ if (mFlattenToSingleLayer) {
+ // animated geometry roots are forced to all match, so we can't
+ // use them and we don't get async scrolling.
+ return;
+ }
+
+ if (!mBuilder->IsPaintingToWindow()) {
+ // async scrolling not possible, and async scrolling info not computed
+ // for this paint.
+ return;
+ }
+
+ AutoTArray<ScrollMetadata,2> metricsArray;
+ if (aEntry->mBaseScrollMetadata) {
+ metricsArray.AppendElement(*aEntry->mBaseScrollMetadata);
+
+ // The base FrameMetrics was not computed by the nsIScrollableframe, so it
+ // should not have a mask layer.
+ MOZ_ASSERT(!aEntry->mBaseScrollMetadata->HasMaskLayer());
+ }
+
+ // Any extra mask layers we need to attach to ScrollMetadatas.
+ // The list may already contain an entry added for the layer's scrolled clip
+ // so add to it rather than overwriting it (we clear the list when recycling
+ // a layer).
+ nsTArray<RefPtr<Layer>> maskLayers(aEntry->mLayer->GetAllAncestorMaskLayers());
+
+ for (const DisplayItemScrollClip* scrollClip = aEntry->mScrollClip;
+ scrollClip && scrollClip != mContainerScrollClip;
+ scrollClip = scrollClip->mParent) {
+ if (!scrollClip->mIsAsyncScrollable) {
+ // This scroll clip was created for a scroll frame that didn't know
+ // whether it needs to be async scrollable for scroll handoff. It was
+ // not activated, so we don't need to create a frame metrics for it.
+ continue;
+ }
+
+ nsIScrollableFrame* scrollFrame = scrollClip->mScrollableFrame;
+ const DisplayItemClip* clip = scrollClip->mClip;
+
+ Maybe<ScrollMetadata> metadata =
+ scrollFrame->ComputeScrollMetadata(aEntry->mLayer, mContainerReferenceFrame, mParameters, clip);
+ if (!metadata) {
+ continue;
+ }
+
+ if (clip &&
+ clip->HasClip() &&
+ clip->GetRoundedRectCount() > 0)
+ {
+ // The clip in between this scrollframe and its ancestor scrollframe
+ // requires a mask layer. Since this mask layer should not move with
+ // the APZC associated with this FrameMetrics, we attach the mask
+ // layer as an additional, separate clip.
+ Maybe<size_t> nextIndex = Some(maskLayers.Length());
+ RefPtr<Layer> maskLayer =
+ CreateMaskLayer(aEntry->mLayer, *clip, nextIndex, clip->GetRoundedRectCount());
+ if (maskLayer) {
+ MOZ_ASSERT(metadata->HasScrollClip());
+ metadata->ScrollClip().SetMaskLayerIndex(nextIndex);
+ maskLayers.AppendElement(maskLayer);
+ }
+ }
+
+ metricsArray.AppendElement(*metadata);
+ }
+
+ // Watch out for FrameMetrics copies in profiles
+ aEntry->mLayer->SetScrollMetadata(metricsArray);
+ aEntry->mLayer->SetAncestorMaskLayers(maskLayers);
+}
+
+static inline Maybe<ParentLayerIntRect>
+GetStationaryClipInContainer(Layer* aLayer)
+{
+ if (size_t metricsCount = aLayer->GetScrollMetadataCount()) {
+ return aLayer->GetScrollMetadata(metricsCount - 1).GetClipRect();
+ }
+ return aLayer->GetClipRect();
+}
+
+void
+ContainerState::PostprocessRetainedLayers(nsIntRegion* aOpaqueRegionForContainer)
+{
+ AutoTArray<OpaqueRegionEntry,4> opaqueRegions;
+ bool hideAll = false;
+ int32_t opaqueRegionForContainer = -1;
+
+ for (int32_t i = mNewChildLayers.Length() - 1; i >= 0; --i) {
+ NewLayerEntry* e = &mNewChildLayers.ElementAt(i);
+ if (!e->mLayer) {
+ continue;
+ }
+
+ OpaqueRegionEntry* data = FindOpaqueRegionEntry(opaqueRegions, e->mAnimatedGeometryRoot);
+
+ SetupScrollingMetadata(e);
+
+ if (hideAll) {
+ e->mVisibleRegion.SetEmpty();
+ } else if (!e->mLayer->IsScrollbarContainer()) {
+ Maybe<ParentLayerIntRect> clipRect = GetStationaryClipInContainer(e->mLayer);
+ if (clipRect && opaqueRegionForContainer >= 0 &&
+ opaqueRegions[opaqueRegionForContainer].mOpaqueRegion.Contains(clipRect->ToUnknownRect())) {
+ e->mVisibleRegion.SetEmpty();
+ } else if (data) {
+ e->mVisibleRegion.Sub(e->mVisibleRegion, data->mOpaqueRegion);
+ }
+ }
+
+ SetOuterVisibleRegionForLayer(e->mLayer,
+ e->mVisibleRegion,
+ e->mLayerContentsVisibleRect.width >= 0 ? &e->mLayerContentsVisibleRect : nullptr,
+ e->mUntransformedVisibleRegion);
+
+ if (!e->mOpaqueRegion.IsEmpty()) {
+ AnimatedGeometryRoot* animatedGeometryRootToCover = e->mAnimatedGeometryRoot;
+ if (e->mOpaqueForAnimatedGeometryRootParent &&
+ e->mAnimatedGeometryRoot->mParentAGR == mContainerAnimatedGeometryRoot) {
+ animatedGeometryRootToCover = mContainerAnimatedGeometryRoot;
+ data = FindOpaqueRegionEntry(opaqueRegions, animatedGeometryRootToCover);
+ }
+
+ if (!data) {
+ if (animatedGeometryRootToCover == mContainerAnimatedGeometryRoot) {
+ NS_ASSERTION(opaqueRegionForContainer == -1, "Already found it?");
+ opaqueRegionForContainer = opaqueRegions.Length();
+ }
+ data = opaqueRegions.AppendElement();
+ data->mAnimatedGeometryRoot = animatedGeometryRootToCover;
+ }
+
+ nsIntRegion clippedOpaque = e->mOpaqueRegion;
+ Maybe<ParentLayerIntRect> clipRect = e->mLayer->GetCombinedClipRect();
+ if (clipRect) {
+ clippedOpaque.AndWith(clipRect->ToUnknownRect());
+ }
+ if (e->mLayer->GetIsFixedPosition() && e->mLayer->GetScrolledClip()) {
+ // The clip can move asynchronously, so we can't rely on opaque parts
+ // staying in the same place.
+ clippedOpaque.SetEmpty();
+ } else if (e->mHideAllLayersBelow) {
+ hideAll = true;
+ }
+ data->mOpaqueRegion.Or(data->mOpaqueRegion, clippedOpaque);
+ }
+
+ if (e->mLayer->GetType() == Layer::TYPE_READBACK) {
+ // ReadbackLayers need to accurately read what's behind them. So,
+ // we don't want to do any occlusion culling of layers behind them.
+ // Theoretically we could just punch out the ReadbackLayer's rectangle
+ // from all mOpaqueRegions, but that's probably not worth doing.
+ opaqueRegions.Clear();
+ opaqueRegionForContainer = -1;
+ }
+ }
+
+ if (opaqueRegionForContainer >= 0) {
+ aOpaqueRegionForContainer->Or(*aOpaqueRegionForContainer,
+ opaqueRegions[opaqueRegionForContainer].mOpaqueRegion);
+ }
+}
+
+void
+ContainerState::Finish(uint32_t* aTextContentFlags,
+ const nsIntRect& aContainerPixelBounds,
+ nsDisplayList* aChildItems, bool* aHasComponentAlphaChildren)
+{
+ mPaintedLayerDataTree.Finish();
+
+ if (!mParameters.mForEventsAndPluginsOnly) {
+ NS_ASSERTION(mContainerBounds.IsEqualInterior(mAccumulatedChildBounds),
+ "Bounds computation mismatch");
+ }
+
+ if (mLayerBuilder->IsBuildingRetainedLayers()) {
+ nsIntRegion containerOpaqueRegion;
+ PostprocessRetainedLayers(&containerOpaqueRegion);
+ if (containerOpaqueRegion.Contains(aContainerPixelBounds)) {
+ aChildItems->SetIsOpaque();
+ }
+ }
+
+ uint32_t textContentFlags = 0;
+
+ // Make sure that current/existing layers are added to the parent and are
+ // in the correct order.
+ Layer* layer = nullptr;
+ Layer* prevChild = nullptr;
+ for (uint32_t i = 0; i < mNewChildLayers.Length(); ++i, prevChild = layer) {
+ if (!mNewChildLayers[i].mLayer) {
+ continue;
+ }
+
+ layer = mNewChildLayers[i].mLayer;
+
+ if (!layer->GetVisibleRegion().IsEmpty()) {
+ textContentFlags |=
+ layer->GetContentFlags() & (Layer::CONTENT_COMPONENT_ALPHA |
+ Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT |
+ Layer::CONTENT_DISABLE_FLATTENING);
+
+ // Notify the parent of component alpha children unless it's coming from
+ // within a child that has asked not to contribute to layer flattening.
+ if (aHasComponentAlphaChildren &&
+ mNewChildLayers[i].mPropagateComponentAlphaFlattening &&
+ (layer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA)) {
+
+ for (int32_t j = i - 1; j >= 0; j--) {
+ if (mNewChildLayers[j].mVisibleRegion.Intersects(mNewChildLayers[i].mVisibleRegion.GetBounds())) {
+ if (mNewChildLayers[j].mLayerState != LAYER_ACTIVE_FORCE) {
+ *aHasComponentAlphaChildren = true;
+ }
+ break;
+
+ }
+
+ }
+ }
+ }
+
+ if (!layer->GetParent()) {
+ // This is not currently a child of the container, so just add it
+ // now.
+ mContainerLayer->InsertAfter(layer, prevChild);
+ } else {
+ NS_ASSERTION(layer->GetParent() == mContainerLayer,
+ "Layer shouldn't be the child of some other container");
+ if (layer->GetPrevSibling() != prevChild) {
+ mContainerLayer->RepositionChild(layer, prevChild);
+ }
+ }
+ }
+
+ // Remove old layers that have become unused.
+ if (!layer) {
+ layer = mContainerLayer->GetFirstChild();
+ } else {
+ layer = layer->GetNextSibling();
+ }
+ while (layer) {
+ Layer *layerToRemove = layer;
+ layer = layer->GetNextSibling();
+ mContainerLayer->RemoveChild(layerToRemove);
+ }
+
+ *aTextContentFlags = textContentFlags;
+}
+
+static inline gfxSize RoundToFloatPrecision(const gfxSize& aSize)
+{
+ return gfxSize(float(aSize.width), float(aSize.height));
+}
+
+static void RestrictScaleToMaxLayerSize(gfxSize& aScale,
+ const nsRect& aVisibleRect,
+ nsIFrame* aContainerFrame,
+ Layer* aContainerLayer)
+{
+ if (!aContainerLayer->Manager()->IsWidgetLayerManager()) {
+ return;
+ }
+
+ nsIntRect pixelSize =
+ aVisibleRect.ScaleToOutsidePixels(aScale.width, aScale.height,
+ aContainerFrame->PresContext()->AppUnitsPerDevPixel());
+
+ int32_t maxLayerSize = aContainerLayer->GetMaxLayerSize();
+
+ if (pixelSize.width > maxLayerSize) {
+ float scale = (float)pixelSize.width / maxLayerSize;
+ scale = gfxUtils::ClampToScaleFactor(scale);
+ aScale.width /= scale;
+ }
+ if (pixelSize.height > maxLayerSize) {
+ float scale = (float)pixelSize.height / maxLayerSize;
+ scale = gfxUtils::ClampToScaleFactor(scale);
+ aScale.height /= scale;
+ }
+}
+static bool
+ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder,
+ nsDisplayListBuilder* aDisplayListBuilder,
+ nsIFrame* aContainerFrame,
+ nsDisplayItem* aContainerItem,
+ const nsRect& aVisibleRect,
+ const Matrix4x4* aTransform,
+ const ContainerLayerParameters& aIncomingScale,
+ ContainerLayer* aLayer,
+ LayerState aState,
+ ContainerLayerParameters& aOutgoingScale)
+{
+ nsIntPoint offset;
+
+ Matrix4x4 transform =
+ Matrix4x4::Scaling(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0);
+ if (aTransform) {
+ // aTransform is applied first, then the scale is applied to the result
+ transform = (*aTransform)*transform;
+ // Set any matrix entries close to integers to be those exact integers.
+ // This protects against floating-point inaccuracies causing problems
+ // in the checks below.
+ // We use the fixed epsilon version here because we don't want the nudging
+ // to depend on the scroll position.
+ transform.NudgeToIntegersFixedEpsilon();
+ }
+ Matrix transform2d;
+ if (aContainerFrame &&
+ (aState == LAYER_INACTIVE || aState == LAYER_SVG_EFFECTS) &&
+ (!aTransform || (aTransform->Is2D(&transform2d) &&
+ !transform2d.HasNonTranslation()))) {
+ // When we have an inactive ContainerLayer, translate the container by the offset to the
+ // reference frame (and offset all child layers by the reverse) so that the coordinate
+ // space of the child layers isn't affected by scrolling.
+ // This gets confusing for complicated transform (since we'd have to compute the scale
+ // factors for the matrix), so we don't bother. Any frames that are building an nsDisplayTransform
+ // for a css transform would have 0,0 as their offset to the reference frame, so this doesn't
+ // matter.
+ nsPoint appUnitOffset = aDisplayListBuilder->ToReferenceFrame(aContainerFrame);
+ nscoord appUnitsPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
+ offset = nsIntPoint(
+ NS_lround(NSAppUnitsToDoublePixels(appUnitOffset.x, appUnitsPerDevPixel)*aIncomingScale.mXScale),
+ NS_lround(NSAppUnitsToDoublePixels(appUnitOffset.y, appUnitsPerDevPixel)*aIncomingScale.mYScale));
+ }
+ transform.PostTranslate(offset.x + aIncomingScale.mOffset.x,
+ offset.y + aIncomingScale.mOffset.y,
+ 0);
+
+ if (transform.IsSingular()) {
+ return false;
+ }
+
+ bool canDraw2D = transform.CanDraw2D(&transform2d);
+ gfxSize scale;
+ // XXX Should we do something for 3D transforms?
+ if (canDraw2D &&
+ !aContainerFrame->Combines3DTransformWithAncestors() &&
+ !aContainerFrame->HasPerspective()) {
+ // If the container's transform is animated off main thread, fix a suitable scale size
+ // for animation
+ if (aContainerItem &&
+ aContainerItem->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
+ EffectCompositor::HasAnimationsForCompositor(
+ aContainerFrame, eCSSProperty_transform)) {
+ // Use the size of the nearest widget as the maximum size. This
+ // is important since it might be a popup that is bigger than the
+ // pres context's size.
+ nsPresContext* presContext = aContainerFrame->PresContext();
+ nsIWidget* widget = aContainerFrame->GetNearestWidget();
+ nsSize displaySize;
+ if (widget) {
+ LayoutDeviceIntSize widgetSize = widget->GetClientSize();
+ int32_t p2a = presContext->AppUnitsPerDevPixel();
+ displaySize.width = NSIntPixelsToAppUnits(widgetSize.width, p2a);
+ displaySize.height = NSIntPixelsToAppUnits(widgetSize.height, p2a);
+ } else {
+ displaySize = presContext->GetVisibleArea().Size();
+ }
+ // compute scale using the animation on the container (ignoring
+ // its ancestors)
+ scale = nsLayoutUtils::ComputeSuitableScaleForAnimation(
+ aContainerFrame, aVisibleRect.Size(),
+ displaySize);
+ // multiply by the scale inherited from ancestors--we use a uniform
+ // scale factor to prevent blurring when the layer is rotated.
+ float incomingScale = std::max(aIncomingScale.mXScale, aIncomingScale.mYScale);
+ scale.width *= incomingScale;
+ scale.height *= incomingScale;
+ } else {
+ // Scale factors are normalized to a power of 2 to reduce the number of resolution changes
+ scale = RoundToFloatPrecision(ThebesMatrix(transform2d).ScaleFactors(true));
+ // For frames with a changing transform that's not just a translation,
+ // round scale factors up to nearest power-of-2 boundary so that we don't
+ // keep having to redraw the content as it scales up and down. Rounding up to nearest
+ // power-of-2 boundary ensures we never scale up, only down --- avoiding
+ // jaggies. It also ensures we never scale down by more than a factor of 2,
+ // avoiding bad downscaling quality.
+ Matrix frameTransform;
+ if (ActiveLayerTracker::IsStyleAnimated(aDisplayListBuilder, aContainerFrame, eCSSProperty_transform) &&
+ aTransform &&
+ (!aTransform->Is2D(&frameTransform) || frameTransform.HasNonTranslationOrFlip())) {
+ // Don't clamp the scale factor when the new desired scale factor matches the old one
+ // or it was previously unscaled.
+ bool clamp = true;
+ Matrix oldFrameTransform2d;
+ if (aLayer->GetBaseTransform().Is2D(&oldFrameTransform2d)) {
+ gfxSize oldScale = RoundToFloatPrecision(ThebesMatrix(oldFrameTransform2d).ScaleFactors(true));
+ if (oldScale == scale || oldScale == gfxSize(1.0, 1.0)) {
+ clamp = false;
+ }
+ }
+ if (clamp) {
+ scale.width = gfxUtils::ClampToScaleFactor(scale.width);
+ scale.height = gfxUtils::ClampToScaleFactor(scale.height);
+ }
+ } else {
+ // XXX Do we need to move nearly-integer values to integers here?
+ }
+ }
+ // If the scale factors are too small, just use 1.0. The content is being
+ // scaled out of sight anyway.
+ if (fabs(scale.width) < 1e-8 || fabs(scale.height) < 1e-8) {
+ scale = gfxSize(1.0, 1.0);
+ }
+ // If this is a transform container layer, then pre-rendering might
+ // mean we try render a layer bigger than the max texture size. If we have
+ // tiling, that's not a problem, since we'll automatically choose a tiled
+ // layer for layers of that size. If not, we need to apply clamping to
+ // prevent this.
+ if (aTransform && !gfxPrefs::LayersTilesEnabled()) {
+ RestrictScaleToMaxLayerSize(scale, aVisibleRect, aContainerFrame, aLayer);
+ }
+ } else {
+ scale = gfxSize(1.0, 1.0);
+ }
+
+ // Store the inverse of our resolution-scale on the layer
+ aLayer->SetBaseTransform(transform);
+ aLayer->SetPreScale(1.0f/float(scale.width),
+ 1.0f/float(scale.height));
+ aLayer->SetInheritedScale(aIncomingScale.mXScale,
+ aIncomingScale.mYScale);
+
+ aOutgoingScale =
+ ContainerLayerParameters(scale.width, scale.height, -offset, aIncomingScale);
+ if (aTransform) {
+ aOutgoingScale.mInTransformedSubtree = true;
+ if (ActiveLayerTracker::IsStyleAnimated(aDisplayListBuilder, aContainerFrame,
+ eCSSProperty_transform)) {
+ aOutgoingScale.mInActiveTransformedSubtree = true;
+ }
+ }
+ if ((aLayerBuilder->IsBuildingRetainedLayers() &&
+ (!canDraw2D || transform2d.HasNonIntegerTranslation())) ||
+ aContainerFrame->Extend3DContext() ||
+ aContainerFrame->Combines3DTransformWithAncestors()) {
+ aOutgoingScale.mDisableSubpixelAntialiasingInDescendants = true;
+ }
+ return true;
+}
+
+already_AddRefed<ContainerLayer>
+FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsIFrame* aContainerFrame,
+ nsDisplayItem* aContainerItem,
+ nsDisplayList* aChildren,
+ const ContainerLayerParameters& aParameters,
+ const Matrix4x4* aTransform,
+ uint32_t aFlags)
+{
+ uint32_t containerDisplayItemKey =
+ aContainerItem ? aContainerItem->GetPerFrameKey() : nsDisplayItem::TYPE_ZERO;
+ NS_ASSERTION(aContainerFrame, "Container display items here should have a frame");
+ NS_ASSERTION(!aContainerItem ||
+ aContainerItem->Frame() == aContainerFrame,
+ "Container display item must match given frame");
+
+ if (!aParameters.mXScale || !aParameters.mYScale) {
+ return nullptr;
+ }
+
+ RefPtr<ContainerLayer> containerLayer;
+ if (aManager == mRetainingManager) {
+ // Using GetOldLayerFor will search merged frames, as well as the underlying
+ // frame. The underlying frame can change when a page scrolls, so this
+ // avoids layer recreation in the situation that a new underlying frame is
+ // picked for a layer.
+ Layer* oldLayer = nullptr;
+ if (aContainerItem) {
+ oldLayer = GetOldLayerFor(aContainerItem);
+ } else {
+ DisplayItemData *data = GetOldLayerForFrame(aContainerFrame, containerDisplayItemKey);
+ if (data) {
+ oldLayer = data->mLayer;
+ }
+ }
+
+ if (oldLayer) {
+ NS_ASSERTION(oldLayer->Manager() == aManager, "Wrong manager");
+ if (oldLayer->HasUserData(&gPaintedDisplayItemLayerUserData)) {
+ // The old layer for this item is actually our PaintedLayer
+ // because we rendered its layer into that PaintedLayer. 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);
+ ResetLayerStateForRecycling(containerLayer);
+ }
+ }
+ }
+ if (!containerLayer) {
+ // No suitable existing layer was found.
+ containerLayer = aManager->CreateContainerLayer();
+ if (!containerLayer)
+ return nullptr;
+ }
+
+ LayerState state = aContainerItem ? aContainerItem->GetLayerState(aBuilder, aManager, aParameters) : LAYER_ACTIVE;
+ if (state == LAYER_INACTIVE &&
+ nsDisplayItem::ForceActiveLayers()) {
+ state = LAYER_ACTIVE;
+ }
+
+ if (aContainerItem && state == LAYER_ACTIVE_EMPTY) {
+ // Empty layers only have metadata and should never have display items. We
+ // early exit because later, invalidation will walk up the frame tree to
+ // determine which painted layer gets invalidated. Since an empty layer
+ // should never have anything to paint, it should never be invalidated.
+ NS_ASSERTION(aChildren->IsEmpty(), "Should have no children");
+ return containerLayer.forget();
+ }
+
+ const DisplayItemScrollClip* containerScrollClip = aParameters.mScrollClip;
+
+ ContainerLayerParameters scaleParameters;
+ nsRect bounds = aChildren->GetScrollClippedBoundsUpTo(aBuilder, containerScrollClip);
+ nsRect childrenVisible =
+ aContainerItem ? aContainerItem->GetVisibleRectForChildren() :
+ aContainerFrame->GetVisualOverflowRectRelativeToSelf();
+ if (!ChooseScaleAndSetTransform(this, aBuilder, aContainerFrame,
+ aContainerItem,
+ bounds.Intersect(childrenVisible),
+ aTransform, aParameters,
+ containerLayer, state, scaleParameters)) {
+ return nullptr;
+ }
+
+ uint32_t oldGeneration = mContainerLayerGeneration;
+ mContainerLayerGeneration = ++mMaxContainerLayerGeneration;
+
+ if (mRetainingManager) {
+ if (aContainerItem) {
+ StoreDataForFrame(aContainerItem, containerLayer, LAYER_ACTIVE);
+ } else {
+ StoreDataForFrame(aContainerFrame, containerDisplayItemKey, containerLayer, LAYER_ACTIVE);
+ }
+ }
+
+ LayerManagerData* data = static_cast<LayerManagerData*>
+ (aManager->GetUserData(&gLayerManagerUserData));
+
+ nsIntRect pixBounds;
+ nscoord appUnitsPerDevPixel;
+ bool flattenToSingleLayer = false;
+ if ((aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) &&
+ mRetainingManager &&
+ mRetainingManager->ShouldAvoidComponentAlphaLayers() &&
+ !nsLayoutUtils::AsyncPanZoomEnabled(aContainerFrame))
+ {
+ flattenToSingleLayer = true;
+ }
+
+ nscolor backgroundColor = NS_RGBA(0,0,0,0);
+ if (aFlags & CONTAINER_ALLOW_PULL_BACKGROUND_COLOR) {
+ backgroundColor = aParameters.mBackgroundColor;
+ }
+
+ uint32_t flags;
+ while (true) {
+ ContainerState state(aBuilder, aManager, aManager->GetLayerBuilder(),
+ aContainerFrame, aContainerItem, bounds,
+ containerLayer, scaleParameters, flattenToSingleLayer,
+ backgroundColor, containerScrollClip);
+
+ state.ProcessDisplayItems(aChildren);
+
+ // 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.
+ bool hasComponentAlphaChildren = false;
+ bool mayFlatten =
+ mRetainingManager &&
+ mRetainingManager->ShouldAvoidComponentAlphaLayers() &&
+ !flattenToSingleLayer &&
+ !nsLayoutUtils::AsyncPanZoomEnabled(aContainerFrame);
+
+ pixBounds = state.ScaleToOutsidePixels(bounds, false);
+ appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel();
+ state.Finish(&flags, pixBounds, aChildren, mayFlatten ? &hasComponentAlphaChildren : nullptr);
+
+ if (hasComponentAlphaChildren &&
+ !(flags & Layer::CONTENT_DISABLE_FLATTENING) &&
+ containerLayer->HasMultipleChildren())
+ {
+ // 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.
+ flattenToSingleLayer = true;
+
+ // Restore DisplayItemData
+ for (auto iter = data->mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
+ DisplayItemData* data = iter.Get()->GetKey();
+ if (data->mUsed && data->mContainerLayerGeneration >= mContainerLayerGeneration) {
+ iter.Remove();
+ }
+ }
+
+ // Restore PaintedLayerItemEntries
+ for (auto iter = mPaintedLayerItems.Iter(); !iter.Done(); iter.Next()) {
+ PaintedLayerItemsEntry* entry = iter.Get();
+ if (entry->mContainerLayerGeneration >= mContainerLayerGeneration) {
+ // We can just remove these items rather than attempting to revert them
+ // because we're going to want to invalidate everything when transitioning
+ // to component alpha flattening.
+ iter.Remove();
+ continue;
+ }
+
+ for (uint32_t i = 0; i < entry->mItems.Length(); i++) {
+ if (entry->mItems[i].mContainerLayerGeneration >= mContainerLayerGeneration) {
+ entry->mItems.TruncateLength(i);
+ break;
+ }
+ }
+ }
+
+ aContainerFrame->AddStateBits(NS_FRAME_NO_COMPONENT_ALPHA);
+ continue;
+ }
+ break;
+ }
+
+ // CONTENT_COMPONENT_ALPHA is propogated up to the nearest CONTENT_OPAQUE
+ // ancestor so that BasicLayerManager knows when to copy the background into
+ // pushed groups. Accelerated layers managers can't necessarily do this (only
+ // when the visible region is a simple rect), so we propogate
+ // CONTENT_COMPONENT_ALPHA_DESCENDANT all the way to the root.
+ if (flags & Layer::CONTENT_COMPONENT_ALPHA) {
+ flags |= Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT;
+ }
+
+ // Make sure that rounding the visible region out didn't add any area
+ // we won't paint
+ if (aChildren->IsOpaque() && !aChildren->NeedsTransparentSurface()) {
+ bounds.ScaleRoundIn(scaleParameters.mXScale, scaleParameters.mYScale);
+ if (bounds.Contains(ToAppUnits(pixBounds, appUnitsPerDevPixel))) {
+ // Clear CONTENT_COMPONENT_ALPHA and add CONTENT_OPAQUE instead.
+ flags &= ~Layer::CONTENT_COMPONENT_ALPHA;
+ flags |= Layer::CONTENT_OPAQUE;
+ }
+ }
+ containerLayer->SetContentFlags(flags);
+ // If aContainerItem is non-null some BuildContainerLayer further up the
+ // call stack is responsible for setting containerLayer's visible region.
+ if (!aContainerItem) {
+ containerLayer->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(pixBounds));
+ }
+ if (aParameters.mLayerContentsVisibleRect) {
+ *aParameters.mLayerContentsVisibleRect = pixBounds + scaleParameters.mOffset;
+ }
+
+ mContainerLayerGeneration = oldGeneration;
+ nsPresContext::ClearNotifySubDocInvalidationData(containerLayer);
+
+ return containerLayer.forget();
+}
+
+Layer*
+FrameLayerBuilder::GetLeafLayerFor(nsDisplayListBuilder* aBuilder,
+ nsDisplayItem* aItem)
+{
+ Layer* layer = GetOldLayerFor(aItem);
+ if (!layer)
+ return nullptr;
+ if (layer->HasUserData(&gPaintedDisplayItemLayerUserData)) {
+ // 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 nullptr;
+ }
+ ResetLayerStateForRecycling(layer);
+ return layer;
+}
+
+/* static */ void
+FrameLayerBuilder::InvalidateAllLayers(LayerManager* aManager)
+{
+ LayerManagerData* data = static_cast<LayerManagerData*>
+ (aManager->GetUserData(&gLayerManagerUserData));
+ if (data) {
+ data->mInvalidateAllLayers = true;
+ }
+}
+
+/* static */ void
+FrameLayerBuilder::InvalidateAllLayersForFrame(nsIFrame *aFrame)
+{
+ const nsTArray<DisplayItemData*>* array =
+ aFrame->Properties().Get(LayerManagerDataProperty());
+ if (array) {
+ for (uint32_t i = 0; i < array->Length(); i++) {
+ AssertDisplayItemData(array->ElementAt(i))->mParent->mInvalidateAllLayers = true;
+ }
+ }
+}
+
+/* static */
+Layer*
+FrameLayerBuilder::GetDedicatedLayer(nsIFrame* aFrame, uint32_t aDisplayItemKey)
+{
+ //TODO: This isn't completely correct, since a frame could exist as a layer
+ // in the normal widget manager, and as a different layer (or no layer)
+ // in the secondary manager
+
+ const nsTArray<DisplayItemData*>* array =
+ aFrame->Properties().Get(LayerManagerDataProperty());
+ if (array) {
+ for (uint32_t i = 0; i < array->Length(); i++) {
+ DisplayItemData *element = AssertDisplayItemData(array->ElementAt(i));
+ if (!element->mParent->mLayerManager->IsWidgetLayerManager()) {
+ continue;
+ }
+ if (element->mDisplayItemKey == aDisplayItemKey) {
+ if (element->mOptLayer) {
+ return element->mOptLayer;
+ }
+
+ Layer* layer = element->mLayer;
+ if (!layer->HasUserData(&gColorLayerUserData) &&
+ !layer->HasUserData(&gImageLayerUserData) &&
+ !layer->HasUserData(&gPaintedDisplayItemLayerUserData)) {
+ return layer;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+static gfxSize
+PredictScaleForContent(nsIFrame* aFrame, nsIFrame* aAncestorWithScale,
+ const gfxSize& aScale)
+{
+ Matrix4x4 transform = Matrix4x4::Scaling(aScale.width, aScale.height, 1.0);
+ if (aFrame != aAncestorWithScale) {
+ // aTransform is applied first, then the scale is applied to the result
+ transform = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestorWithScale)*transform;
+ }
+ Matrix transform2d;
+ if (transform.CanDraw2D(&transform2d)) {
+ return ThebesMatrix(transform2d).ScaleFactors(true);
+ }
+ return gfxSize(1.0, 1.0);
+}
+
+gfxSize
+FrameLayerBuilder::GetPaintedLayerScaleForFrame(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(aFrame, "need a frame");
+ nsIFrame* last = nullptr;
+ for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
+ last = f;
+
+ if (nsLayoutUtils::IsPopup(f)) {
+ // Don't examine ancestors of a popup. It won't make sense to check
+ // the transform from some content inside the popup to some content
+ // which is an ancestor of the popup.
+ break;
+ }
+
+ const nsTArray<DisplayItemData*>* array =
+ f->Properties().Get(LayerManagerDataProperty());
+ if (!array) {
+ continue;
+ }
+
+ for (uint32_t i = 0; i < array->Length(); i++) {
+ Layer* layer = AssertDisplayItemData(array->ElementAt(i))->mLayer;
+ ContainerLayer* container = layer->AsContainerLayer();
+ if (!container ||
+ !layer->Manager()->IsWidgetLayerManager()) {
+ continue;
+ }
+ for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) {
+ PaintedDisplayItemLayerUserData* data =
+ static_cast<PaintedDisplayItemLayerUserData*>
+ (l->GetUserData(&gPaintedDisplayItemLayerUserData));
+ if (data) {
+ return PredictScaleForContent(aFrame, f, gfxSize(data->mXScale, data->mYScale));
+ }
+ }
+ }
+ }
+
+ float presShellResolution = last->PresContext()->PresShell()->GetResolution();
+ return PredictScaleForContent(aFrame, last,
+ gfxSize(presShellResolution, presShellResolution));
+}
+
+#ifdef MOZ_DUMP_PAINTING
+static void DebugPaintItem(DrawTarget& aDrawTarget,
+ nsPresContext* aPresContext,
+ nsDisplayItem *aItem,
+ nsDisplayListBuilder* aBuilder)
+{
+ bool snap;
+ Rect bounds = NSRectToRect(aItem->GetBounds(aBuilder, &snap),
+ aPresContext->AppUnitsPerDevPixel());
+
+ RefPtr<DrawTarget> tempDT =
+ aDrawTarget.CreateSimilarDrawTarget(IntSize::Truncate(bounds.width, bounds.height),
+ SurfaceFormat::B8G8R8A8);
+ RefPtr<gfxContext> context = gfxContext::CreateOrNull(tempDT);
+ if (!context) {
+ // Leave this as crash, it's in the debugging code, we want to know
+ gfxDevCrash(LogReason::InvalidContext) << "DebugPaintItem context problem " << gfx::hexa(tempDT);
+ return;
+ }
+ context->SetMatrix(gfxMatrix::Translation(-bounds.x, -bounds.y));
+ nsRenderingContext ctx(context);
+
+ aItem->Paint(aBuilder, &ctx);
+ RefPtr<SourceSurface> surface = tempDT->Snapshot();
+ DumpPaintedImage(aItem, surface);
+
+ aDrawTarget.DrawSurface(surface, bounds, Rect(Point(0,0), bounds.Size()));
+
+ aItem->SetPainted();
+}
+#endif
+
+/* static */ void
+FrameLayerBuilder::RecomputeVisibilityForItems(nsTArray<ClippedDisplayItem>& aItems,
+ nsDisplayListBuilder *aBuilder,
+ const nsIntRegion& aRegionToDraw,
+ const nsIntPoint& aOffset,
+ int32_t aAppUnitsPerDevPixel,
+ float aXScale,
+ float aYScale)
+{
+ uint32_t i;
+ // Update visible regions. We perform visibility analysis to take account
+ // of occlusion culling.
+ nsRegion visible = aRegionToDraw.ToAppUnits(aAppUnitsPerDevPixel);
+ visible.MoveBy(NSIntPixelsToAppUnits(aOffset.x, aAppUnitsPerDevPixel),
+ NSIntPixelsToAppUnits(aOffset.y, aAppUnitsPerDevPixel));
+ visible.ScaleInverseRoundOut(aXScale, aYScale);
+
+ for (i = aItems.Length(); i > 0; --i) {
+ ClippedDisplayItem* cdi = &aItems[i - 1];
+ const DisplayItemClip& clip = cdi->mItem->GetClip();
+
+ NS_ASSERTION(AppUnitsPerDevPixel(cdi->mItem) == aAppUnitsPerDevPixel,
+ "a painted layer should contain items only at the same zoom");
+
+ MOZ_ASSERT(clip.HasClip() || clip.GetRoundedRectCount() == 0,
+ "If we have rounded rects, we must have a clip rect");
+
+ if (!clip.IsRectAffectedByClip(visible.GetBounds())) {
+ cdi->mItem->RecomputeVisibility(aBuilder, &visible);
+ continue;
+ }
+
+ // Do a little dance to account for the fact that we're clipping
+ // to cdi->mClipRect
+ nsRegion clipped;
+ clipped.And(visible, clip.NonRoundedIntersection());
+ nsRegion finalClipped = clipped;
+ cdi->mItem->RecomputeVisibility(aBuilder, &finalClipped);
+ // If we have rounded clip rects, don't subtract from the visible
+ // region since we aren't displaying everything inside the rect.
+ if (clip.GetRoundedRectCount() == 0) {
+ nsRegion removed;
+ removed.Sub(clipped, finalClipped);
+ nsRegion newVisible;
+ newVisible.Sub(visible, removed);
+ // Don't let the visible region get too complex.
+ if (newVisible.GetNumRects() <= 15) {
+ visible = newVisible;
+ }
+ }
+ }
+}
+
+void
+FrameLayerBuilder::PaintItems(nsTArray<ClippedDisplayItem>& aItems,
+ const nsIntRect& aRect,
+ gfxContext *aContext,
+ nsRenderingContext *aRC,
+ nsDisplayListBuilder* aBuilder,
+ nsPresContext* aPresContext,
+ const nsIntPoint& aOffset,
+ float aXScale, float aYScale,
+ int32_t aCommonClipCount)
+{
+ DrawTarget& aDrawTarget = *aRC->GetDrawTarget();
+
+ int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
+ nsRect boundRect = ToAppUnits(aRect, appUnitsPerDevPixel);
+ boundRect.MoveBy(NSIntPixelsToAppUnits(aOffset.x, appUnitsPerDevPixel),
+ NSIntPixelsToAppUnits(aOffset.y, appUnitsPerDevPixel));
+ boundRect.ScaleInverseRoundOut(aXScale, aYScale);
+
+ DisplayItemClip currentClip;
+ bool currentClipIsSetInContext = false;
+ DisplayItemClip tmpClip;
+
+ for (uint32_t i = 0; i < aItems.Length(); ++i) {
+ ClippedDisplayItem* cdi = &aItems[i];
+
+ nsRect paintRect = cdi->mItem->GetVisibleRect().Intersect(boundRect);
+ if (paintRect.IsEmpty())
+ continue;
+
+#ifdef MOZ_DUMP_PAINTING
+ PROFILER_LABEL_PRINTF("DisplayList", "Draw",
+ js::ProfileEntry::Category::GRAPHICS, "%s", cdi->mItem->Name());
+#else
+ PROFILER_LABEL("DisplayList", "Draw",
+ js::ProfileEntry::Category::GRAPHICS);
+#endif
+
+ // If the new desired clip state is different from the current state,
+ // update the clip.
+ const DisplayItemClip* clip = &cdi->mItem->GetClip();
+ if (clip->GetRoundedRectCount() > 0 &&
+ !clip->IsRectClippedByRoundedCorner(cdi->mItem->GetVisibleRect())) {
+ tmpClip = *clip;
+ tmpClip.RemoveRoundedCorners();
+ clip = &tmpClip;
+ }
+ if (currentClipIsSetInContext != clip->HasClip() ||
+ (clip->HasClip() && *clip != currentClip)) {
+ if (currentClipIsSetInContext) {
+ aContext->Restore();
+ }
+ currentClipIsSetInContext = clip->HasClip();
+ if (currentClipIsSetInContext) {
+ currentClip = *clip;
+ aContext->Save();
+ NS_ASSERTION(aCommonClipCount < 100,
+ "Maybe you really do have more than a hundred clipping rounded rects, or maybe something has gone wrong.");
+ currentClip.ApplyTo(aContext, aPresContext, aCommonClipCount);
+ aContext->NewPath();
+ }
+ }
+
+ if (cdi->mInactiveLayerManager) {
+ bool saved = aDrawTarget.GetPermitSubpixelAA();
+ PaintInactiveLayer(aBuilder, cdi->mInactiveLayerManager, cdi->mItem, aContext, aRC);
+ aDrawTarget.SetPermitSubpixelAA(saved);
+ } else {
+ nsIFrame* frame = cdi->mItem->Frame();
+ if (aBuilder->IsPaintingToWindow()) {
+ frame->AddStateBits(NS_FRAME_PAINTED_THEBES);
+ }
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpPaintItems()) {
+ DebugPaintItem(aDrawTarget, aPresContext, cdi->mItem, aBuilder);
+ } else {
+#else
+ {
+#endif
+ cdi->mItem->Paint(aBuilder, aRC);
+ }
+ }
+
+ if (CheckDOMModified())
+ break;
+ }
+
+ if (currentClipIsSetInContext) {
+ aContext->Restore();
+ }
+}
+
+/**
+ * Returns true if it is preferred to draw the list of display
+ * items separately for each rect in the visible region rather
+ * than clipping to a complex region.
+ */
+static bool
+ShouldDrawRectsSeparately(DrawTarget* aDrawTarget, DrawRegionClip aClip)
+{
+ if (!gfxPrefs::LayoutPaintRectsSeparately() ||
+ aClip == DrawRegionClip::NONE) {
+ return false;
+ }
+
+ return !aDrawTarget->SupportsRegionClipping();
+}
+
+static void DrawForcedBackgroundColor(DrawTarget& aDrawTarget,
+ const IntRect& aBounds,
+ nscolor aBackgroundColor)
+{
+ if (NS_GET_A(aBackgroundColor) > 0) {
+ ColorPattern color(ToDeviceColor(aBackgroundColor));
+ aDrawTarget.FillRect(Rect(aBounds), color);
+ }
+}
+
+/*
+ * A note on residual transforms:
+ *
+ * In a transformed subtree we sometimes apply the PaintedLayer's
+ * "residual transform" when drawing content into the PaintedLayer.
+ * This is a translation by components in the range [-0.5,0.5) provided
+ * by the layer system; applying the residual transform followed by the
+ * transforms used by layer compositing ensures that the subpixel alignment
+ * of the content of the PaintedLayer exactly matches what it would be if
+ * we used cairo/Thebes to draw directly to the screen without going through
+ * retained layer buffers.
+ *
+ * The visible and valid regions of the PaintedLayer are computed without
+ * knowing the residual transform (because we don't know what the residual
+ * transform is going to be until we've built the layer tree!). So we have to
+ * consider whether content painted in the range [x, xmost) might be painted
+ * outside the visible region we computed for that content. The visible region
+ * would be [floor(x), ceil(xmost)). The content would be rendered at
+ * [x + r, xmost + r), where -0.5 <= r < 0.5. So some half-rendered pixels could
+ * indeed fall outside the computed visible region, which is not a big deal;
+ * similar issues already arise when we snap cliprects to nearest pixels.
+ * Note that if the rendering of the content is snapped to nearest pixels ---
+ * which it often is --- then the content is actually rendered at
+ * [snap(x + r), snap(xmost + r)). It turns out that floor(x) <= snap(x + r)
+ * and ceil(xmost) >= snap(xmost + r), so the rendering of snapped content
+ * always falls within the visible region we computed.
+ */
+
+/* static */ void
+FrameLayerBuilder::DrawPaintedLayer(PaintedLayer* aLayer,
+ gfxContext* aContext,
+ const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aDirtyRegion,
+ DrawRegionClip aClip,
+ const nsIntRegion& aRegionToInvalidate,
+ void* aCallbackData)
+{
+ DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
+
+ PROFILER_LABEL("FrameLayerBuilder", "DrawPaintedLayer",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ nsDisplayListBuilder* builder = static_cast<nsDisplayListBuilder*>
+ (aCallbackData);
+
+ FrameLayerBuilder *layerBuilder = aLayer->Manager()->GetLayerBuilder();
+ NS_ASSERTION(layerBuilder, "Unexpectedly null layer builder!");
+
+ if (layerBuilder->CheckDOMModified())
+ return;
+
+ PaintedLayerItemsEntry* entry = layerBuilder->mPaintedLayerItems.GetEntry(aLayer);
+ NS_ASSERTION(entry, "We shouldn't be drawing into a layer with no items!");
+ if (!entry->mContainerLayerFrame) {
+ return;
+ }
+
+
+ PaintedDisplayItemLayerUserData* userData =
+ static_cast<PaintedDisplayItemLayerUserData*>
+ (aLayer->GetUserData(&gPaintedDisplayItemLayerUserData));
+ NS_ASSERTION(userData, "where did our user data go?");
+
+ bool shouldDrawRectsSeparately =
+ ShouldDrawRectsSeparately(&aDrawTarget, aClip);
+
+ if (!shouldDrawRectsSeparately) {
+ if (aClip == DrawRegionClip::DRAW) {
+ gfxUtils::ClipToRegion(aContext, aRegionToDraw);
+ }
+
+ DrawForcedBackgroundColor(aDrawTarget, aRegionToDraw.GetBounds(),
+ userData->mForcedBackgroundColor);
+ }
+
+ if (NS_GET_A(userData->mFontSmoothingBackgroundColor) > 0) {
+ aContext->SetFontSmoothingBackgroundColor(
+ Color::FromABGR(userData->mFontSmoothingBackgroundColor));
+ }
+
+ // make the origin of the context coincide with the origin of the
+ // PaintedLayer
+ gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
+ nsIntPoint offset = GetTranslationForPaintedLayer(aLayer);
+ nsPresContext* presContext = entry->mContainerLayerFrame->PresContext();
+
+ if (!userData->mVisibilityComputedRegion.Contains(aDirtyRegion) &&
+ !layerBuilder->GetContainingPaintedLayerData()) {
+ // Recompute visibility of items in our PaintedLayer, if required. Note
+ // that this recomputes visibility for all descendants of our display
+ // items too, so there's no need to do this for the items in inactive
+ // PaintedLayers. If aDirtyRegion has not changed since the previous call
+ // then we can skip this.
+ int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
+ RecomputeVisibilityForItems(entry->mItems, builder, aDirtyRegion,
+ offset, appUnitsPerDevPixel,
+ userData->mXScale, userData->mYScale);
+ userData->mVisibilityComputedRegion = aDirtyRegion;
+ }
+
+ nsRenderingContext rc(aContext);
+
+ if (shouldDrawRectsSeparately) {
+ for (auto iter = aRegionToDraw.RectIter(); !iter.Done(); iter.Next()) {
+ const nsIntRect& iterRect = iter.Get();
+ gfxContextAutoSaveRestore save(aContext);
+ aContext->NewPath();
+ aContext->Rectangle(ThebesRect(iterRect));
+ aContext->Clip();
+
+ DrawForcedBackgroundColor(aDrawTarget, iterRect,
+ userData->mForcedBackgroundColor);
+
+ // Apply the residual transform if it has been enabled, to ensure that
+ // snapping when we draw into aContext exactly matches the ideal transform.
+ // See above for why this is OK.
+ aContext->SetMatrix(
+ aContext->CurrentMatrix().Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)).
+ Scale(userData->mXScale, userData->mYScale));
+
+ layerBuilder->PaintItems(entry->mItems, iterRect, aContext, &rc,
+ builder, presContext,
+ offset, userData->mXScale, userData->mYScale,
+ entry->mCommonClipCount);
+ if (gfxPrefs::GfxLoggingPaintedPixelCountEnabled()) {
+ aLayer->Manager()->AddPaintedPixelCount(iterRect.Area());
+ }
+ }
+ } else {
+ // Apply the residual transform if it has been enabled, to ensure that
+ // snapping when we draw into aContext exactly matches the ideal transform.
+ // See above for why this is OK.
+ aContext->SetMatrix(
+ aContext->CurrentMatrix().Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)).
+ Scale(userData->mXScale,userData->mYScale));
+
+ layerBuilder->PaintItems(entry->mItems, aRegionToDraw.GetBounds(), aContext, &rc,
+ builder, presContext,
+ offset, userData->mXScale, userData->mYScale,
+ entry->mCommonClipCount);
+ if (gfxPrefs::GfxLoggingPaintedPixelCountEnabled()) {
+ aLayer->Manager()->AddPaintedPixelCount(
+ aRegionToDraw.GetBounds().Area());
+ }
+ }
+
+ aContext->SetFontSmoothingBackgroundColor(Color());
+
+ bool isActiveLayerManager = !aLayer->Manager()->IsInactiveLayerManager();
+
+ if (presContext->GetPaintFlashing() && isActiveLayerManager) {
+ gfxContextAutoSaveRestore save(aContext);
+ if (shouldDrawRectsSeparately) {
+ if (aClip == DrawRegionClip::DRAW) {
+ gfxUtils::ClipToRegion(aContext, aRegionToDraw);
+ }
+ }
+ FlashPaint(aContext);
+ }
+
+ if (presContext->GetDocShell() && isActiveLayerManager) {
+ nsDocShell* docShell = static_cast<nsDocShell*>(presContext->GetDocShell());
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+
+ if (timelines && timelines->HasConsumer(docShell)) {
+ timelines->AddMarkerForDocShell(docShell, Move(
+ MakeUnique<LayerTimelineMarker>(aRegionToDraw)));
+ }
+ }
+
+ if (!aRegionToInvalidate.IsEmpty()) {
+ aLayer->AddInvalidRect(aRegionToInvalidate.GetBounds());
+ }
+}
+
+bool
+FrameLayerBuilder::CheckDOMModified()
+{
+ if (!mRootPresContext ||
+ mInitialDOMGeneration == mRootPresContext->GetDOMGeneration())
+ return false;
+ if (mDetectedDOMModification) {
+ // Don't spam the console with extra warnings
+ return true;
+ }
+ mDetectedDOMModification = true;
+ // Painting is not going to complete properly. There's not much
+ // we can do here though. Invalidating the window to get another repaint
+ // is likely to lead to an infinite repaint loop.
+ NS_WARNING("Detected DOM modification during paint, bailing out!");
+ return true;
+}
+
+/* static */ void
+FrameLayerBuilder::DumpRetainedLayerTree(LayerManager* aManager, std::stringstream& aStream, bool aDumpHtml)
+{
+ aManager->Dump(aStream, "", aDumpHtml);
+}
+
+nsDisplayItemGeometry*
+FrameLayerBuilder::GetMostRecentGeometry(nsDisplayItem* aItem)
+{
+ typedef nsTArray<DisplayItemData*> DataArray;
+
+ // Retrieve the array of DisplayItemData associated with our frame.
+ FrameProperties properties = aItem->Frame()->Properties();
+ const DataArray* dataArray =
+ properties.Get(LayerManagerDataProperty());
+ if (!dataArray) {
+ return nullptr;
+ }
+
+ // Find our display item data, if it exists, and return its geometry.
+ uint32_t itemPerFrameKey = aItem->GetPerFrameKey();
+ for (uint32_t i = 0; i < dataArray->Length(); i++) {
+ DisplayItemData* data = AssertDisplayItemData(dataArray->ElementAt(i));
+ if (data->GetDisplayItemKey() == itemPerFrameKey) {
+ return data->GetGeometry();
+ }
+ }
+
+ return nullptr;
+}
+
+gfx::Rect
+CalculateBounds(const nsTArray<DisplayItemClip::RoundedRect>& aRects, int32_t A2D)
+{
+ nsRect bounds = aRects[0].mRect;
+ for (uint32_t i = 1; i < aRects.Length(); ++i) {
+ bounds.UnionRect(bounds, aRects[i].mRect);
+ }
+
+ return gfx::ToRect(nsLayoutUtils::RectToGfxRect(bounds, A2D));
+}
+
+static void
+SetClipCount(PaintedDisplayItemLayerUserData* apaintedData,
+ uint32_t aClipCount)
+{
+ if (apaintedData) {
+ apaintedData->mMaskClipCount = aClipCount;
+ }
+}
+
+void
+ContainerState::SetupMaskLayer(Layer *aLayer,
+ const DisplayItemClip& aClip,
+ uint32_t aRoundedRectClipCount)
+{
+ // if the number of clips we are going to mask has decreased, then aLayer might have
+ // cached graphics which assume the existence of a soon-to-be non-existent mask layer
+ // in that case, invalidate the whole layer.
+ PaintedDisplayItemLayerUserData* paintedData = GetPaintedDisplayItemLayerUserData(aLayer);
+ if (paintedData &&
+ aRoundedRectClipCount < paintedData->mMaskClipCount) {
+ PaintedLayer* painted = aLayer->AsPaintedLayer();
+ painted->InvalidateRegion(painted->GetValidRegion().GetBounds());
+ }
+
+ // don't build an unnecessary mask
+ if (aClip.GetRoundedRectCount() == 0 ||
+ aRoundedRectClipCount == 0) {
+ SetClipCount(paintedData, 0);
+ return;
+ }
+
+ RefPtr<Layer> maskLayer =
+ CreateMaskLayer(aLayer, aClip, Nothing(), aRoundedRectClipCount);
+
+ if (!maskLayer) {
+ SetClipCount(paintedData, 0);
+ return;
+ }
+
+ aLayer->SetMaskLayer(maskLayer);
+ SetClipCount(paintedData, aRoundedRectClipCount);
+}
+
+already_AddRefed<Layer>
+ContainerState::CreateMaskLayer(Layer *aLayer,
+ const DisplayItemClip& aClip,
+ const Maybe<size_t>& aForAncestorMaskLayer,
+ uint32_t aRoundedRectClipCount)
+{
+ // aLayer will never be the container layer created by an nsDisplayMask
+ // because nsDisplayMask propagates the DisplayItemClip to its contents
+ // and is not clipped itself.
+ // This assertion will fail if that ever stops being the case.
+ MOZ_ASSERT(!aLayer->GetUserData(&gCSSMaskLayerUserData),
+ "A layer contains round clips should not have css-mask on it.");
+
+ // check if we can re-use the mask layer
+ MaskLayerKey recycleKey(aLayer, aForAncestorMaskLayer);
+ RefPtr<ImageLayer> maskLayer =
+ CreateOrRecycleMaskImageLayerFor(recycleKey,
+ [](Layer* aMaskLayer)
+ {
+ aMaskLayer->SetUserData(&gMaskLayerUserData,
+ new MaskLayerUserData());
+ }
+ );
+ MaskLayerUserData* userData = GetMaskLayerUserData(maskLayer);
+
+ int32_t A2D = mContainerFrame->PresContext()->AppUnitsPerDevPixel();
+ MaskLayerUserData newData(aClip, aRoundedRectClipCount, A2D, mParameters);
+ if (*userData == newData) {
+ return maskLayer.forget();
+ }
+
+ // calculate a more precise bounding rect
+ gfx::Rect boundingRect = CalculateBounds(newData.mRoundedClipRects,
+ newData.mAppUnitsPerDevPixel);
+ boundingRect.Scale(mParameters.mXScale, mParameters.mYScale);
+
+ uint32_t maxSize = mManager->GetMaxTextureSize();
+ NS_ASSERTION(maxSize > 0, "Invalid max texture size");
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ // Make mask image width aligned to 4. See Bug 1245552.
+ gfx::Size surfaceSize(std::min<gfx::Float>(GetAlignedStride<4>(NSToIntCeil(boundingRect.Width()), 1), maxSize),
+ std::min<gfx::Float>(boundingRect.Height(), maxSize));
+#else
+ gfx::Size surfaceSize(std::min<gfx::Float>(boundingRect.Width(), maxSize),
+ std::min<gfx::Float>(boundingRect.Height(), maxSize));
+#endif
+
+ // maskTransform is applied to the clip when it is painted into the mask (as a
+ // component of imageTransform), and its inverse used when the mask is used for
+ // masking.
+ // It is the transform from the masked layer's space to mask space
+ gfx::Matrix maskTransform =
+ Matrix::Scaling(surfaceSize.width / boundingRect.Width(),
+ surfaceSize.height / boundingRect.Height());
+ if (surfaceSize.IsEmpty()) {
+ // Return early if we know that the size of this mask surface is empty.
+ return nullptr;
+ }
+
+ gfx::Point p = boundingRect.TopLeft();
+ maskTransform.PreTranslate(-p.x, -p.y);
+ // imageTransform is only used when the clip is painted to the mask
+ gfx::Matrix imageTransform = maskTransform;
+ imageTransform.PreScale(mParameters.mXScale, mParameters.mYScale);
+
+ nsAutoPtr<MaskLayerImageCache::MaskLayerImageKey> newKey(
+ new MaskLayerImageCache::MaskLayerImageKey());
+
+ // copy and transform the rounded rects
+ for (uint32_t i = 0; i < newData.mRoundedClipRects.Length(); ++i) {
+ newKey->mRoundedClipRects.AppendElement(
+ MaskLayerImageCache::PixelRoundedRect(newData.mRoundedClipRects[i],
+ mContainerFrame->PresContext()));
+ newKey->mRoundedClipRects[i].ScaleAndTranslate(imageTransform);
+ }
+ newKey->mForwarder = mManager->AsShadowForwarder();
+
+ const MaskLayerImageCache::MaskLayerImageKey* lookupKey = newKey;
+
+ // check to see if we can reuse a mask image
+ RefPtr<ImageContainer> container =
+ GetMaskLayerImageCache()->FindImageFor(&lookupKey);
+
+ if (!container) {
+ IntSize surfaceSizeInt(NSToIntCeil(surfaceSize.width),
+ NSToIntCeil(surfaceSize.height));
+ // no existing mask image, so build a new one
+ MaskImageData imageData(surfaceSizeInt, mManager);
+ RefPtr<DrawTarget> dt = imageData.CreateDrawTarget();
+
+ // fail if we can't get the right surface
+ if (!dt || !dt->IsValid()) {
+ NS_WARNING("Could not create DrawTarget for mask layer.");
+ return nullptr;
+ }
+
+ RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
+ MOZ_ASSERT(context); // already checked the draw target above
+ context->Multiply(ThebesMatrix(imageTransform));
+
+ // paint the clipping rects with alpha to create the mask
+ aClip.FillIntersectionOfRoundedRectClips(context,
+ Color(1.f, 1.f, 1.f, 1.f),
+ newData.mAppUnitsPerDevPixel,
+ 0,
+ aRoundedRectClipCount);
+
+ // build the image and container
+ MOZ_ASSERT(aLayer->Manager() == mManager);
+ container = imageData.CreateImageAndImageContainer();
+ NS_ASSERTION(container, "Could not create image container for mask layer.");
+
+ if (!container) {
+ return nullptr;
+ }
+
+ GetMaskLayerImageCache()->PutImage(newKey.forget(), container);
+ }
+
+ maskLayer->SetContainer(container);
+
+ maskTransform.Invert();
+ Matrix4x4 matrix = Matrix4x4::From2D(maskTransform);
+ matrix.PreTranslate(mParameters.mOffset.x, mParameters.mOffset.y, 0);
+ maskLayer->SetBaseTransform(matrix);
+
+ // save the details of the clip in user data
+ *userData = Move(newData);
+ userData->mImageKey.Reset(lookupKey);
+
+ return maskLayer.forget();
+}
+
+} // namespace mozilla