Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FrameLayerBuilder.h"
#include <algorithm>
#include <deque>
#include <functional>
#include <utility>
#include "ActiveLayerTracker.h"
#include "BasicLayers.h"
#include "GeckoProfiler.h"
#include "ImageContainer.h"
#include "ImageLayers.h"
#include "LayerTreeInvalidation.h"
#include "LayerUserData.h"
#include "Layers.h"
#include "LayersLogging.h"
#include "MaskLayerImageCache.h"
#include "MatrixStack.h"
#include "UnitTransforms.h"
#include "Units.h"
#include "gfx2DGlue.h"
#include "gfxContext.h"
#include "gfxEnv.h"
#include "gfxUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/LayerAnimationInfo.h"
#include "mozilla/LayerTimelineMarker.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/Maybe.h"
#include "mozilla/PerfStats.h"
#include "mozilla/PresShell.h"
#include "mozilla/ReverseIterator.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layers.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/SVGIntegrationUtils.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/EffectsInfo.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
#include "mozilla/dom/RemoteBrowser.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Matrix.h"
#include "mozilla/gfx/Tools.h"
#include "mozilla/layers/ShadowLayers.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/TextureWrapperImage.h"
#include "mozilla/layers/WebRenderUserData.h"
#include "nsDisplayList.h"
#include "nsDocShell.h"
#include "nsIScrollableFrame.h"
#include "nsImageFrame.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h"
#include "nsPrintfCString.h"
#include "nsSubDocumentFrame.h"
#include "nsTransitionManager.h"
using namespace mozilla::layers;
using namespace mozilla::gfx;
using mozilla::UniquePtr;
using mozilla::WrapUnique;
// PaintedLayerData::mAssignedDisplayItems is a std::vector, which is
// non-memmovable
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(mozilla::PaintedLayerData);
namespace mozilla {
class PaintedDisplayItemLayerUserData;
static nsTHashtable<nsPtrHashKey<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;
}
struct DisplayItemEntry {
DisplayItemEntry(nsDisplayItem* aItem, DisplayItemEntryType aType)
: mItem(aItem), mType(aType) {}
nsDisplayItem* mItem;
DisplayItemEntryType mType;
};
/**
* Returns true if the given |aType| is an effect start marker.
*/
static bool IsEffectStartMarker(DisplayItemEntryType aType) {
return aType == DisplayItemEntryType::PushOpacity ||
aType == DisplayItemEntryType::PushOpacityWithBg ||
aType == DisplayItemEntryType::PushTransform;
}
/**
* Returns true if the given |aType| is an effect end marker.
*/
static bool IsEffectEndMarker(DisplayItemEntryType aType) {
return aType == DisplayItemEntryType::PopOpacity ||
aType == DisplayItemEntryType::PopTransform;
}
enum class MarkerType { StartMarker, EndMarker };
/**
* Returns true if the given nsDisplayOpacity |aItem| has had opacity applied
* to its children and can be flattened away.
*/
static bool IsOpacityAppliedToChildren(nsDisplayItem* aItem) {
MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_OPACITY);
return static_cast<nsDisplayOpacity*>(aItem)->OpacityAppliedToChildren();
}
/**
* Returns true if the given display item type supports flattening with markers.
*/
static bool SupportsFlatteningWithMarkers(const DisplayItemType& aType) {
return aType == DisplayItemType::TYPE_OPACITY ||
aType == DisplayItemType::TYPE_TRANSFORM;
}
/**
* Adds the effect marker to |aMarkers| based on the type of |aItem| and whether
* |markerType| is a start or end marker.
*/
template <MarkerType markerType>
static bool AddMarkerIfNeeded(nsDisplayItem* aItem,
std::deque<DisplayItemEntry>& aMarkers) {
const DisplayItemType type = aItem->GetType();
if (!SupportsFlatteningWithMarkers(type)) {
return false;
}
DisplayItemEntryType marker;
// Just a fancy way to avoid writing two separate functions to select between
// PUSH and POP markers. This is done during compile time based on |markerType|.
#define GET_MARKER(start_marker, end_marker) \
std::conditional< \
markerType == MarkerType::StartMarker, \
std::integral_constant<DisplayItemEntryType, start_marker>, \
std::integral_constant<DisplayItemEntryType, end_marker>>::type::value;
switch (type) {
case DisplayItemType::TYPE_OPACITY:
if (IsOpacityAppliedToChildren(aItem)) {
// TODO(miko): I am not a fan of this. The more correct solution would
// be to return an enum from nsDisplayItem::ShouldFlattenAway(), so that
// we could distinguish between different flattening methods and avoid
// entering this function when markers are not needed.
return false;
}
marker = GET_MARKER(DisplayItemEntryType::PushOpacity,
DisplayItemEntryType::PopOpacity);
break;
case DisplayItemType::TYPE_TRANSFORM:
marker = GET_MARKER(DisplayItemEntryType::PushTransform,
DisplayItemEntryType::PopTransform);
break;
default:
MOZ_ASSERT_UNREACHABLE("Invalid display item type!");
break;
}
aMarkers.emplace_back(aItem, marker);
return true;
}
DisplayItemData::DisplayItemData(LayerManagerData* aParent, uint32_t aKey,
Layer* aLayer, nsIFrame* aFrame)
: mRefCnt(0),
mParent(aParent),
mLayer(aLayer),
mDisplayItemKey(aKey),
mItem(nullptr),
mUsed(true),
mIsInvalid(false),
mReusedItem(false) {
MOZ_COUNT_CTOR(DisplayItemData);
if (!sAliveDisplayItemDatas) {
sAliveDisplayItemDatas = new nsTHashtable<nsPtrHashKey<DisplayItemData>>();
}
MOZ_RELEASE_ASSERT(!sAliveDisplayItemDatas->Contains(this));
sAliveDisplayItemDatas->PutEntry(this);
MOZ_RELEASE_ASSERT(mLayer);
if (aFrame) {
AddFrame(aFrame);
}
}
void DisplayItemData::AddFrame(nsIFrame* aFrame) {
MOZ_RELEASE_ASSERT(mLayer);
MOZ_RELEASE_ASSERT(!mFrameList.Contains(aFrame));
mFrameList.AppendElement(aFrame);
SmallPointerArray<DisplayItemData>& array = aFrame->DisplayItemData();
array.AppendElement(this);
}
void 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!");
SmallPointerArray<DisplayItemData>& array = aFrame->DisplayItemData();
array.RemoveElement(this);
}
void DisplayItemData::EndUpdate() {
MOZ_RELEASE_ASSERT(mLayer);
mIsInvalid = false;
mUsed = false;
mReusedItem = false;
mOldTransform = nullptr;
}
void DisplayItemData::EndUpdate(UniquePtr<nsDisplayItemGeometry>&& aGeometry) {
MOZ_RELEASE_ASSERT(mLayer);
MOZ_ASSERT(mItem);
MOZ_ASSERT(mGeometry || aGeometry);
if (aGeometry) {
mGeometry = std::move(aGeometry);
}
mClip = mItem->GetClip();
mChangedFrameInvalidations.SetEmpty();
EndUpdate();
}
void DisplayItemData::BeginUpdate(Layer* aLayer, LayerState aState,
bool aFirstUpdate,
nsPaintedDisplayItem* aItem /* = nullptr */) {
bool isReused = false;
bool isMerged = false;
if (aItem) {
isReused = !aFirstUpdate ? aItem->IsReused() : false;
const nsDisplayWrapList* wraplist = aItem->AsDisplayWrapList();
isMerged = wraplist && wraplist->HasMergedFrames();
}
BeginUpdate(aLayer, aState, aItem, isReused, isMerged);
}
void DisplayItemData::BeginUpdate(Layer* aLayer, LayerState aState,
nsPaintedDisplayItem* aItem, bool aIsReused,
bool aIsMerged) {
MOZ_RELEASE_ASSERT(mLayer);
MOZ_RELEASE_ASSERT(aLayer);
mLayer = aLayer;
mOptLayer = nullptr;
mInactiveManager = nullptr;
mLayerState = aState;
mUsed = true;
if (aLayer->AsPaintedLayer()) {
if (aItem != mItem) {
aItem->SetDisplayItemData(this, aLayer->Manager());
} else {
MOZ_ASSERT(aItem->GetDisplayItemData() == this);
}
mReusedItem = aIsReused;
}
if (!aItem) {
return;
}
if (!aIsMerged && mFrameList.Length() == 1) {
MOZ_ASSERT(mFrameList[0] == aItem->Frame());
return;
}
// We avoid adding or removing element unnecessarily
// since we have to modify userdata each time
CopyableAutoTArray<nsIFrame*, 4> copy(mFrameList);
if (!copy.RemoveElement(aItem->Frame())) {
AddFrame(aItem->Frame());
mChangedFrameInvalidations.Or(mChangedFrameInvalidations,
aItem->Frame()->GetVisualOverflowRect());
}
if (aIsMerged) {
MOZ_ASSERT(aItem->AsDisplayWrapList());
for (nsIFrame* frame : aItem->AsDisplayWrapList()->GetMergedFrames()) {
if (!copy.RemoveElement(frame)) {
AddFrame(frame);
mChangedFrameInvalidations.Or(mChangedFrameInvalidations,
frame->GetVisualOverflowRect());
}
}
}
for (nsIFrame* frame : copy) {
RemoveFrame(frame);
mChangedFrameInvalidations.Or(mChangedFrameInvalidations,
frame->GetVisualOverflowRect());
}
}
static const nsIFrame* sDestroyedFrame = nullptr;
DisplayItemData::~DisplayItemData() {
MOZ_COUNT_DTOR(DisplayItemData);
if (mItem) {
MOZ_ASSERT(mItem->GetDisplayItemData() == this);
mItem->SetDisplayItemData(nullptr, nullptr);
}
for (uint32_t i = 0; i < mFrameList.Length(); i++) {
nsIFrame* frame = mFrameList[i];
if (frame == sDestroyedFrame) {
continue;
}
SmallPointerArray<DisplayItemData>& array = frame->DisplayItemData();
array.RemoveElement(this);
}
MOZ_RELEASE_ASSERT(sAliveDisplayItemDatas);
nsPtrHashKey<mozilla::DisplayItemData>* entry =
sAliveDisplayItemDatas->GetEntry(this);
MOZ_RELEASE_ASSERT(entry);
sAliveDisplayItemDatas->RemoveEntry(entry);
if (sAliveDisplayItemDatas->Count() == 0) {
delete sAliveDisplayItemDatas;
sAliveDisplayItemDatas = nullptr;
}
}
void DisplayItemData::NotifyRemoved() {
if (mDisplayItemKey > static_cast<uint8_t>(DisplayItemType::TYPE_MAX)) {
// This is sort of a hack. The display item key has higher bits set, which
// means that it is not the only display item for the frame.
// This branch skips separator transforms.
return;
}
const DisplayItemType type = GetDisplayItemTypeFromKey(mDisplayItemKey);
if (type == DisplayItemType::TYPE_REMOTE) {
// TYPE_REMOTE doesn't support merging, so access it directly
MOZ_ASSERT(mFrameList.Length() == 1);
if (mFrameList.Length() != 1) {
return;
}
// This is a remote browser that is going away, notify it that it is now
// hidden
nsIFrame* frame = mFrameList[0];
nsSubDocumentFrame* subdoc = static_cast<nsSubDocumentFrame*>(frame);
nsFrameLoader* frameLoader = subdoc->FrameLoader();
if (frameLoader && frameLoader->GetRemoteBrowser()) {
frameLoader->GetRemoteBrowser()->UpdateEffects(
mozilla::dom::EffectsInfo::FullyHidden());
}
}
if (type != DisplayItemType::TYPE_TRANSFORM &&
type != DisplayItemType::TYPE_OPACITY &&
type != DisplayItemType::TYPE_BACKGROUND_COLOR) {
return;
}
for (nsIFrame* frame : mFrameList) {
EffectCompositor::ClearIsRunningOnCompositor(frame, type);
}
}
const nsRegion& DisplayItemData::GetChangedFrameInvalidations() {
return mChangedFrameInvalidations;
}
DisplayItemData* DisplayItemData::AssertDisplayItemData(
DisplayItemData* aData) {
MOZ_RELEASE_ASSERT(aData);
MOZ_RELEASE_ASSERT(sAliveDisplayItemDatas &&
sAliveDisplayItemDatas->Contains(aData));
MOZ_RELEASE_ASSERT(aData->mLayer);
return aData;
}
/**
* 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() override { MOZ_COUNT_DTOR(LayerManagerData); }
#ifdef DEBUG_DISPLAY_ITEM_DATA
void Dump(const char* aPrefix = "") {
printf_stderr("%sLayerManagerData %p\n", aPrefix, this);
for (auto& data : mDisplayItems) {
nsAutoCString prefix;
prefix += aPrefix;
prefix += " ";
const char* layerState;
switch (data->mLayerState) {
case LayerState::LAYER_NONE:
layerState = "LAYER_NONE";
break;
case LayerState::LAYER_INACTIVE:
layerState = "LAYER_INACTIVE";
break;
case LayerState::LAYER_ACTIVE:
layerState = "LAYER_ACTIVE";
break;
case LayerState::LAYER_ACTIVE_FORCE:
layerState = "LAYER_ACTIVE_FORCE";
break;
case LayerState::LAYER_ACTIVE_EMPTY:
layerState = "LAYER_ACTIVE_EMPTY";
break;
case LayerState::LAYER_SVG_EFFECTS:
layerState = "LAYER_SVG_EFFECTS";
break;
}
uint32_t mask = (1 << 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 >> TYPE_BITS)) {
str += nsPrintfCString("(%i)", data->mDisplayItemKey >> 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
std::vector<RefPtr<DisplayItemData>> mDisplayItems;
bool mInvalidateAllLayers;
};
/* static */
void FrameLayerBuilder::DestroyDisplayItemDataFor(nsIFrame* aFrame) {
RemoveFrameFromLayerManager(aFrame, aFrame->DisplayItemData());
aFrame->DisplayItemData().Clear();
// Destroying a WebRenderUserDataTable can cause destruction of other objects
// which can remove frame properties in their destructor. If we delete a frame
// property it runs the destructor of the stored object in the middle of
// updating the frame property table, so if the destruction of that object
// causes another update to the frame property table it would leave the frame
// property table in an inconsistent state. So we remove it from the table and
// then destroy it. (bug 1530657)
WebRenderUserDataTable* userDataTable =
aFrame->TakeProperty(WebRenderUserDataProperty::Key());
if (userDataTable) {
for (auto iter = userDataTable->Iter(); !iter.Done(); iter.Next()) {
iter.UserData()->RemoveFromTable();
}
delete userDataTable;
}
}
/**
* 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),
mASR(nullptr),
mClipChain(nullptr),
mReferenceFrame(nullptr),
mLayer(nullptr),
mSolidColor(NS_RGBA(0, 0, 0, 0)),
mIsSolidColorInVisibleRegion(false),
mNeedComponentAlpha(false),
mForceTransparentSurface(false),
mHideAllLayersBelow(false),
mOpaqueForAnimatedGeometryRootParent(false),
mBackfaceHidden(false),
mDTCRequiresTargetConfirmation(false),
mImage(nullptr),
mItemClip(nullptr),
mNewChildLayersIndex(-1)
#ifdef DEBUG
,
mTransformLevel(0)
#endif
{
}
PaintedLayerData(PaintedLayerData&& aRhs) = default;
~PaintedLayerData() { MOZ_ASSERT(mTransformLevel == 0); }
#ifdef MOZ_DUMP_PAINTING
/**
* Keep track of important decisions for debugging.
*/
nsCString mLog;
# define FLB_LOG_PAINTED_LAYER_DECISION(pld, ...) \
if (StaticPrefs::layers_dump_decision()) { \
pld->mLog.AppendPrintf("\t\t\t\t"); \
pld->mLog.AppendPrintf(__VA_ARGS__); \
}
#else
# define FLB_LOG_PAINTED_LAYER_DECISION(...)
#endif
/**
* Disables component alpha for |aItem| if the component alpha bounds are not
* contained in |mOpaqueRegion|. Alternatively if possible, sets
* |mNeedComponentAlpha| to true for this PaintedLayerData.
*/
bool SetupComponentAlpha(ContainerState* aState, nsPaintedDisplayItem* aItem,
const nsIntRect& aVisibleRect,
const TransformClipNode* aTransform);
/**
* 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
*/
void Accumulate(ContainerState* aState, nsPaintedDisplayItem* aItem,
const nsIntRect& aVisibleRect, const nsRect& aContentRect,
const DisplayItemClip& aClip, LayerState aLayerState,
nsDisplayList* aList, DisplayItemEntryType aType,
nsTArray<size_t>& aOpacityIndices,
const RefPtr<TransformClipNode>& aTransform);
UniquePtr<InactiveLayerData> CreateInactiveLayerData(
ContainerState* aState, nsPaintedDisplayItem* aItem,
DisplayItemData* aData);
/**
* Updates the status of |mTransform| and |aOpacityIndices|, based on |aType|.
*/
void UpdateEffectStatus(DisplayItemEntryType aType,
nsTArray<size_t>& aOpacityIndices);
AnimatedGeometryRoot* GetAnimatedGeometryRoot() {
return mAnimatedGeometryRoot;
}
/**
* A region including the horizontal pan, vertical pan, and no action regions.
*/
nsRegion CombinedTouchActionRegion();
/**
* Add the given hit test info to the hit regions for this PaintedLayer.
*/
void AccumulateHitTestItem(ContainerState* aState, nsDisplayItem* aItem,
const DisplayItemClip& aClip,
TransformClipNode* aTransform);
void HitRegionsUpdated();
/**
* 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 owning ContainerState that created this PaintedLayerData.
*/
ContainerState* mState;
/**
* 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;
bool mCollapsedTouchActions = false;
/**
* 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;
const ActiveScrolledRoot* mASR;
/**
* The chain of clips that should apply to this layer.
*/
const DisplayItemClipChain* mClipChain;
/**
* 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;
/**
* 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 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;
/**
* Set to true if events targeting the dispatch-to-content region
* require target confirmation.
* See CompositorHitTestFlags::eRequiresTargetConfirmation and
* EventRegions::mDTCRequiresTargetConfirmation.
*/
bool mDTCRequiresTargetConfirmation;
/**
* 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.
*/
const DisplayItemClip* mItemClip;
/**
* Index of this layer in mNewChildLayers.
*/
int32_t mNewChildLayersIndex;
/**
* 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().
*/
std::vector<AssignedDisplayItem> mAssignedDisplayItems;
#ifdef DEBUG
/**
* Tracks the level of transform to ensure balanced PUSH/POP markers.
*/
int mTransformLevel;
#endif
};
struct NewLayerEntry {
NewLayerEntry()
: mAnimatedGeometryRoot(nullptr),
mASR(nullptr),
mClipChain(nullptr),
mScrollMetadataASR(nullptr),
mLayerContentsVisibleRect(0, 0, -1, -1),
mLayerState(LayerState::LAYER_INACTIVE),
mHideAllLayersBelow(false),
mOpaqueForAnimatedGeometryRootParent(false),
mUntransformedVisibleRegion(false),
mIsFixedToRootScrollFrame(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 ActiveScrolledRoot* mASR;
const DisplayItemClipChain* mClipChain;
const ActiveScrolledRoot* mScrollMetadataASR;
// 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;
// mVisibleRegion is relative to the associated frame before
// transform.
bool mUntransformedVisibleRegion;
bool mIsFixedToRootScrollFrame;
};
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 ActiveScrolledRoot* aASR, const DisplayItemClipChain* aClipChain,
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 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.
*/
AutoTArray<PaintedLayerData, 3> 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),
mForInactiveLayer(false) {}
~PaintedLayerDataTree() {
MOZ_ASSERT(!mRoot);
MOZ_ASSERT(mNodes.Count() == 0);
}
void InitializeForInactiveLayer(AnimatedGeometryRoot* aAnimatedGeometryRoot);
/**
* 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 ActiveScrolledRoot* aASR, const DisplayItemClipChain* aClipChain,
const nsIntRect& aVisibleRect, const bool aBackfaceHidden,
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;
Maybe<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;
bool mForInactiveLayer;
};
/**
* 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,
nscolor aBackgroundColor,
const ActiveScrolledRoot* aContainerASR,
const ActiveScrolledRoot* aContainerScrollMetadataASR,
const ActiveScrolledRoot* aContainerCompositorASR)
: mBuilder(aBuilder),
mManager(aManager),
mLayerBuilder(aLayerBuilder),
mContainerFrame(aContainerFrame),
mContainerLayer(aContainerLayer),
mContainerBounds(aContainerBounds),
mContainerASR(aContainerASR),
mContainerScrollMetadataASR(aContainerScrollMetadataASR),
mContainerCompositorASR(aContainerCompositorASR),
mParameters(aParameters),
mPaintedLayerDataTree(*this, aBackgroundColor),
mLastDisplayPortAGR(nullptr),
mContainerItem(aContainerItem) {
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(!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);
nscoord GetAppUnitsPerDevPixel() { return mAppUnitsPerDevPixel; }
nsIntRect ScaleToNearestPixels(const nsRect& aRect) const {
return aRect.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale,
mAppUnitsPerDevPixel);
}
nsIntRect ScaleToOutsidePixels(const nsRect& aRect,
bool aSnap = false) const {
if (aRect.IsEmpty()) {
return nsIntRect();
}
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 ScaleRegionToNearestPixels(const nsRegion& aRegion) const {
return aRegion.ScaleToNearestPixels(
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 (aRegion.IsEmpty()) {
return nsIntRegion();
}
if (aSnap && mSnappingEnabled) {
return ScaleRegionToNearestPixels(aRegion);
}
return aRegion.ScaleToOutsidePixels(
mParameters.mXScale, mParameters.mYScale, mAppUnitsPerDevPixel);
}
nsIFrame* GetContainerFrame() const { return mContainerFrame; }
nsDisplayListBuilder* Builder() const { return mBuilder; }
FrameLayerBuilder* LayerBuilder() const { return mLayerBuilder; }
/**
* 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;
friend class FLBDisplayListIterator;
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, const nsIFrame* aReferenceFrame);
/**
* 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;
template <typename UserData>
already_AddRefed<ImageLayer> CreateOrRecycleMaskImageLayerFor(
const MaskLayerKey& aKey, UserData* (*aGetUserData)(Layer* aLayer),
void (*aSetDefaultUserData)(Layer* aLayer));
/**
* 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,
DisplayItemData* aData);
/**
* 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 ScrollMetadata 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 ActiveScrolledRoot* aASR,
const DisplayItemClip& aClip,
nsDisplayList* aList, bool* aHideAllLayersBelow,
bool* aOpaqueForAnimatedGeometryRootParent);
/**
* Fills a PaintedLayerData object that is initialized for a layer that the
* current item will be assigned to. Also creates mNewChildLayers entries.
* @param aData The PaintedLayerData that will be filled.
* @param aVisibleRect The visible rect of the item.
* @param aAnimatedGeometryRoot The item's animated geometry root.
* @param aASR The active scrolled root that moves this
* PaintedLayer.
* @param aClipChain The clip chain that the compositor needs to
* apply to this layer.
* @param aScrollMetadataASR The leaf ASR for which scroll metadata needs
* to be set on the layer, because either the layer itself or its scrolled
* clip need to move with that ASR.
* @param aTopLeft The offset between aAnimatedGeometryRoot and
* the reference frame.
* @param aReferenceFrame The reference frame for the item.
* @param aBackfaceHidden The backface visibility for the item frame.
*/
void NewPaintedLayerData(
PaintedLayerData* aData, AnimatedGeometryRoot* aAnimatedGeometryRoot,
const ActiveScrolledRoot* aASR, const DisplayItemClipChain* aClipChain,
const ActiveScrolledRoot* aScrollMetadataASR, const nsPoint& aTopLeft,
const nsIFrame* aReferenceFrame, const bool aBackfaceHidden);
/* 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,
*/
void SetupMaskLayer(Layer* aLayer, const DisplayItemClip& aClip);
/**
* 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,
nsDisplayMasksAndClipPaths* aMaskItem);
already_AddRefed<Layer> CreateMaskLayer(
Layer* aLayer, const DisplayItemClip& aClip,
const Maybe<size_t>& aForAncestorMaskLayer);
/**
* Get the display port for an AGR.
* The result would be cached for later reusing.
*/
nsRect GetDisplayPortForAnimatedGeometryRoot(
AnimatedGeometryRoot* aAnimatedGeometryRoot);
nsDisplayListBuilder* mBuilder;
LayerManager* mManager;
FrameLayerBuilder* mLayerBuilder;
nsIFrame* mContainerFrame;
nsIFrame* mContainerReferenceFrame;
AnimatedGeometryRoot* mContainerAnimatedGeometryRoot;
ContainerLayer* mContainerLayer;
nsRect mContainerBounds;
// Due to the way we store scroll annotations in the layer tree, we need to
// keep track of three (possibly different) ASRs here.
// mContainerASR is the ASR of the container display item that this
// ContainerState was created for.
// mContainerScrollMetadataASR is the ASR of the leafmost scroll metadata
// that's in effect on mContainerLayer.
// mContainerCompositorASR is the ASR that mContainerLayer moves with on
// the compositor / APZ side, taking into account both the scroll meta data
// and the fixed position annotation on itself and its ancestors.
const ActiveScrolledRoot* mContainerASR;
const ActiveScrolledRoot* mContainerScrollMetadataASR;
const ActiveScrolledRoot* mContainerCompositorASR;
#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;
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;
// Keep display port of AGR to avoid wasting time on doing the same
// thing repeatly.
AnimatedGeometryRoot* mLastDisplayPortAGR;
nsRect mLastDisplayPortRect;
nsDisplayItem* mContainerItem;
// Cache ScrollMetadata so it doesn't need recomputed if the ASR and clip are
// unchanged. If mASR == nullptr then mMetadata is not valid.
struct CachedScrollMetadata {
const ActiveScrolledRoot* mASR;
const DisplayItemClip* mClip;
Maybe<ScrollMetadata> mMetadata;
CachedScrollMetadata() : mASR(nullptr), mClip(nullptr) {}
};
CachedScrollMetadata mCachedScrollMetadata;
};
class FLBDisplayListIterator : public FlattenedDisplayListIterator {
public:
FLBDisplayListIterator(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
ContainerState* aState)
: FlattenedDisplayListIterator(aBuilder, aList, false), mState(aState) {
MOZ_ASSERT(mState);
if (mState->mContainerItem) {
// Add container item hit test information for processing, if needed.
AddHitTestMarkerIfNeeded(mState->mContainerItem);
}
ResolveFlattening();
}
DisplayItemEntry GetNextEntry() {
if (!mMarkers.empty()) {
DisplayItemEntry entry = mMarkers.front();
mMarkers.pop_front();
return entry;
}
return DisplayItemEntry{GetNextItem(), DisplayItemEntryType::Item};
}
bool HasNext() const override {
return FlattenedDisplayListIterator::HasNext() || !mMarkers.empty();
}
private:
void AddHitTestMarkerIfNeeded(nsDisplayItem* aItem) {
if (aItem->HasHitTestInfo()) {
mMarkers.emplace_back(aItem, DisplayItemEntryType::HitTestInfo);
}
}
bool ShouldFlattenNextItem() override {
if (!FlattenedDisplayListIterator::ShouldFlattenNextItem()) {
return false;
}
nsDisplayItem* next = PeekNext();
const DisplayItemType type = next->GetType();
if (type == DisplayItemType::TYPE_SVG_WRAPPER) {
// We mark SetContainsSVG for the CONTENT_FRAME_TIME_WITH_SVG metric
if (RefPtr<LayerManager> lm = mState->mBuilder->GetWidgetLayerManager()) {
lm->SetContainsSVG(true);
}
}
if (!SupportsFlatteningWithMarkers(type)) {
return true;
}
if (type == DisplayItemType::TYPE_OPACITY &&
IsOpacityAppliedToChildren(next)) {
// This is the previous opacity flattening path, where the opacity has
// been applied to children.
return true;
}
if (mState->IsInInactiveLayer() || !ItemWantsInactiveLayer(next)) {
// Do not flatten nested inactive display items, or display items that
// want an active layer.
return false;
}
// If we reach here, we will emit an effect start marker for
// nsDisplayTransform or nsDisplayOpacity.
MOZ_ASSERT(type == DisplayItemType::TYPE_TRANSFORM ||
!IsOpacityAppliedToChildren(next));
return true;
}
void EnterChildList(nsDisplayItem* aContainerItem) override {
mFlattenedLists.AppendElement(aContainerItem);
AddMarkerIfNeeded<MarkerType::StartMarker>(aContainerItem, mMarkers);
AddHitTestMarkerIfNeeded(aContainerItem);
}
void ExitChildList() override {
MOZ_ASSERT(!mFlattenedLists.IsEmpty());
nsDisplayItem* aContainerItem = mFlattenedLists.PopLastElement();
AddMarkerIfNeeded<MarkerType::EndMarker>(aContainerItem, mMarkers);
}
bool ItemWantsInactiveLayer(nsDisplayItem* aItem) {
const LayerState layerState = aItem->GetLayerState(
mState->mBuilder, mState->mManager, mState->mParameters);
return layerState == LayerState::LAYER_INACTIVE;
}
std::deque<DisplayItemEntry> mMarkers;
AutoTArray<nsDisplayItem*, 16> mFlattenedLists;
ContainerState* mState;
};
class PaintedDisplayItemLayerUserData : public LayerUserData {
public:
PaintedDisplayItemLayerUserData()
: mForcedBackgroundColor(NS_RGBA(0, 0, 0, 0)),
mXScale(1.f),
mYScale(1.f),
mAppUnitsPerDevPixel(0),
mTranslation(0, 0),
mAnimatedGeometryRootPosition(0, 0),
mLastItemCount(0),
mContainerLayerFrame(nullptr),
mDisabledAlpha(false) {}
NS_INLINE_DECL_REFCOUNTING(PaintedDisplayItemLayerUserData);
/**
* A color that should be painted over the bounds of the layer's visible
* region before any other content is painted.
*/
nscolor mForcedBackgroundColor;
/**
* 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;
// The area for which we called RecomputeVisibilityForItems on the
// previous paint.
nsRect mPreviousRecomputeVisibilityRect;
// The number of items assigned to this layer on the previous paint.
size_t mLastItemCount;
// The translation set on this PaintedLayer during the previous paint. This
// is needed when invalidating based on a display item's geometry information
// from the previous paint.
Maybe<nsIntPoint> mLastPaintOffset;
// Temporary state only valid during the FrameLayerBuilder's lifetime.
// FLB's mPaintedLayerItems is responsible for cleaning these up when
// we finish painting to avoid dangling pointers.
std::vector<AssignedDisplayItem> mItems;
nsIFrame* mContainerLayerFrame;
/**
* This is set when the painted layer has no component alpha.
*/
bool mDisabledAlpha;
protected:
~PaintedDisplayItemLayerUserData() override = default;
};
FrameLayerBuilder::FrameLayerBuilder()
: mRetainingManager(nullptr),
mDisplayListBuilder(nullptr),
mContainingPaintedLayer(nullptr),
mInactiveLayerClip(nullptr),
mInvalidateAllLayers(false),
mInLayerTreeCompressionMode(false),
mIsInactiveLayerManager(false) {
MOZ_COUNT_CTOR(FrameLayerBuilder);
}
FrameLayerBuilder::~FrameLayerBuilder() {
GetMaskLayerImageCache()->Sweep();
for (PaintedDisplayItemLayerUserData* userData : mPaintedLayerItems) {
userData->mLastPaintOffset = Some(userData->mTranslation);
userData->mItems.clear();
userData->mContainerLayerFrame = nullptr;
}
MOZ_COUNT_DTOR(FrameLayerBuilder);
}
void FrameLayerBuilder::AddPaintedLayerItemsEntry(
PaintedDisplayItemLayerUserData* aData) {
mPaintedLayerItems.AppendElement(aData);
}
/*
* 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, int32_t aAppUnitsPerDevPixel,
const ContainerLayerParameters& aParams)
: mScaleX(aParams.mXScale),
mScaleY(aParams.mYScale),
mOffset(aParams.mOffset),
mAppUnitsPerDevPixel(aAppUnitsPerDevPixel) {
aClip.AppendRoundedRects(&mRoundedClipRects);
}
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() : mMaskStyle(nsStyleImageLayers::LayerType::Mask) {}
CSSMaskLayerUserData(nsIFrame* aFrame, const nsIntRect& aMaskBounds,
const nsPoint& aMaskLayerOffset)
: mMaskBounds(aMaskBounds),
mMaskStyle(aFrame->StyleSVGReset()->mMask),
mMaskLayerOffset(aMaskLayerOffset) {}
void operator=(CSSMaskLayerUserData&& aOther) {
mMaskBounds = aOther.mMaskBounds;
mMaskStyle = std::move(aOther.mMaskStyle);
mMaskLayerOffset = aOther.mMaskLayerOffset;
}
bool operator==(const CSSMaskLayerUserData& aOther) const {
if (!mMaskBounds.IsEqualInterior(aOther.mMaskBounds)) {
return false;
}
// Make sure we draw the same portion of the mask onto mask layer.
if (mMaskLayerOffset != aOther.mMaskLayerOffset) {
return false;
}
return mMaskStyle == aOther.mMaskStyle;
}
private:
nsIntRect mMaskBounds;
nsStyleImageLayers mMaskStyle;
nsPoint mMaskLayerOffset; // The offset from the origin of mask bounds to
// the origin of mask layer.
};
/*
* 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);