1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "FrameLayerBuilder.h"
14 #include "ActiveLayerTracker.h"
15 #include "BasicLayers.h"
16 #include "ImageContainer.h"
17 #include "ImageLayers.h"
18 #include "LayerTreeInvalidation.h"
19 #include "LayerUserData.h"
21 #include "MaskLayerImageCache.h"
22 #include "MatrixStack.h"
23 #include "TransformClipNode.h"
24 #include "UnitTransforms.h"
26 #include "gfx2DGlue.h"
27 #include "gfxContext.h"
30 #include "mozilla/DebugOnly.h"
31 #include "mozilla/DisplayPortUtils.h"
32 #include "mozilla/EffectCompositor.h"
33 #include "mozilla/LayerAnimationInfo.h"
34 #include "mozilla/LayerTimelineMarker.h"
35 #include "mozilla/LookAndFeel.h"
36 #include "mozilla/Maybe.h"
37 #include "mozilla/PerfStats.h"
38 #include "mozilla/PresShell.h"
39 #include "mozilla/ProfilerLabels.h"
40 #include "mozilla/ReverseIterator.h"
41 #include "mozilla/StaticPrefs_gfx.h"
42 #include "mozilla/StaticPrefs_layers.h"
43 #include "mozilla/StaticPrefs_layout.h"
44 #include "mozilla/SVGIntegrationUtils.h"
45 #include "mozilla/Unused.h"
46 #include "mozilla/dom/EffectsInfo.h"
47 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
48 #include "mozilla/dom/RemoteBrowser.h"
49 #include "mozilla/gfx/2D.h"
50 #include "mozilla/gfx/Matrix.h"
51 #include "mozilla/gfx/Tools.h"
52 #include "mozilla/layers/ShadowLayers.h"
53 #include "mozilla/layers/TextureClient.h"
54 #include "mozilla/layers/TextureWrapperImage.h"
55 #include "mozilla/layers/WebRenderUserData.h"
56 #include "nsDisplayList.h"
57 #include "nsDocShell.h"
58 #include "nsIScrollableFrame.h"
59 #include "nsImageFrame.h"
60 #include "nsLayoutUtils.h"
61 #include "nsPresContext.h"
62 #include "nsPrintfCString.h"
63 #include "nsSubDocumentFrame.h"
64 #include "nsTransitionManager.h"
65 #include "nsTHashMap.h"
67 using namespace mozilla::layers
;
68 using namespace mozilla::gfx
;
69 using mozilla::UniquePtr
;
70 using mozilla::WrapUnique
;
72 // PaintedLayerData::mAssignedDisplayItems is a std::vector, which is
74 MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(mozilla::PaintedLayerData
);
78 class PaintedDisplayItemLayerUserData
;
80 static nsTHashSet
<DisplayItemData
*>* sAliveDisplayItemDatas
;
82 // DO NOT MODIFY THE ARRAY RETURNED BY THIS FUNCTION.
83 // It might be the static empty array.
84 SmallPointerArray
<DisplayItemData
>& GetDisplayItemDataArray(
85 const nsIFrame
* aFrame
) {
86 static SmallPointerArray
<DisplayItemData
> sEmpty
;
87 MOZ_ASSERT(sEmpty
.IsEmpty());
88 auto* array
= aFrame
->DisplayItemData();
89 return array
? *array
: sEmpty
;
93 * The address of gPaintedDisplayItemLayerUserData is used as the user
94 * data key for PaintedLayers created by FrameLayerBuilder.
95 * It identifies PaintedLayers used to draw non-layer content, which are
96 * therefore eligible for recycling. We want display items to be able to
97 * create their own dedicated PaintedLayers in BuildLayer, if necessary,
98 * and we wouldn't want to accidentally recycle those.
99 * The user data is a PaintedDisplayItemLayerUserData.
101 uint8_t gPaintedDisplayItemLayerUserData
;
103 * The address of gColorLayerUserData is used as the user
104 * data key for ColorLayers created by FrameLayerBuilder.
105 * The user data is null.
107 uint8_t gColorLayerUserData
;
109 * The address of gImageLayerUserData is used as the user
110 * data key for ImageLayers created by FrameLayerBuilder.
111 * The user data is null.
113 uint8_t gImageLayerUserData
;
115 * The address of gLayerManagerUserData is used as the user
116 * data key for retained LayerManagers managed by FrameLayerBuilder.
117 * The user data is a LayerManagerData.
119 uint8_t gLayerManagerUserData
;
121 * The address of gMaskLayerUserData is used as the user
122 * data key for mask layers managed by FrameLayerBuilder.
123 * The user data is a MaskLayerUserData.
125 uint8_t gMaskLayerUserData
;
127 * The address of gCSSMaskLayerUserData is used as the user
128 * data key for mask layers of css masking managed by FrameLayerBuilder.
129 * The user data is a CSSMaskLayerUserData.
131 uint8_t gCSSMaskLayerUserData
;
133 // a global cache of image containers used for mask layers
134 static MaskLayerImageCache
* gMaskLayerImageCache
= nullptr;
136 static inline MaskLayerImageCache
* GetMaskLayerImageCache() {
137 if (!gMaskLayerImageCache
) {
138 gMaskLayerImageCache
= new MaskLayerImageCache();
141 return gMaskLayerImageCache
;
144 struct InactiveLayerData
{
145 RefPtr
<layers::BasicLayerManager
> mLayerManager
;
146 RefPtr
<layers::Layer
> mLayer
;
147 UniquePtr
<layers::LayerProperties
> mProps
;
149 ~InactiveLayerData();
152 struct AssignedDisplayItem
{
153 AssignedDisplayItem(nsPaintedDisplayItem
* aItem
, LayerState aLayerState
,
154 DisplayItemData
* aData
, const nsRect
& aContentRect
,
155 DisplayItemEntryType aType
, const bool aHasOpacity
,
156 const RefPtr
<TransformClipNode
>& aTransform
,
157 const bool aIsMerged
);
158 AssignedDisplayItem(AssignedDisplayItem
&& aRhs
) = default;
160 bool HasOpacity() const { return mHasOpacity
; }
162 bool HasTransform() const { return mTransform
; }
164 nsPaintedDisplayItem
* mItem
;
165 DisplayItemData
* mDisplayItemData
;
168 * If the display item is being rendered as an inactive
169 * layer, then this stores the layer manager being
170 * used for the inactive transaction.
172 UniquePtr
<InactiveLayerData
> mInactiveLayerData
;
173 RefPtr
<TransformClipNode
> mTransform
;
176 LayerState mLayerState
;
177 DisplayItemEntryType mType
;
185 struct DisplayItemEntry
{
186 DisplayItemEntry(nsDisplayItem
* aItem
, DisplayItemEntryType aType
)
187 : mItem(aItem
), mType(aType
) {}
189 nsDisplayItem
* mItem
;
190 DisplayItemEntryType mType
;
194 * Returns true if the given |aType| is an effect start marker.
196 static bool IsEffectStartMarker(DisplayItemEntryType aType
) {
197 return aType
== DisplayItemEntryType::PushOpacity
||
198 aType
== DisplayItemEntryType::PushOpacityWithBg
||
199 aType
== DisplayItemEntryType::PushTransform
;
203 * Returns true if the given |aType| is an effect end marker.
205 static bool IsEffectEndMarker(DisplayItemEntryType aType
) {
206 return aType
== DisplayItemEntryType::PopOpacity
||
207 aType
== DisplayItemEntryType::PopTransform
;
210 enum class MarkerType
{ StartMarker
, EndMarker
};
213 * Returns true if the given nsDisplayOpacity |aItem| has had opacity applied
214 * to its children and can be flattened away.
216 static bool IsOpacityAppliedToChildren(nsDisplayItem
* aItem
) {
217 MOZ_ASSERT(aItem
->GetType() == DisplayItemType::TYPE_OPACITY
);
218 return static_cast<nsDisplayOpacity
*>(aItem
)->OpacityAppliedToChildren();
222 * Returns true if the given display item type supports flattening with markers.
224 static bool SupportsFlatteningWithMarkers(const DisplayItemType
& aType
) {
225 return aType
== DisplayItemType::TYPE_OPACITY
||
226 aType
== DisplayItemType::TYPE_TRANSFORM
;
230 * Adds the effect marker to |aMarkers| based on the type of |aItem| and whether
231 * |markerType| is a start or end marker.
233 template <MarkerType markerType
>
234 static bool AddMarkerIfNeeded(nsDisplayItem
* aItem
,
235 std::deque
<DisplayItemEntry
>& aMarkers
) {
236 const DisplayItemType type
= aItem
->GetType();
237 if (!SupportsFlatteningWithMarkers(type
)) {
241 DisplayItemEntryType marker
;
243 // Just a fancy way to avoid writing two separate functions to select between
244 // PUSH and POP markers. This is done during compile time based on |markerType|.
245 #define GET_MARKER(start_marker, end_marker) \
247 markerType == MarkerType::StartMarker, \
248 std::integral_constant<DisplayItemEntryType, start_marker>, \
249 std::integral_constant<DisplayItemEntryType, end_marker>>::type::value;
252 case DisplayItemType::TYPE_OPACITY
:
253 if (IsOpacityAppliedToChildren(aItem
)) {
254 // TODO(miko): I am not a fan of this. The more correct solution would
255 // be to return an enum from nsDisplayItem::ShouldFlattenAway(), so that
256 // we could distinguish between different flattening methods and avoid
257 // entering this function when markers are not needed.
261 marker
= GET_MARKER(DisplayItemEntryType::PushOpacity
,
262 DisplayItemEntryType::PopOpacity
);
264 case DisplayItemType::TYPE_TRANSFORM
:
265 marker
= GET_MARKER(DisplayItemEntryType::PushTransform
,
266 DisplayItemEntryType::PopTransform
);
269 MOZ_ASSERT_UNREACHABLE("Invalid display item type!");
273 aMarkers
.emplace_back(aItem
, marker
);
277 DisplayItemData::DisplayItemData(LayerManagerData
* aParent
, uint32_t aKey
,
278 Layer
* aLayer
, nsIFrame
* aFrame
)
283 mDisplayItemKey(aKey
),
288 MOZ_COUNT_CTOR(DisplayItemData
);
290 if (!sAliveDisplayItemDatas
) {
291 sAliveDisplayItemDatas
= new nsTHashSet
<DisplayItemData
*>();
293 MOZ_RELEASE_ASSERT(!sAliveDisplayItemDatas
->Contains(this));
294 sAliveDisplayItemDatas
->Insert(this);
296 MOZ_RELEASE_ASSERT(mLayer
);
302 void DisplayItemData::Destroy() {
303 // Get the pres context.
304 RefPtr
<nsPresContext
> presContext
= mFrameList
[0]->PresContext();
306 // Call our destructor.
307 this->~DisplayItemData();
309 // Don't let the memory be freed, since it will be recycled
310 // instead. Don't call the global operator delete.
311 presContext
->PresShell()->FreeByObjectID(eArenaObjectID_DisplayItemData
,
315 void DisplayItemData::AddFrame(nsIFrame
* aFrame
) {
316 MOZ_RELEASE_ASSERT(mLayer
);
317 MOZ_RELEASE_ASSERT(!mFrameList
.Contains(aFrame
));
318 mFrameList
.AppendElement(aFrame
);
320 SmallPointerArray
<DisplayItemData
>* array
= aFrame
->DisplayItemData();
323 array
= new SmallPointerArray
<DisplayItemData
>();
324 aFrame
->SetProperty(nsIFrame::DisplayItemDataProperty(), array
);
327 array
->AppendElement(this);
330 void DisplayItemData::RemoveFrame(nsIFrame
* aFrame
) {
331 MOZ_RELEASE_ASSERT(mLayer
);
332 bool result
= mFrameList
.RemoveElement(aFrame
);
333 MOZ_RELEASE_ASSERT(result
, "Can't remove a frame that wasn't added!");
335 SmallPointerArray
<DisplayItemData
>* array
= aFrame
->DisplayItemData();
337 array
->RemoveElement(this);
341 void DisplayItemData::EndUpdate() {
342 MOZ_RELEASE_ASSERT(mLayer
);
346 mOldTransform
= nullptr;
349 void DisplayItemData::EndUpdate(UniquePtr
<nsDisplayItemGeometry
>&& aGeometry
) {
350 MOZ_RELEASE_ASSERT(mLayer
);
352 MOZ_ASSERT(mGeometry
|| aGeometry
);
355 mGeometry
= std::move(aGeometry
);
357 mClip
= mItem
->GetClip();
358 mChangedFrameInvalidations
.SetEmpty();
363 void DisplayItemData::BeginUpdate(Layer
* aLayer
, LayerState aState
,
365 nsPaintedDisplayItem
* aItem
/* = nullptr */) {
366 bool isReused
= false;
367 bool isMerged
= false;
370 isReused
= !aFirstUpdate
? aItem
->IsReused() : false;
372 const nsDisplayWrapList
* wraplist
= aItem
->AsDisplayWrapList();
373 isMerged
= wraplist
&& wraplist
->HasMergedFrames();
376 BeginUpdate(aLayer
, aState
, aItem
, isReused
, isMerged
);
379 void DisplayItemData::BeginUpdate(Layer
* aLayer
, LayerState aState
,
380 nsPaintedDisplayItem
* aItem
, bool aIsReused
,
382 MOZ_RELEASE_ASSERT(mLayer
);
383 MOZ_RELEASE_ASSERT(aLayer
);
386 mInactiveManager
= nullptr;
387 mLayerState
= aState
;
391 mReusedItem
= aIsReused
;
397 if (!aIsMerged
&& mFrameList
.Length() == 1) {
398 MOZ_ASSERT(mFrameList
[0] == aItem
->Frame());
402 // We avoid adding or removing element unnecessarily
403 // since we have to modify userdata each time
404 CopyableAutoTArray
<nsIFrame
*, 4> copy(mFrameList
);
405 if (!copy
.RemoveElement(aItem
->Frame())) {
406 AddFrame(aItem
->Frame());
407 mChangedFrameInvalidations
.Or(mChangedFrameInvalidations
,
408 aItem
->Frame()->InkOverflowRect());
412 MOZ_ASSERT(aItem
->AsDisplayWrapList());
414 for (nsIFrame
* frame
: aItem
->AsDisplayWrapList()->GetMergedFrames()) {
415 if (!copy
.RemoveElement(frame
)) {
417 mChangedFrameInvalidations
.Or(mChangedFrameInvalidations
,
418 frame
->InkOverflowRect());
423 for (nsIFrame
* frame
: copy
) {
425 mChangedFrameInvalidations
.Or(mChangedFrameInvalidations
,
426 frame
->InkOverflowRect());
430 static const nsIFrame
* sDestroyedFrame
= nullptr;
431 DisplayItemData::~DisplayItemData() {
432 MOZ_COUNT_DTOR(DisplayItemData
);
434 for (nsIFrame
* frame
: mFrameList
) {
435 if (frame
== sDestroyedFrame
) {
439 SmallPointerArray
<DisplayItemData
>* array
= frame
->DisplayItemData();
441 array
->RemoveElement(this);
445 MOZ_RELEASE_ASSERT(sAliveDisplayItemDatas
);
446 const bool removed
= sAliveDisplayItemDatas
->EnsureRemoved(this);
447 MOZ_RELEASE_ASSERT(removed
);
449 if (sAliveDisplayItemDatas
->Count() == 0) {
450 delete sAliveDisplayItemDatas
;
451 sAliveDisplayItemDatas
= nullptr;
455 void DisplayItemData::NotifyRemoved() {
456 if (mDisplayItemKey
> static_cast<uint8_t>(DisplayItemType::TYPE_MAX
)) {
457 // This is sort of a hack. The display item key has higher bits set, which
458 // means that it is not the only display item for the frame.
459 // This branch skips separator transforms.
463 const DisplayItemType type
= GetDisplayItemTypeFromKey(mDisplayItemKey
);
465 if (type
== DisplayItemType::TYPE_REMOTE
) {
466 // TYPE_REMOTE doesn't support merging, so access it directly
467 MOZ_ASSERT(mFrameList
.Length() == 1);
468 if (mFrameList
.Length() != 1) {
472 // This is a remote browser that is going away, notify it that it is now
474 nsIFrame
* frame
= mFrameList
[0];
475 nsSubDocumentFrame
* subdoc
= static_cast<nsSubDocumentFrame
*>(frame
);
476 nsFrameLoader
* frameLoader
= subdoc
->FrameLoader();
477 if (frameLoader
&& frameLoader
->GetRemoteBrowser()) {
478 frameLoader
->GetRemoteBrowser()->UpdateEffects(
479 mozilla::dom::EffectsInfo::FullyHidden());
483 if (type
!= DisplayItemType::TYPE_TRANSFORM
&&
484 type
!= DisplayItemType::TYPE_OPACITY
&&
485 type
!= DisplayItemType::TYPE_BACKGROUND_COLOR
) {
489 for (nsIFrame
* frame
: mFrameList
) {
490 EffectCompositor::ClearIsRunningOnCompositor(frame
, type
);
494 const nsRegion
& DisplayItemData::GetChangedFrameInvalidations() {
495 return mChangedFrameInvalidations
;
498 DisplayItemData
* DisplayItemData::AssertDisplayItemData(
499 DisplayItemData
* aData
) {
500 MOZ_RELEASE_ASSERT(aData
);
501 MOZ_RELEASE_ASSERT(sAliveDisplayItemDatas
&&
502 sAliveDisplayItemDatas
->Contains(aData
));
503 MOZ_RELEASE_ASSERT(aData
->mLayer
);
507 void* DisplayItemData::operator new(size_t sz
, nsPresContext
* aPresContext
) {
508 // Check the recycle list first.
509 return aPresContext
->PresShell()->AllocateByObjectID(
510 eArenaObjectID_DisplayItemData
, sz
);
514 * This is the userdata we associate with a layer manager.
516 class LayerManagerData
: public LayerUserData
{
518 explicit LayerManagerData(LayerManager
* aManager
)
519 : mLayerManager(aManager
),
520 #ifdef DEBUG_DISPLAY_ITEM_DATA
523 mInvalidateAllLayers(false) {
524 MOZ_COUNT_CTOR(LayerManagerData
);
526 ~LayerManagerData() override
{ MOZ_COUNT_DTOR(LayerManagerData
); }
528 #ifdef DEBUG_DISPLAY_ITEM_DATA
529 void Dump(const char* aPrefix
= "") {
530 printf_stderr("%sLayerManagerData %p\n", aPrefix
, this);
532 for (auto& data
: mDisplayItems
) {
533 nsAutoCString prefix
;
537 const char* layerState
;
538 switch (data
->mLayerState
) {
539 case LayerState::LAYER_NONE
:
540 layerState
= "LAYER_NONE";
542 case LayerState::LAYER_INACTIVE
:
543 layerState
= "LAYER_INACTIVE";
545 case LayerState::LAYER_ACTIVE
:
546 layerState
= "LAYER_ACTIVE";
548 case LayerState::LAYER_ACTIVE_FORCE
:
549 layerState
= "LAYER_ACTIVE_FORCE";
551 case LayerState::LAYER_ACTIVE_EMPTY
:
552 layerState
= "LAYER_ACTIVE_EMPTY";
554 case LayerState::LAYER_SVG_EFFECTS
:
555 layerState
= "LAYER_SVG_EFFECTS";
558 uint32_t mask
= (1 << TYPE_BITS
) - 1;
562 str
+= nsPrintfCString("Frame %p ", data
->mFrameList
[0]);
563 str
+= nsDisplayItem::DisplayItemTypeName(
564 static_cast<nsDisplayItem::Type
>(data
->mDisplayItemKey
& mask
));
565 if ((data
->mDisplayItemKey
>> TYPE_BITS
)) {
566 str
+= nsPrintfCString("(%i)", data
->mDisplayItemKey
>> TYPE_BITS
);
568 str
+= nsPrintfCString(", %s, Layer %p", layerState
, data
->mLayer
.get());
569 if (data
->mOptLayer
) {
570 str
+= nsPrintfCString(", OptLayer %p", data
->mOptLayer
.get());
572 if (data
->mInactiveManager
) {
573 str
+= nsPrintfCString(", InactiveLayerManager %p",
574 data
->mInactiveManager
.get());
578 printf_stderr("%s", str
.get());
580 if (data
->mInactiveManager
) {
582 printf_stderr("%sDumping inactive layer info:\n", prefix
.get());
583 LayerManagerData
* lmd
= static_cast<LayerManagerData
*>(
584 data
->mInactiveManager
->GetUserData(&gLayerManagerUserData
));
585 lmd
->Dump(prefix
.get());
592 * Tracks which frames have layers associated with them.
594 LayerManager
* mLayerManager
;
595 #ifdef DEBUG_DISPLAY_ITEM_DATA
596 LayerManagerData
* mParent
;
598 std::vector
<RefPtr
<DisplayItemData
>> mDisplayItems
;
599 bool mInvalidateAllLayers
;
603 void FrameLayerBuilder::DestroyDisplayItemDataFor(nsIFrame
* aFrame
) {
604 RemoveFrameFromLayerManager(aFrame
);
606 // Destroying a WebRenderUserDataTable can cause destruction of other objects
607 // which can remove frame properties in their destructor. If we delete a frame
608 // property it runs the destructor of the stored object in the middle of
609 // updating the frame property table, so if the destruction of that object
610 // causes another update to the frame property table it would leave the frame
611 // property table in an inconsistent state. So we remove it from the table and
612 // then destroy it. (bug 1530657)
613 WebRenderUserDataTable
* userDataTable
=
614 aFrame
->TakeProperty(WebRenderUserDataProperty::Key());
616 for (const auto& data
: userDataTable
->Values()) {
617 data
->RemoveFromTable();
619 delete userDataTable
;
624 * We keep a stack of these to represent the PaintedLayers that are
625 * currently available to have display items added to.
626 * We use a stack here because as much as possible we want to
627 * assign display items to existing PaintedLayers, and to the lowest
628 * PaintedLayer in z-order. This reduces the number of layers and
629 * makes it more likely a display item will be rendered to an opaque
630 * layer, giving us the best chance of getting subpixel AA.
632 class PaintedLayerData
{
635 : mAnimatedGeometryRoot(nullptr),
638 mReferenceFrame(nullptr),
640 mSolidColor(NS_RGBA(0, 0, 0, 0)),
641 mIsSolidColorInVisibleRegion(false),
642 mNeedComponentAlpha(false),
643 mForceTransparentSurface(false),
644 mHideAllLayersBelow(false),
645 mOpaqueForAnimatedGeometryRootParent(false),
646 mBackfaceHidden(false),
647 mDTCRequiresTargetConfirmation(false),
650 mNewChildLayersIndex(-1)
658 PaintedLayerData(PaintedLayerData
&& aRhs
) = default;
660 ~PaintedLayerData() { MOZ_ASSERT(mTransformLevel
== 0); }
662 #ifdef MOZ_DUMP_PAINTING
664 * Keep track of important decisions for debugging.
668 # define FLB_LOG_PAINTED_LAYER_DECISION(pld, ...) \
669 if (StaticPrefs::layers_dump_decision()) { \
670 pld->mLog.AppendPrintf("\t\t\t\t"); \
671 pld->mLog.AppendPrintf(__VA_ARGS__); \
674 # define FLB_LOG_PAINTED_LAYER_DECISION(...)
678 * Disables component alpha for |aItem| if the component alpha bounds are not
679 * contained in |mOpaqueRegion|. Alternatively if possible, sets
680 * |mNeedComponentAlpha| to true for this PaintedLayerData.
682 bool SetupComponentAlpha(ContainerState
* aState
, nsPaintedDisplayItem
* aItem
,
683 const nsIntRect
& aVisibleRect
,
684 const TransformClipNode
* aTransform
);
687 * Record that an item has been added to the PaintedLayer, so we
688 * need to update our regions.
689 * @param aVisibleRect the area of the item that's visible
691 void Accumulate(ContainerState
* aState
, nsDisplayItem
* aItem
,
692 const nsIntRect
& aVisibleRect
, const nsRect
& aContentRect
,
693 const DisplayItemClip
& aClip
, LayerState aLayerState
,
694 nsDisplayList
* aList
, DisplayItemEntryType aType
,
695 nsTArray
<size_t>& aOpacityIndices
,
696 const RefPtr
<TransformClipNode
>& aTransform
);
698 UniquePtr
<InactiveLayerData
> CreateInactiveLayerData(
699 ContainerState
* aState
, nsPaintedDisplayItem
* aItem
,
700 DisplayItemData
* aData
);
703 * Updates the status of |mTransform| and |aOpacityIndices|, based on |aType|.
705 void UpdateEffectStatus(DisplayItemEntryType aType
,
706 nsTArray
<size_t>& aOpacityIndices
);
708 AnimatedGeometryRoot
* GetAnimatedGeometryRoot() {
709 return mAnimatedGeometryRoot
;
713 * A region including the horizontal pan, vertical pan, and no action regions.
715 nsRegion
CombinedTouchActionRegion();
718 * Add the given hit test info to the hit regions for this PaintedLayer.
720 void AccumulateHitTestItem(ContainerState
* aState
, nsDisplayItem
* aItem
,
721 const DisplayItemClip
& aClip
,
722 TransformClipNode
* aTransform
);
724 void HitRegionsUpdated();
727 * If this represents only a nsDisplayImage, and the image type supports being
728 * optimized to an ImageLayer, returns true.
730 bool CanOptimizeToImageLayer(nsDisplayListBuilder
* aBuilder
);
733 * If this represents only a nsDisplayImage, and the image type supports being
734 * optimized to an ImageLayer, returns an ImageContainer for the underlying
735 * image if one is available.
737 already_AddRefed
<ImageContainer
> GetContainerForImageLayer(
738 nsDisplayListBuilder
* aBuilder
);
740 bool VisibleAboveRegionIntersects(const nsIntRegion
& aRegion
) const {
741 return !mVisibleAboveRegion
.Intersect(aRegion
).IsEmpty();
743 bool VisibleRegionIntersects(const nsIntRegion
& aRegion
) const {
744 return !mVisibleRegion
.Intersect(aRegion
).IsEmpty();
748 * The owning ContainerState that created this PaintedLayerData.
750 ContainerState
* mState
;
753 * The region of visible content in the layer, relative to the
754 * container layer (which is at the snapped top-left of the display
755 * list reference frame).
757 nsIntRegion mVisibleRegion
;
759 * The region of visible content in the layer that is opaque.
760 * Same coordinate system as mVisibleRegion.
762 nsIntRegion mOpaqueRegion
;
764 * The definitely-hit region for this PaintedLayer.
768 * The maybe-hit region for this PaintedLayer.
770 nsRegion mMaybeHitRegion
;
772 * The dispatch-to-content hit region for this PaintedLayer.
774 nsRegion mDispatchToContentHitRegion
;
776 * The region for this PaintedLayer that is sensitive to events
777 * but disallows panning and zooming. This is an approximation
778 * and any deviation from the true region will be part of the
779 * mDispatchToContentHitRegion.
781 nsRegion mNoActionRegion
;
783 * The region for this PaintedLayer that is sensitive to events and
784 * allows horizontal panning but not zooming. This is an approximation
785 * and any deviation from the true region will be part of the
786 * mDispatchToContentHitRegion.
788 nsRegion mHorizontalPanRegion
;
790 * The region for this PaintedLayer that is sensitive to events and
791 * allows vertical panning but not zooming. This is an approximation
792 * and any deviation from the true region will be part of the
793 * mDispatchToContentHitRegion.
795 nsRegion mVerticalPanRegion
;
797 bool mCollapsedTouchActions
= false;
799 * Scaled versions of the bounds of mHitRegion and mMaybeHitRegion.
800 * We store these because FindPaintedLayerFor() needs to consume them
801 * in this form, and it's a hot code path so we don't want to scale
802 * them inside that function.
804 nsIntRect mScaledHitRegionBounds
;
805 nsIntRect mScaledMaybeHitRegionBounds
;
807 * The "active scrolled root" for all content in the layer. Must
808 * be non-null; all content in a PaintedLayer must have the same
809 * active scrolled root.
811 AnimatedGeometryRoot
* mAnimatedGeometryRoot
;
812 const ActiveScrolledRoot
* mASR
;
814 * The chain of clips that should apply to this layer.
816 const DisplayItemClipChain
* mClipChain
;
818 * The offset between mAnimatedGeometryRoot and the reference frame.
820 nsPoint mAnimatedGeometryRootOffset
;
822 * If non-null, the frame from which we'll extract "fixed positioning"
823 * metadata for this layer. This can be a position:fixed frame or a viewport
824 * frame; the latter case is used for background-attachment:fixed content.
826 const nsIFrame
* mReferenceFrame
;
827 PaintedLayer
* mLayer
;
829 * If mIsSolidColorInVisibleRegion is true, this is the color of the visible
834 * True if every pixel in mVisibleRegion will have color mSolidColor.
836 bool mIsSolidColorInVisibleRegion
;
838 * True if there is any text visible in the layer that's over
839 * transparent pixels in the layer.
841 bool mNeedComponentAlpha
;
843 * Set if the layer should be treated as transparent, even if its entire
844 * area is covered by opaque display items. For example, this needs to
845 * be set if something is going to "punch holes" in the layer by clearing
846 * part of its surface.
848 bool mForceTransparentSurface
;
850 * Set if all layers below this PaintedLayer should be hidden.
852 bool mHideAllLayersBelow
;
854 * Set if the opaque region for this layer can be applied to the parent
855 * animated geometry root of this layer's animated geometry root.
856 * We set this when a PaintedLayer's animated geometry root is a scrollframe
857 * and the PaintedLayer completely fills the displayport of the scrollframe.
859 bool mOpaqueForAnimatedGeometryRootParent
;
861 * Set if the backface of this region is hidden to the user.
862 * Content that backface is hidden should not be draw on the layer
863 * with visible backface.
865 bool mBackfaceHidden
;
867 * Set to true if events targeting the dispatch-to-content region
868 * require target confirmation.
869 * See CompositorHitTestFlags::eRequiresTargetConfirmation and
870 * EventRegions::mDTCRequiresTargetConfirmation.
872 bool mDTCRequiresTargetConfirmation
;
874 * Stores the pointer to the nsDisplayImage if we want to
875 * convert this to an ImageLayer.
877 nsDisplayImageContainer
* mImage
;
879 * Stores the clip that we need to apply to the image or, if there is no
880 * image, a clip for SOME item in the layer. There is no guarantee which
881 * item's clip will be stored here and mItemClip should not be used to clip
882 * the whole layer - only some part of the clip should be used, as determined
883 * by PaintedDisplayItemLayerUserData::GetCommonClipCount() - which may even
886 const DisplayItemClip
* mItemClip
;
888 * Index of this layer in mNewChildLayers.
890 int32_t mNewChildLayersIndex
;
892 * The region of visible content above the layer and below the
893 * next PaintedLayerData currently in the stack, if any.
894 * This is a conservative approximation: it contains the true region.
896 nsIntRegion mVisibleAboveRegion
;
898 * All the display items that have been assigned to this painted layer.
899 * These items get added by Accumulate().
901 std::vector
<AssignedDisplayItem
> mAssignedDisplayItems
;
905 * Tracks the level of transform to ensure balanced PUSH/POP markers.
911 struct NewLayerEntry
{
913 : mAnimatedGeometryRoot(nullptr),
916 mScrollMetadataASR(nullptr),
917 mLayerContentsVisibleRect(0, 0, -1, -1),
918 mLayerState(LayerState::LAYER_INACTIVE
),
919 mHideAllLayersBelow(false),
920 mOpaqueForAnimatedGeometryRootParent(false),
921 mUntransformedVisibleRegion(false),
922 mIsFixedToRootScrollFrame(false) {}
923 // mLayer is null if the previous entry is for a PaintedLayer that hasn't
924 // been optimized to some other form (yet).
925 RefPtr
<Layer
> mLayer
;
926 AnimatedGeometryRoot
* mAnimatedGeometryRoot
;
927 const ActiveScrolledRoot
* mASR
;
928 const DisplayItemClipChain
* mClipChain
;
929 const ActiveScrolledRoot
* mScrollMetadataASR
;
930 // If non-null, this ScrollMetadata is set to the be the first ScrollMetadata
932 UniquePtr
<ScrollMetadata
> mBaseScrollMetadata
;
933 // The following are only used for retained layers (for occlusion
934 // culling of those layers). These regions are all relative to the
935 // container reference frame.
936 nsIntRegion mVisibleRegion
;
937 nsIntRegion mOpaqueRegion
;
938 // This rect is in the layer's own coordinate space. The computed visible
939 // region for the layer cannot extend beyond this rect.
940 nsIntRect mLayerContentsVisibleRect
;
941 LayerState mLayerState
;
942 bool mHideAllLayersBelow
;
943 // When mOpaqueForAnimatedGeometryRootParent is true, the opaque region of
944 // this layer is opaque in the same position even subject to the animation of
945 // geometry of mAnimatedGeometryRoot. For example when mAnimatedGeometryRoot
946 // is a scrolled frame and the scrolled content is opaque everywhere in the
947 // displayport, we can set this flag.
948 // When this flag is set, we can treat this opaque region as covering
949 // content whose animated geometry root is the animated geometry root for
950 // mAnimatedGeometryRoot->GetParent().
951 bool mOpaqueForAnimatedGeometryRootParent
;
953 // mVisibleRegion is relative to the associated frame before
955 bool mUntransformedVisibleRegion
;
956 bool mIsFixedToRootScrollFrame
;
959 class PaintedLayerDataTree
;
962 * This is tree node type for PaintedLayerDataTree.
963 * Each node corresponds to a different animated geometry root, and contains
964 * a stack of PaintedLayerDatas, in bottom-to-top order.
965 * There is at most one node per animated geometry root. The ancestor and
966 * descendant relations in PaintedLayerDataTree tree mirror those in the frame
968 * Each node can have clip that describes the potential extents that items in
969 * this node can cover. If mHasClip is false, it means that the node's contents
971 * Testing against the clip instead of the node's actual contents has the
972 * advantage that the node's contents can move or animate without affecting
973 * content in other nodes. So we don't need to re-layerize during animations
974 * (sync or async), and during async animations everything is guaranteed to
976 * The contents of a node's PaintedLayerData stack all share the node's
977 * animated geometry root. The child nodes are on top of the PaintedLayerData
978 * stack, in z-order, and the clip rects of the child nodes are allowed to
979 * intersect with the visible region or visible above region of their parent
980 * node's PaintedLayerDatas.
982 class PaintedLayerDataNode
{
984 PaintedLayerDataNode(PaintedLayerDataTree
& aTree
,
985 PaintedLayerDataNode
* aParent
,
986 AnimatedGeometryRoot
* aAnimatedGeometryRoot
);
987 ~PaintedLayerDataNode();
989 AnimatedGeometryRoot
* GetAnimatedGeometryRoot() const {
990 return mAnimatedGeometryRoot
;
994 * Whether this node's contents can potentially intersect aRect.
995 * aRect is in our tree's ContainerState's coordinate space.
997 bool Intersects(const nsIntRect
& aRect
) const {
998 return !mHasClip
|| mClipRect
.Intersects(aRect
);
1002 * Create a PaintedLayerDataNode for aAnimatedGeometryRoot, add it to our
1003 * children, and return it.
1005 PaintedLayerDataNode
* AddChildNodeFor(
1006 AnimatedGeometryRoot
* aAnimatedGeometryRoot
);
1009 * Find a PaintedLayerData in our mPaintedLayerDataStack that aItem can be
1010 * added to. Creates a new PaintedLayerData by calling
1011 * aNewPaintedLayerCallback if necessary.
1013 template <typename NewPaintedLayerCallbackType
>
1014 PaintedLayerData
* FindPaintedLayerFor(
1015 const nsIntRect
& aVisibleRect
, bool aBackfaceHidden
,
1016 const ActiveScrolledRoot
* aASR
, const DisplayItemClipChain
* aClipChain
,
1017 NewPaintedLayerCallbackType aNewPaintedLayerCallback
);
1020 * Find an opaque background color for aRegion. Pulls a color from the parent
1021 * geometry root if appropriate, but only if that color is present underneath
1022 * the whole clip of this node, so that this node's contents can animate or
1023 * move (possibly async) without having to change the background color.
1024 * @param aUnderIndex Searching will start in mPaintedLayerDataStack right
1025 * below aUnderIndex.
1027 enum { ABOVE_TOP
= -1 };
1028 nscolor
FindOpaqueBackgroundColor(const nsIntRegion
& aRegion
,
1029 int32_t aUnderIndex
= ABOVE_TOP
) const;
1031 * Same as FindOpaqueBackgroundColor, but only returns a color if absolutely
1032 * nothing is in between, so that it can be used for a layer that can move
1033 * anywhere inside our clip.
1035 nscolor
FindOpaqueBackgroundColorCoveringEverything() const;
1038 * Adds aRect to this node's top PaintedLayerData's mVisibleAboveRegion,
1039 * or mVisibleAboveBackgroundRegion if mPaintedLayerDataStack is empty.
1041 void AddToVisibleAboveRegion(const nsIntRect
& aRect
);
1043 * Call this if all of our existing content can potentially be covered, so
1044 * nothing can merge with it and all new content needs to create new items
1045 * on top. This will finish all of our children and pop our whole
1046 * mPaintedLayerDataStack.
1048 void SetAllDrawingAbove();
1051 * Finish this node: Finish all children, finish our PaintedLayer contents,
1052 * and (if requested) adjust our parent's visible above region to include
1055 void Finish(bool aParentNeedsAccurateVisibleAboveRegion
);
1058 * Finish any children that intersect aRect.
1060 void FinishChildrenIntersecting(const nsIntRect
& aRect
);
1063 * Finish all children.
1065 void FinishAllChildren() { FinishAllChildren(true); }
1069 * Finish all items in mPaintedLayerDataStack and clear the stack.
1071 void PopAllPaintedLayerData();
1073 * Finish all of our child nodes, but don't touch mPaintedLayerDataStack.
1075 void FinishAllChildren(bool aThisNodeNeedsAccurateVisibleAboveRegion
);
1077 * Pass off opaque background color searching to our parent node, if we have
1080 nscolor
FindOpaqueBackgroundColorInParentNode() const;
1082 PaintedLayerDataTree
& mTree
;
1083 PaintedLayerDataNode
* mParent
;
1084 AnimatedGeometryRoot
* mAnimatedGeometryRoot
;
1087 * Our contents: a PaintedLayerData stack and our child nodes.
1089 AutoTArray
<PaintedLayerData
, 3> mPaintedLayerDataStack
;
1092 * UniquePtr is used here in the sense of "unique ownership", i.e. there is
1093 * only one owner. Not in the sense of "this is the only pointer to the
1094 * node": There are two other, non-owning, pointers to our child nodes: The
1095 * node's respective children point to their parent node with their mParent
1096 * pointer, and the tree keeps a map of animated geometry root to node in its
1097 * mNodes member. These outside pointers are the reason that mChildren isn't
1098 * just an nsTArray<PaintedLayerDataNode> (since the pointers would become
1099 * invalid whenever the array expands its capacity).
1101 nsTArray
<UniquePtr
<PaintedLayerDataNode
>> mChildren
;
1104 * The region that's covered between our "background" and the bottom of
1105 * mPaintedLayerDataStack. This is used to indicate whether we can pull
1106 * a background color from our parent node. If mVisibleAboveBackgroundRegion
1107 * should be considered infinite, mAllDrawingAboveBackground will be true and
1108 * the value of mVisibleAboveBackgroundRegion will be meaningless.
1110 nsIntRegion mVisibleAboveBackgroundRegion
;
1113 * Our clip, if we have any. If not, that means we can move anywhere, and
1114 * mHasClip will be false and mClipRect will be meaningless.
1116 nsIntRect mClipRect
;
1120 * Whether mVisibleAboveBackgroundRegion should be considered infinite.
1122 bool mAllDrawingAboveBackground
;
1125 class ContainerState
;
1128 * A tree of PaintedLayerDataNodes. At any point in time, the tree only
1129 * contains nodes for animated geometry roots that new items can potentially
1130 * merge into. Any time content is added on top that overlaps existing things
1131 * in such a way that we no longer want to merge new items with some existing
1132 * content, that existing content gets "finished".
1133 * The public-facing methods of this class are FindPaintedLayerFor,
1134 * AddingOwnLayer, and Finish. The other public methods are for
1135 * PaintedLayerDataNode.
1136 * The tree calls out to its containing ContainerState for some things.
1137 * All coordinates / rects in the tree or the tree nodes are in the
1138 * ContainerState's coordinate space, i.e. relative to the reference frame and
1140 * The clip rects of sibling nodes never overlap. This is ensured by finishing
1141 * existing nodes before adding new ones, if this property were to be violated.
1142 * The root tree node doesn't get finished until the ContainerState is
1144 * The tree's root node is always the root reference frame of the builder. We
1145 * don't stop at the container state's mContainerAnimatedGeometryRoot because
1146 * some of our contents can have animated geometry roots that are not
1147 * descendants of the container's animated geometry root. Every animated
1148 * geometry root we encounter for our contents needs to have a defined place in
1151 class PaintedLayerDataTree
{
1153 PaintedLayerDataTree(ContainerState
& aContainerState
,
1154 nscolor
& aBackgroundColor
)
1155 : mContainerState(aContainerState
),
1156 mContainerUniformBackgroundColor(aBackgroundColor
),
1157 mForInactiveLayer(false) {}
1159 ~PaintedLayerDataTree() {
1161 MOZ_ASSERT(mNodes
.Count() == 0);
1164 void InitializeForInactiveLayer(AnimatedGeometryRoot
* aAnimatedGeometryRoot
);
1167 * Notify our contents that some non-PaintedLayer content has been added.
1168 * *aRect needs to be a rectangle that doesn't move with respect to
1169 * aAnimatedGeometryRoot and that contains the added item.
1170 * If aRect is null, the extents will be considered infinite.
1171 * If aOutUniformBackgroundColor is non-null, it will be set to an opaque
1172 * color that can be pulled into the background of the added content, or
1173 * transparent if that is not possible.
1175 void AddingOwnLayer(AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
1176 const nsIntRect
* aRect
,
1177 nscolor
* aOutUniformBackgroundColor
);
1180 * Find a PaintedLayerData for aItem. This can either be an existing
1181 * PaintedLayerData from inside a node in our tree, or a new one that gets
1182 * created by a call out to aNewPaintedLayerCallback.
1184 template <typename NewPaintedLayerCallbackType
>
1185 PaintedLayerData
* FindPaintedLayerFor(
1186 AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
1187 const ActiveScrolledRoot
* aASR
, const DisplayItemClipChain
* aClipChain
,
1188 const nsIntRect
& aVisibleRect
, const bool aBackfaceHidden
,
1189 NewPaintedLayerCallbackType aNewPaintedLayerCallback
);
1192 * Finish everything.
1197 * Get the parent animated geometry root of aAnimatedGeometryRoot.
1198 * That's either aAnimatedGeometryRoot's animated geometry root, or, if
1199 * that's aAnimatedGeometryRoot itself, then it's the animated geometry
1200 * root for aAnimatedGeometryRoot's cross-doc parent frame.
1202 AnimatedGeometryRoot
* GetParentAnimatedGeometryRoot(
1203 AnimatedGeometryRoot
* aAnimatedGeometryRoot
);
1206 * Whether aAnimatedGeometryRoot has an intrinsic clip that doesn't move with
1207 * respect to aAnimatedGeometryRoot's parent animated geometry root.
1208 * If aAnimatedGeometryRoot is a scroll frame, this will be the scroll frame's
1209 * scroll port, otherwise there is no clip.
1210 * This method doesn't have much to do with PaintedLayerDataTree, but this is
1211 * where we have easy access to a display list builder, which we use to get
1212 * the clip rect result into the right coordinate space.
1214 bool IsClippedWithRespectToParentAnimatedGeometryRoot(
1215 AnimatedGeometryRoot
* aAnimatedGeometryRoot
, nsIntRect
* aOutClip
);
1218 * Called by PaintedLayerDataNode when it is finished, so that we can drop
1219 * our pointers to it.
1221 void NodeWasFinished(AnimatedGeometryRoot
* aAnimatedGeometryRoot
);
1223 nsDisplayListBuilder
* Builder() const;
1224 ContainerState
& ContState() const { return mContainerState
; }
1225 nscolor
UniformBackgroundColor() const {
1226 return mContainerUniformBackgroundColor
;
1231 * Finish all nodes that potentially intersect *aRect, where *aRect is a rect
1232 * that doesn't move with respect to aAnimatedGeometryRoot.
1233 * If aRect is null, *aRect will be considered infinite.
1235 void FinishPotentiallyIntersectingNodes(
1236 AnimatedGeometryRoot
* aAnimatedGeometryRoot
, const nsIntRect
* aRect
);
1239 * Make sure that there is a node for aAnimatedGeometryRoot and all of its
1240 * ancestor geometry roots. Return the node for aAnimatedGeometryRoot.
1242 PaintedLayerDataNode
* EnsureNodeFor(
1243 AnimatedGeometryRoot
* aAnimatedGeometryRoot
);
1246 * Find an existing node in the tree for an ancestor of aAnimatedGeometryRoot.
1247 * *aOutAncestorChild will be set to the last ancestor that was encountered
1248 * in the search up from aAnimatedGeometryRoot; it will be a child animated
1249 * geometry root of the result, if neither are null.
1251 PaintedLayerDataNode
* FindNodeForAncestorAnimatedGeometryRoot(
1252 AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
1253 AnimatedGeometryRoot
** aOutAncestorChild
);
1255 ContainerState
& mContainerState
;
1256 Maybe
<PaintedLayerDataNode
> mRoot
;
1259 * The uniform opaque color from behind this container layer, or
1260 * NS_RGBA(0,0,0,0) if the background behind this container layer is not
1261 * uniform and opaque. This color can be pulled into PaintedLayers that are
1262 * directly above the background.
1264 nscolor mContainerUniformBackgroundColor
;
1267 * A hash map for quick access the node belonging to a particular animated
1270 nsTHashMap
<nsPtrHashKey
<AnimatedGeometryRoot
>, PaintedLayerDataNode
*> mNodes
;
1272 bool mForInactiveLayer
;
1276 * This is a helper object used to build up the layer children for
1279 class ContainerState
{
1281 ContainerState(nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
1282 FrameLayerBuilder
* aLayerBuilder
, nsIFrame
* aContainerFrame
,
1283 nsDisplayItem
* aContainerItem
, const nsRect
& aContainerBounds
,
1284 ContainerLayer
* aContainerLayer
,
1285 const ContainerLayerParameters
& aParameters
,
1286 nscolor aBackgroundColor
,
1287 const ActiveScrolledRoot
* aContainerASR
,
1288 const ActiveScrolledRoot
* aContainerScrollMetadataASR
,
1289 const ActiveScrolledRoot
* aContainerCompositorASR
)
1290 : mBuilder(aBuilder
),
1292 mLayerBuilder(aLayerBuilder
),
1293 mContainerFrame(aContainerFrame
),
1294 mContainerLayer(aContainerLayer
),
1295 mContainerBounds(aContainerBounds
),
1296 mContainerASR(aContainerASR
),
1297 mContainerScrollMetadataASR(aContainerScrollMetadataASR
),
1298 mContainerCompositorASR(aContainerCompositorASR
),
1299 mParameters(aParameters
),
1300 mPaintedLayerDataTree(*this, aBackgroundColor
),
1301 mLastDisplayPortAGR(nullptr),
1302 mContainerItem(aContainerItem
) {
1303 nsPresContext
* presContext
= aContainerFrame
->PresContext();
1304 mAppUnitsPerDevPixel
= presContext
->AppUnitsPerDevPixel();
1305 mContainerReferenceFrame
= const_cast<nsIFrame
*>(
1306 aContainerItem
? aContainerItem
->ReferenceFrameForChildren()
1307 : mBuilder
->FindReferenceFrameFor(mContainerFrame
));
1308 bool isAtRoot
= !aContainerItem
||
1309 (aContainerItem
->Frame() == mBuilder
->RootReferenceFrame());
1310 MOZ_ASSERT(!isAtRoot
||
1311 mContainerReferenceFrame
== mBuilder
->RootReferenceFrame());
1312 mContainerAnimatedGeometryRoot
=
1313 isAtRoot
? aBuilder
->GetRootAnimatedGeometryRoot()
1314 : aContainerItem
->GetAnimatedGeometryRoot();
1316 !mBuilder
->IsPaintingToWindow() ||
1317 nsLayoutUtils::IsAncestorFrameCrossDoc(
1318 mBuilder
->RootReferenceFrame(), *mContainerAnimatedGeometryRoot
));
1319 // When AllowResidualTranslation is false, display items will be drawn
1320 // scaled with a translation by integer pixels, so we know how the snapping
1322 mSnappingEnabled
= aManager
->IsSnappingEffectiveTransforms() &&
1323 !mParameters
.AllowResidualTranslation();
1328 * This is the method that actually walks a display list and builds
1331 void ProcessDisplayItems(nsDisplayList
* aList
);
1333 * This finalizes all the open PaintedLayers by popping every element off
1334 * mPaintedLayerDataStack, then sets the children of the container layer
1335 * to be all the layers in mNewChildLayers in that order and removes any
1336 * layers as children of the container that aren't in mNewChildLayers.
1337 * @param aTextContentFlags if any child layer has CONTENT_COMPONENT_ALPHA,
1338 * set *aTextContentFlags to CONTENT_COMPONENT_ALPHA
1340 void Finish(uint32_t* aTextContentFlags
,
1341 const nsIntRect
& aContainerPixelBounds
,
1342 nsDisplayList
* aChildItems
);
1344 nscoord
GetAppUnitsPerDevPixel() { return mAppUnitsPerDevPixel
; }
1346 nsIntRect
ScaleToNearestPixels(const nsRect
& aRect
) const {
1347 return aRect
.ScaleToNearestPixels(mParameters
.mXScale
, mParameters
.mYScale
,
1348 mAppUnitsPerDevPixel
);
1350 nsIntRect
ScaleToOutsidePixels(const nsRect
& aRect
,
1351 bool aSnap
= false) const {
1352 if (aRect
.IsEmpty()) {
1355 if (aSnap
&& mSnappingEnabled
) {
1356 return ScaleToNearestPixels(aRect
);
1358 return aRect
.ScaleToOutsidePixels(mParameters
.mXScale
, mParameters
.mYScale
,
1359 mAppUnitsPerDevPixel
);
1361 nsIntRect
ScaleToInsidePixels(const nsRect
& aRect
, bool aSnap
= false) const {
1362 if (aSnap
&& mSnappingEnabled
) {
1363 return ScaleToNearestPixels(aRect
);
1365 return aRect
.ScaleToInsidePixels(mParameters
.mXScale
, mParameters
.mYScale
,
1366 mAppUnitsPerDevPixel
);
1368 nsIntRegion
ScaleRegionToNearestPixels(const nsRegion
& aRegion
) const {
1369 return aRegion
.ScaleToNearestPixels(
1370 mParameters
.mXScale
, mParameters
.mYScale
, mAppUnitsPerDevPixel
);
1372 nsIntRegion
ScaleRegionToInsidePixels(const nsRegion
& aRegion
,
1373 bool aSnap
= false) const {
1374 if (aSnap
&& mSnappingEnabled
) {
1375 return ScaleRegionToNearestPixels(aRegion
);
1377 return aRegion
.ScaleToInsidePixels(mParameters
.mXScale
, mParameters
.mYScale
,
1378 mAppUnitsPerDevPixel
);
1381 nsIntRegion
ScaleRegionToOutsidePixels(const nsRegion
& aRegion
,
1382 bool aSnap
= false) const {
1383 if (aRegion
.IsEmpty()) {
1384 return nsIntRegion();
1386 if (aSnap
&& mSnappingEnabled
) {
1387 return ScaleRegionToNearestPixels(aRegion
);
1389 return aRegion
.ScaleToOutsidePixels(
1390 mParameters
.mXScale
, mParameters
.mYScale
, mAppUnitsPerDevPixel
);
1393 nsIFrame
* GetContainerFrame() const { return mContainerFrame
; }
1394 nsDisplayListBuilder
* Builder() const { return mBuilder
; }
1395 FrameLayerBuilder
* LayerBuilder() const { return mLayerBuilder
; }
1398 * Check if we are currently inside an inactive layer.
1400 bool IsInInactiveLayer() const {
1401 return mLayerBuilder
->GetContainingPaintedLayerData();
1405 * Sets aOuterVisibleRegion as aLayer's visible region.
1406 * @param aOuterVisibleRegion
1407 * is in the coordinate space of the container reference frame.
1408 * @param aLayerContentsVisibleRect, if non-null, is in the layer's own
1409 * coordinate system.
1410 * @param aOuterUntransformed is true if the given aOuterVisibleRegion
1411 * is already untransformed with the matrix of the layer.
1413 void SetOuterVisibleRegionForLayer(
1414 Layer
* aLayer
, const nsIntRegion
& aOuterVisibleRegion
,
1415 const nsIntRect
* aLayerContentsVisibleRect
= nullptr,
1416 bool aOuterUntransformed
= false) const;
1419 * Try to determine whether the PaintedLayer aData has a single opaque color
1420 * covering aRect. If successful, return that color, otherwise return
1422 * If aRect turns out not to intersect any content in the layer,
1423 * *aOutIntersectsLayer will be set to false.
1425 nscolor
FindOpaqueBackgroundColorInLayer(const PaintedLayerData
* aData
,
1426 const nsIntRect
& aRect
,
1427 bool* aOutIntersectsLayer
) const;
1430 * Indicate that we are done adding items to the PaintedLayer represented by
1431 * aData. Make sure that a real PaintedLayer exists for it, and set the final
1432 * visible region and opaque-content.
1434 template <typename FindOpaqueBackgroundColorCallbackType
>
1435 void FinishPaintedLayerData(
1436 PaintedLayerData
& aData
,
1437 FindOpaqueBackgroundColorCallbackType aFindOpaqueBackgroundColor
);
1440 friend class PaintedLayerData
;
1441 friend class FLBDisplayListIterator
;
1443 LayerManager::PaintedLayerCreationHint
GetLayerCreationHint(
1444 AnimatedGeometryRoot
* aAnimatedGeometryRoot
);
1447 * Creates a new PaintedLayer and sets up the transform on the PaintedLayer
1448 * to account for scrolling.
1450 already_AddRefed
<PaintedLayer
> CreatePaintedLayer(PaintedLayerData
* aData
);
1453 * Find a PaintedLayer for recycling, recycle it and prepare it for use, or
1454 * return null if no suitable layer was found.
1456 already_AddRefed
<PaintedLayer
> AttemptToRecyclePaintedLayer(
1457 AnimatedGeometryRoot
* aAnimatedGeometryRoot
, nsDisplayItem
* aItem
,
1458 const nsPoint
& aTopLeft
, const nsIFrame
* aReferenceFrame
);
1460 * Recycle aLayer and do any necessary invalidation.
1462 PaintedDisplayItemLayerUserData
* RecyclePaintedLayer(
1463 PaintedLayer
* aLayer
, AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
1464 bool& didResetScrollPositionForLayerPixelAlignment
);
1467 * Perform the last step of CreatePaintedLayer / AttemptToRecyclePaintedLayer:
1468 * Initialize aData, set up the layer's transform for scrolling, and
1469 * invalidate the layer for layer pixel alignment changes if necessary.
1471 void PreparePaintedLayerForUse(
1472 PaintedLayer
* aLayer
, PaintedDisplayItemLayerUserData
* aData
,
1473 AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
1474 const nsIFrame
* aReferenceFrame
, const nsPoint
& aTopLeft
,
1475 bool aDidResetScrollPositionForLayerPixelAlignment
);
1478 * Attempt to prepare an ImageLayer based upon the provided PaintedLayerData.
1479 * Returns nullptr on failure.
1481 already_AddRefed
<Layer
> PrepareImageLayer(PaintedLayerData
* aData
);
1484 * Attempt to prepare a ColorLayer based upon the provided PaintedLayerData.
1485 * Returns nullptr on failure.
1487 already_AddRefed
<Layer
> PrepareColorLayer(PaintedLayerData
* aData
);
1490 * Grab the next recyclable ColorLayer, or create one if there are no
1491 * more recyclable ColorLayers.
1493 already_AddRefed
<ColorLayer
> CreateOrRecycleColorLayer(
1494 PaintedLayer
* aPainted
);
1496 * Grab the next recyclable ImageLayer, or create one if there are no
1497 * more recyclable ImageLayers.
1499 already_AddRefed
<ImageLayer
> CreateOrRecycleImageLayer(
1500 PaintedLayer
* aPainted
);
1502 * Grab a recyclable ImageLayer for use as a mask layer for aLayer (that is a
1503 * mask layer which has been used for aLayer before), or create one if such
1504 * a layer doesn't exist.
1506 * Since mask layers can exist either on the layer directly, or as a side-
1507 * attachment to FrameMetrics (for ancestor scrollframe clips), we key the
1508 * recycle operation on both the originating layer and the mask layer's
1509 * index in the layer, if any.
1511 struct MaskLayerKey
;
1512 template <typename UserData
>
1513 already_AddRefed
<ImageLayer
> CreateOrRecycleMaskImageLayerFor(
1514 const MaskLayerKey
& aKey
, UserData
* (*aGetUserData
)(Layer
* aLayer
),
1515 void (*aSetDefaultUserData
)(Layer
* aLayer
));
1517 * Grabs all PaintedLayers and ColorLayers from the ContainerLayer and makes
1518 * them available for recycling.
1520 void CollectOldLayers();
1522 * If aItem used to belong to a PaintedLayer, invalidates the area of
1523 * aItem in that layer. If aNewLayer is a PaintedLayer, invalidates the area
1524 * of aItem in that layer.
1526 void InvalidateForLayerChange(nsDisplayItem
* aItem
, PaintedLayer
* aNewLayer
,
1527 DisplayItemData
* aData
);
1529 * Returns true if aItem's opaque area (in aOpaque) covers the entire
1530 * scrollable area of its presshell.
1532 bool ItemCoversScrollableArea(nsDisplayItem
* aItem
, const nsRegion
& aOpaque
);
1535 * Set ScrollMetadata and scroll-induced clipping on aEntry's layer.
1537 void SetupScrollingMetadata(NewLayerEntry
* aEntry
);
1540 * Applies occlusion culling.
1541 * For each layer in mNewChildLayers, remove from its visible region the
1542 * opaque regions of the layers at higher z-index, but only if they have
1543 * the same animated geometry root and fixed-pos frame ancestor.
1544 * The opaque region for the child layers that share the same animated
1545 * geometry root as the container frame is returned in
1546 * *aOpaqueRegionForContainer.
1548 * Also sets scroll metadata on the layers.
1550 void PostprocessRetainedLayers(nsIntRegion
* aOpaqueRegionForContainer
);
1553 * Computes the snapped opaque area of aItem. Sets aList's opaque flag
1554 * if it covers the entire list bounds. Sets *aHideAllLayersBelow to true
1555 * this item covers the entire viewport so that all layers below are
1556 * permanently invisible.
1558 nsIntRegion
ComputeOpaqueRect(nsDisplayItem
* aItem
,
1559 AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
1560 const ActiveScrolledRoot
* aASR
,
1561 const DisplayItemClip
& aClip
,
1562 nsDisplayList
* aList
, bool* aHideAllLayersBelow
,
1563 bool* aOpaqueForAnimatedGeometryRootParent
);
1566 * Fills a PaintedLayerData object that is initialized for a layer that the
1567 * current item will be assigned to. Also creates mNewChildLayers entries.
1568 * @param aData The PaintedLayerData that will be filled.
1569 * @param aVisibleRect The visible rect of the item.
1570 * @param aAnimatedGeometryRoot The item's animated geometry root.
1571 * @param aASR The active scrolled root that moves this
1573 * @param aClipChain The clip chain that the compositor needs to
1574 * apply to this layer.
1575 * @param aScrollMetadataASR The leaf ASR for which scroll metadata needs
1576 * to be set on the layer, because either the layer itself or its scrolled
1577 * clip need to move with that ASR.
1578 * @param aTopLeft The offset between aAnimatedGeometryRoot and
1579 * the reference frame.
1580 * @param aReferenceFrame The reference frame for the item.
1581 * @param aBackfaceHidden The backface visibility for the item frame.
1583 void NewPaintedLayerData(
1584 PaintedLayerData
* aData
, AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
1585 const ActiveScrolledRoot
* aASR
, const DisplayItemClipChain
* aClipChain
,
1586 const ActiveScrolledRoot
* aScrollMetadataASR
, const nsPoint
& aTopLeft
,
1587 const nsIFrame
* aReferenceFrame
, const bool aBackfaceHidden
);
1589 /* Build a mask layer to represent the clipping region. Will return null if
1590 * there is no clipping specified or a mask layer cannot be built.
1591 * Builds an ImageLayer for the appropriate backend; the mask is relative to
1592 * aLayer's visible region.
1593 * aLayer is the layer to be clipped.
1594 * relative to the container reference frame
1595 * aRoundedRectClipCount is used when building mask layers for PaintedLayers,
1597 void SetupMaskLayer(Layer
* aLayer
, const DisplayItemClip
& aClip
);
1600 * If |aClip| has rounded corners, create a mask layer for them, and
1601 * add it to |aLayer|'s ancestor mask layers, returning an index into
1602 * the array of ancestor mask layers. Returns an empty Maybe if
1603 * |aClip| does not have rounded corners, or if no mask layer could
1606 Maybe
<size_t> SetupMaskLayerForScrolledClip(Layer
* aLayer
,
1607 const DisplayItemClip
& aClip
);
1610 * Create/find a mask layer with suitable size for aMaskItem to paint
1611 * css-positioned-masking onto.
1613 void SetupMaskLayerForCSSMask(Layer
* aLayer
,
1614 nsDisplayMasksAndClipPaths
* aMaskItem
);
1616 already_AddRefed
<Layer
> CreateMaskLayer(
1617 Layer
* aLayer
, const DisplayItemClip
& aClip
,
1618 const Maybe
<size_t>& aForAncestorMaskLayer
);
1621 * Get the display port for an AGR.
1622 * The result would be cached for later reusing.
1624 nsRect
GetDisplayPortForAnimatedGeometryRoot(
1625 AnimatedGeometryRoot
* aAnimatedGeometryRoot
);
1627 nsDisplayListBuilder
* mBuilder
;
1628 LayerManager
* mManager
;
1629 FrameLayerBuilder
* mLayerBuilder
;
1630 nsIFrame
* mContainerFrame
;
1631 nsIFrame
* mContainerReferenceFrame
;
1632 AnimatedGeometryRoot
* mContainerAnimatedGeometryRoot
;
1633 ContainerLayer
* mContainerLayer
;
1634 nsRect mContainerBounds
;
1636 // Due to the way we store scroll annotations in the layer tree, we need to
1637 // keep track of three (possibly different) ASRs here.
1638 // mContainerASR is the ASR of the container display item that this
1639 // ContainerState was created for.
1640 // mContainerScrollMetadataASR is the ASR of the leafmost scroll metadata
1641 // that's in effect on mContainerLayer.
1642 // mContainerCompositorASR is the ASR that mContainerLayer moves with on
1643 // the compositor / APZ side, taking into account both the scroll meta data
1644 // and the fixed position annotation on itself and its ancestors.
1645 const ActiveScrolledRoot
* mContainerASR
;
1646 const ActiveScrolledRoot
* mContainerScrollMetadataASR
;
1647 const ActiveScrolledRoot
* mContainerCompositorASR
;
1649 nsRect mAccumulatedChildBounds
;
1651 ContainerLayerParameters mParameters
;
1653 * The region of PaintedLayers that should be invalidated every time
1656 nsIntRegion mInvalidPaintedContent
;
1657 PaintedLayerDataTree mPaintedLayerDataTree
;
1659 * We collect the list of children in here. During ProcessDisplayItems,
1660 * the layers in this array either have mContainerLayer as their parent,
1662 * PaintedLayers have two entries in this array: the second one is used only
1663 * if the PaintedLayer is optimized away to a ColorLayer or ImageLayer. It's
1664 * essential that this array is only appended to, since PaintedLayerData
1665 * records the index of its PaintedLayer in this array.
1667 typedef AutoTArray
<NewLayerEntry
, 1> AutoLayersArray
;
1668 AutoLayersArray mNewChildLayers
;
1669 nsTHashSet
<RefPtr
<PaintedLayer
>> mPaintedLayersAvailableForRecycling
;
1670 nscoord mAppUnitsPerDevPixel
;
1671 bool mSnappingEnabled
;
1673 struct MaskLayerKey
{
1674 MaskLayerKey() : mLayer(nullptr) {}
1675 MaskLayerKey(Layer
* aLayer
, const Maybe
<size_t>& aAncestorIndex
)
1676 : mLayer(aLayer
), mAncestorIndex(aAncestorIndex
) {}
1678 PLDHashNumber
Hash() const {
1679 // Hash the layer and add the layer index to the hash.
1680 return (NS_PTR_TO_UINT32(mLayer
) >> 2) +
1681 (mAncestorIndex
? (*mAncestorIndex
+ 1) : 0);
1683 bool operator==(const MaskLayerKey
& aOther
) const {
1684 return mLayer
== aOther
.mLayer
&& mAncestorIndex
== aOther
.mAncestorIndex
;
1688 Maybe
<size_t> mAncestorIndex
;
1691 nsTHashMap
<nsGenericHashKey
<MaskLayerKey
>, RefPtr
<ImageLayer
>>
1692 mRecycledMaskImageLayers
;
1693 // Keep display port of AGR to avoid wasting time on doing the same
1695 AnimatedGeometryRoot
* mLastDisplayPortAGR
;
1696 nsRect mLastDisplayPortRect
;
1698 nsDisplayItem
* mContainerItem
;
1700 // Cache ScrollMetadata so it doesn't need recomputed if the ASR and clip are
1701 // unchanged. If mASR == nullptr then mMetadata is not valid.
1702 struct CachedScrollMetadata
{
1703 const ActiveScrolledRoot
* mASR
;
1704 const DisplayItemClip
* mClip
;
1705 Maybe
<ScrollMetadata
> mMetadata
;
1707 CachedScrollMetadata() : mASR(nullptr), mClip(nullptr) {}
1709 CachedScrollMetadata mCachedScrollMetadata
;
1712 class FLBDisplayListIterator
: public FlattenedDisplayListIterator
{
1714 FLBDisplayListIterator(nsDisplayListBuilder
* aBuilder
, nsDisplayList
* aList
,
1715 ContainerState
* aState
)
1716 : FlattenedDisplayListIterator(aBuilder
, aList
, false), mState(aState
) {
1718 ResolveFlattening();
1721 DisplayItemEntry
GetNextEntry() {
1722 if (!mMarkers
.empty()) {
1723 DisplayItemEntry entry
= mMarkers
.front();
1724 mMarkers
.pop_front();
1728 return DisplayItemEntry
{GetNextItem(), DisplayItemEntryType::Item
};
1731 bool HasNext() const override
{
1732 return FlattenedDisplayListIterator::HasNext() || !mMarkers
.empty();
1736 bool ShouldFlattenNextItem() override
{
1737 if (!FlattenedDisplayListIterator::ShouldFlattenNextItem()) {
1741 nsDisplayItem
* next
= PeekNext();
1742 const DisplayItemType type
= next
->GetType();
1743 if (type
== DisplayItemType::TYPE_SVG_WRAPPER
) {
1744 // We mark SetContainsSVG for the CONTENT_FRAME_TIME_WITH_SVG metric
1745 if (RefPtr
<LayerManager
> lm
= mState
->mBuilder
->GetWidgetLayerManager()) {
1746 lm
->SetContainsSVG(true);
1750 if (!SupportsFlatteningWithMarkers(type
)) {
1754 if (type
== DisplayItemType::TYPE_OPACITY
&&
1755 IsOpacityAppliedToChildren(next
)) {
1756 // This is the previous opacity flattening path, where the opacity has
1757 // been applied to children.
1761 if (mState
->IsInInactiveLayer() || !ItemWantsInactiveLayer(next
)) {
1762 // Do not flatten nested inactive display items, or display items that
1763 // want an active layer.
1767 // If we reach here, we will emit an effect start marker for
1768 // nsDisplayTransform or nsDisplayOpacity.
1769 MOZ_ASSERT(type
== DisplayItemType::TYPE_TRANSFORM
||
1770 !IsOpacityAppliedToChildren(next
));
1774 void EnterChildList(nsDisplayItem
* aContainerItem
) override
{
1775 mFlattenedLists
.AppendElement(aContainerItem
);
1776 AddMarkerIfNeeded
<MarkerType::StartMarker
>(aContainerItem
, mMarkers
);
1779 void ExitChildList() override
{
1780 MOZ_ASSERT(!mFlattenedLists
.IsEmpty());
1781 nsDisplayItem
* aContainerItem
= mFlattenedLists
.PopLastElement();
1782 AddMarkerIfNeeded
<MarkerType::EndMarker
>(aContainerItem
, mMarkers
);
1785 bool ItemWantsInactiveLayer(nsDisplayItem
* aItem
) {
1786 const LayerState layerState
= aItem
->GetLayerState(
1787 mState
->mBuilder
, mState
->mManager
, mState
->mParameters
);
1789 return layerState
== LayerState::LAYER_INACTIVE
;
1792 std::deque
<DisplayItemEntry
> mMarkers
;
1793 AutoTArray
<nsDisplayItem
*, 16> mFlattenedLists
;
1794 ContainerState
* mState
;
1797 class PaintedDisplayItemLayerUserData
: public LayerUserData
{
1799 PaintedDisplayItemLayerUserData()
1800 : mForcedBackgroundColor(NS_RGBA(0, 0, 0, 0)),
1803 mAppUnitsPerDevPixel(0),
1805 mAnimatedGeometryRootPosition(0, 0),
1807 mContainerLayerFrame(nullptr),
1808 mDisabledAlpha(false) {}
1810 NS_INLINE_DECL_REFCOUNTING(PaintedDisplayItemLayerUserData
);
1813 * A color that should be painted over the bounds of the layer's visible
1814 * region before any other content is painted.
1816 nscolor mForcedBackgroundColor
;
1819 * The resolution scale used.
1821 float mXScale
, mYScale
;
1824 * The appunits per dev pixel for the items in this layer.
1826 nscoord mAppUnitsPerDevPixel
;
1829 * The offset from the PaintedLayer's 0,0 to the
1830 * reference frame. This isn't necessarily the same as the transform
1831 * set on the PaintedLayer since we might also be applying an extra
1832 * offset specified by the parent ContainerLayer/
1834 nsIntPoint mTranslation
;
1837 * We try to make 0,0 of the PaintedLayer be the top-left of the
1838 * border-box of the "active scrolled root" frame (i.e. the nearest ancestor
1839 * frame for the display items that is being actively scrolled). But
1840 * we force the PaintedLayer transform to be an integer translation, and we
1841 * may have a resolution scale, so we have to snap the PaintedLayer transform,
1842 * so 0,0 may not be exactly the top-left of the active scrolled root. Here we
1843 * store the coordinates in PaintedLayer space of the top-left of the
1844 * active scrolled root.
1846 gfxPoint mAnimatedGeometryRootPosition
;
1848 nsIntRegion mRegionToInvalidate
;
1850 // The offset between the active scrolled root of this layer
1851 // and the root of the container for the previous and current
1852 // paints respectively.
1853 nsPoint mLastAnimatedGeometryRootOrigin
;
1854 nsPoint mAnimatedGeometryRootOrigin
;
1856 RefPtr
<ColorLayer
> mColorLayer
;
1857 RefPtr
<ImageLayer
> mImageLayer
;
1859 // The region for which display item visibility for this layer has already
1860 // been calculated. Used to reduce the number of calls to
1861 // RecomputeVisibilityForItems if it is known in advance that a larger
1862 // region will be painted during a transaction than in a single call to
1863 // DrawPaintedLayer, for example when progressive paint is enabled.
1864 nsIntRegion mVisibilityComputedRegion
;
1866 // The area for which we called RecomputeVisibilityForItems on the
1868 nsRect mPreviousRecomputeVisibilityRect
;
1870 // The number of items assigned to this layer on the previous paint.
1871 size_t mLastItemCount
;
1873 // The translation set on this PaintedLayer during the previous paint. This
1874 // is needed when invalidating based on a display item's geometry information
1875 // from the previous paint.
1876 Maybe
<nsIntPoint
> mLastPaintOffset
;
1878 // Temporary state only valid during the FrameLayerBuilder's lifetime.
1879 // FLB's mPaintedLayerItems is responsible for cleaning these up when
1880 // we finish painting to avoid dangling pointers.
1881 std::vector
<AssignedDisplayItem
> mItems
;
1882 nsIFrame
* mContainerLayerFrame
;
1885 * This is set when the painted layer has no component alpha.
1887 bool mDisabledAlpha
;
1890 ~PaintedDisplayItemLayerUserData() override
= default;
1893 FrameLayerBuilder::FrameLayerBuilder()
1894 : mRetainingManager(nullptr),
1895 mDisplayListBuilder(nullptr),
1896 mContainingPaintedLayer(nullptr),
1897 mInactiveLayerClip(nullptr),
1898 mInvalidateAllLayers(false),
1899 mInLayerTreeCompressionMode(false),
1900 mIsInactiveLayerManager(false) {
1901 MOZ_COUNT_CTOR(FrameLayerBuilder
);
1904 FrameLayerBuilder::~FrameLayerBuilder() {
1905 GetMaskLayerImageCache()->Sweep();
1906 for (PaintedDisplayItemLayerUserData
* userData
: mPaintedLayerItems
) {
1907 userData
->mLastPaintOffset
= Some(userData
->mTranslation
);
1908 userData
->mItems
.clear();
1909 userData
->mContainerLayerFrame
= nullptr;
1911 MOZ_COUNT_DTOR(FrameLayerBuilder
);
1914 void FrameLayerBuilder::AddPaintedLayerItemsEntry(
1915 PaintedDisplayItemLayerUserData
* aData
) {
1916 mPaintedLayerItems
.AppendElement(aData
);
1920 * User data for layers which will be used as masks.
1922 struct MaskLayerUserData
: public LayerUserData
{
1924 : mScaleX(-1.0f
), mScaleY(-1.0f
), mAppUnitsPerDevPixel(-1) {}
1925 MaskLayerUserData(const DisplayItemClip
& aClip
, int32_t aAppUnitsPerDevPixel
,
1926 const ContainerLayerParameters
& aParams
)
1927 : mScaleX(aParams
.mXScale
),
1928 mScaleY(aParams
.mYScale
),
1929 mOffset(aParams
.mOffset
),
1930 mAppUnitsPerDevPixel(aAppUnitsPerDevPixel
) {
1931 aClip
.AppendRoundedRects(&mRoundedClipRects
);
1934 void operator=(MaskLayerUserData
&& aOther
) {
1935 mScaleX
= aOther
.mScaleX
;
1936 mScaleY
= aOther
.mScaleY
;
1937 mOffset
= aOther
.mOffset
;
1938 mAppUnitsPerDevPixel
= aOther
.mAppUnitsPerDevPixel
;
1939 mRoundedClipRects
= std::move(aOther
.mRoundedClipRects
);
1942 bool operator==(const MaskLayerUserData
& aOther
) const {
1943 return mRoundedClipRects
== aOther
.mRoundedClipRects
&&
1944 mScaleX
== aOther
.mScaleX
&& mScaleY
== aOther
.mScaleY
&&
1945 mOffset
== aOther
.mOffset
&&
1946 mAppUnitsPerDevPixel
== aOther
.mAppUnitsPerDevPixel
;
1949 // Keeps a MaskLayerImageKey alive by managing its mLayerCount member-var
1950 MaskLayerImageCache::MaskLayerImageKeyRef mImageKey
;
1951 // properties of the mask layer; the mask layer may be re-used if these
1952 // remain unchanged.
1953 nsTArray
<DisplayItemClip::RoundedRect
> mRoundedClipRects
;
1954 // scale from the masked layer which is applied to the mask
1955 float mScaleX
, mScaleY
;
1956 // The ContainerLayerParameters offset which is applied to the mask's
1959 int32_t mAppUnitsPerDevPixel
;
1963 * User data for layers which will be used as masks for css positioned mask.
1965 struct CSSMaskLayerUserData
: public LayerUserData
{
1966 CSSMaskLayerUserData() : mMaskStyle(nsStyleImageLayers::LayerType::Mask
) {}
1968 CSSMaskLayerUserData(nsIFrame
* aFrame
, const nsIntRect
& aMaskBounds
,
1969 const nsPoint
& aMaskLayerOffset
)
1970 : mMaskBounds(aMaskBounds
),
1971 mMaskStyle(aFrame
->StyleSVGReset()->mMask
),
1972 mMaskLayerOffset(aMaskLayerOffset
) {}
1974 void operator=(CSSMaskLayerUserData
&& aOther
) {
1975 mMaskBounds
= aOther
.mMaskBounds
;
1976 mMaskStyle
= std::move(aOther
.mMaskStyle
);
1977 mMaskLayerOffset
= aOther
.mMaskLayerOffset
;
1980 bool operator==(const CSSMaskLayerUserData
& aOther
) const {
1981 if (!mMaskBounds
.IsEqualInterior(aOther
.mMaskBounds
)) {
1985 // Make sure we draw the same portion of the mask onto mask layer.
1986 if (mMaskLayerOffset
!= aOther
.mMaskLayerOffset
) {
1990 return mMaskStyle
== aOther
.mMaskStyle
;
1994 nsIntRect mMaskBounds
;
1995 nsStyleImageLayers mMaskStyle
;
1996 nsPoint mMaskLayerOffset
; // The offset from the origin of mask bounds to
1997 // the origin of mask layer.
2001 * A helper object to create a draw target for painting mask and create a
2002 * image container to hold the drawing result. The caller can then bind this
2003 * image container with a image mask layer via ImageLayer::SetContainer.
2005 class MaskImageData
{
2007 MaskImageData(const gfx::IntSize
& aSize
, LayerManager
* aLayerManager
)
2008 : mTextureClientLocked(false),
2010 mLayerManager(aLayerManager
) {
2011 MOZ_ASSERT(!mSize
.IsEmpty());
2012 MOZ_ASSERT(mLayerManager
);
2016 if (mTextureClientLocked
) {
2017 MOZ_ASSERT(mTextureClient
);
2018 // Clear DrawTarget before Unlock.
2019 mDrawTarget
= nullptr;
2020 mTextureClient
->Unlock();
2024 gfx::DrawTarget
* CreateDrawTarget() {
2029 if (mLayerManager
->GetBackendType() == LayersBackend::LAYERS_BASIC
) {
2030 mDrawTarget
= mLayerManager
->CreateOptimalMaskDrawTarget(mSize
);
2034 MOZ_ASSERT(mLayerManager
->GetBackendType() ==
2035 LayersBackend::LAYERS_CLIENT
||
2036 mLayerManager
->GetBackendType() == LayersBackend::LAYERS_WR
);
2038 KnowsCompositor
* knowsCompositor
= mLayerManager
->AsKnowsCompositor();
2039 if (!knowsCompositor
) {
2042 mTextureClient
= TextureClient::CreateForDrawing(
2043 knowsCompositor
, SurfaceFormat::A8
, mSize
, BackendSelector::Content
,
2044 TextureFlags::DISALLOW_BIGIMAGE
,
2045 TextureAllocationFlags::ALLOC_CLEAR_BUFFER
);
2046 if (!mTextureClient
) {
2050 mTextureClientLocked
= mTextureClient
->Lock(OpenMode::OPEN_READ_WRITE
);
2051 if (!mTextureClientLocked
) {
2055 mDrawTarget
= mTextureClient
->BorrowDrawTarget();
2059 already_AddRefed
<ImageContainer
> CreateImageAndImageContainer() {
2060 RefPtr
<ImageContainer
> container
= LayerManager::CreateImageContainer();
2061 RefPtr
<Image
> image
= CreateImage();
2066 container
->SetCurrentImageInTransaction(image
);
2068 return container
.forget();
2072 already_AddRefed
<Image
> CreateImage() {
2073 if (mLayerManager
->GetBackendType() == LayersBackend::LAYERS_BASIC
&&
2075 RefPtr
<SourceSurface
> surface
= mDrawTarget
->Snapshot();
2076 RefPtr
<SourceSurfaceImage
> image
= new SourceSurfaceImage(mSize
, surface
);
2077 // Disallow BIGIMAGE (splitting into multiple textures) for mask
2079 image
->SetTextureFlags(TextureFlags::DISALLOW_BIGIMAGE
);
2080 return image
.forget();
2083 if ((mLayerManager
->GetBackendType() == LayersBackend::LAYERS_CLIENT
||
2084 mLayerManager
->GetBackendType() == LayersBackend::LAYERS_WR
) &&
2085 mTextureClient
&& mDrawTarget
) {
2086 RefPtr
<TextureWrapperImage
> image
= new TextureWrapperImage(
2087 mTextureClient
, gfx::IntRect(gfx::IntPoint(0, 0), mSize
));
2088 return image
.forget();
2094 bool mTextureClientLocked
;
2096 LayerManager
* mLayerManager
;
2097 RefPtr
<gfx::DrawTarget
> mDrawTarget
;
2098 RefPtr
<TextureClient
> mTextureClient
;
2102 void FrameLayerBuilder::Shutdown() {
2103 if (gMaskLayerImageCache
) {
2104 delete gMaskLayerImageCache
;
2105 gMaskLayerImageCache
= nullptr;
2109 void FrameLayerBuilder::Init(nsDisplayListBuilder
* aBuilder
,
2110 LayerManager
* aManager
,
2111 PaintedLayerData
* aLayerData
,
2112 bool aIsInactiveLayerManager
,
2113 const DisplayItemClip
* aInactiveLayerClip
) {
2114 mDisplayListBuilder
= aBuilder
;
2116 aBuilder
->RootReferenceFrame()->PresContext()->GetRootPresContext();
2117 mContainingPaintedLayer
= aLayerData
;
2118 mIsInactiveLayerManager
= aIsInactiveLayerManager
;
2119 mInactiveLayerClip
= aInactiveLayerClip
;
2120 aManager
->SetUserData(&gLayerManagerLayerBuilder
, this);
2123 void FrameLayerBuilder::FlashPaint(gfxContext
* aContext
) {
2124 float r
= float(rand()) / float(RAND_MAX
);
2125 float g
= float(rand()) / float(RAND_MAX
);
2126 float b
= float(rand()) / float(RAND_MAX
);
2127 aContext
->SetColor(sRGBColor(r
, g
, b
, 0.4f
));
2131 DisplayItemData
* FrameLayerBuilder::GetDisplayItemData(nsIFrame
* aFrame
,
2133 for (auto* did
: GetDisplayItemDataArray(aFrame
)) {
2134 DisplayItemData
* data
= DisplayItemData::AssertDisplayItemData(did
);
2135 if (data
->mDisplayItemKey
== aKey
&& data
->FirstFrame() == aFrame
&&
2136 data
->mLayer
->Manager() == mRetainingManager
) {
2144 #ifdef MOZ_DUMP_PAINTING
2145 static nsACString
& AppendToString(nsACString
& s
, const nsIntRect
& r
,
2146 const char* pfx
= "", const char* sfx
= "") {
2148 s
+= nsPrintfCString("(x=%d, y=%d, w=%d, h=%d)", r
.x
, r
.y
, r
.width
, r
.height
);
2152 static nsACString
& AppendToString(nsACString
& s
, const nsIntRegion
& r
,
2153 const char* pfx
= "", const char* sfx
= "") {
2157 for (auto iter
= r
.RectIter(); !iter
.Done(); iter
.Next()) {
2158 AppendToString(s
, iter
.Get()) += "; ";
2164 #endif // MOZ_DUMP_PAINTING
2167 * Invalidate aRegion in aLayer. aLayer is in the coordinate system
2168 * *after* aTranslation has been applied, so we need to
2169 * apply the inverse of that transform before calling InvalidateRegion.
2171 static void InvalidatePostTransformRegion(PaintedLayer
* aLayer
,
2172 const nsIntRegion
& aRegion
,
2173 const nsIntPoint
& aTranslation
) {
2174 // Convert the region from the coordinates of the container layer
2175 // (relative to the snapped top-left of the display list reference frame)
2176 // to the PaintedLayer's own coordinates
2177 nsIntRegion rgn
= aRegion
;
2179 rgn
.MoveBy(-aTranslation
);
2180 aLayer
->InvalidateRegion(rgn
);
2181 #ifdef MOZ_DUMP_PAINTING
2182 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2184 AppendToString(str
, rgn
);
2185 printf_stderr("Invalidating layer %p: %s\n", aLayer
, str
.get());
2190 static PaintedDisplayItemLayerUserData
* GetPaintedDisplayItemLayerUserData(
2192 return static_cast<PaintedDisplayItemLayerUserData
*>(
2193 aLayer
->GetUserData(&gPaintedDisplayItemLayerUserData
));
2196 static nsIntPoint
GetTranslationForPaintedLayer(PaintedLayer
* aLayer
) {
2197 PaintedDisplayItemLayerUserData
* layerData
=
2198 GetPaintedDisplayItemLayerUserData(aLayer
);
2199 NS_ASSERTION(layerData
, "Must be a tracked painted layer!");
2201 return layerData
->mTranslation
;
2205 * Get the translation transform that was in aLayer when we last painted. It's
2206 * either the transform saved by ~FrameLayerBuilder(), or else the transform
2207 * that's currently in the layer (which must be an integer translation).
2209 static nsIntPoint
GetLastPaintOffset(PaintedLayer
* aLayer
) {
2210 auto* layerData
= GetPaintedDisplayItemLayerUserData(aLayer
);
2211 MOZ_ASSERT(layerData
);
2212 return layerData
->mLastPaintOffset
.valueOr(layerData
->mTranslation
);
2215 static void InvalidatePreTransformRect(PaintedLayer
* aLayer
,
2216 const nsRect
& aRect
,
2217 const DisplayItemClip
& aClip
,
2218 const nsIntPoint
& aTranslation
,
2219 TransformClipNode
* aTransform
) {
2220 auto* data
= GetPaintedDisplayItemLayerUserData(aLayer
);
2222 nsRect rect
= aClip
.ApplyNonRoundedIntersection(aRect
);
2225 rect
= aTransform
->TransformRect(rect
, data
->mAppUnitsPerDevPixel
);
2228 nsIntRect pixelRect
= rect
.ScaleToOutsidePixels(data
->mXScale
, data
->mYScale
,
2229 data
->mAppUnitsPerDevPixel
);
2231 InvalidatePostTransformRegion(aLayer
, pixelRect
, aTranslation
);
2235 * Some frames can have multiple, nested, retaining layer managers
2236 * associated with them (normal manager, inactive managers, SVG effects).
2237 * In these cases we store the 'outermost' LayerManager data property
2238 * on the frame since we can walk down the chain from there.
2240 * If one of these frames has just been destroyed, we will free the inner
2241 * layer manager when removing the entry from mFramesWithLayers. Destroying
2242 * the layer manager destroys the LayerManagerData and calls into
2243 * the DisplayItemData destructor. If the inner layer manager had any
2244 * items with the same frame, then we attempt to retrieve properties
2245 * from the deleted frame.
2247 * Cache the destroyed frame pointer here so we can avoid crashing in this case.
2251 void FrameLayerBuilder::RemoveFrameFromLayerManager(nsIFrame
* aFrame
) {
2252 MOZ_RELEASE_ASSERT(!sDestroyedFrame
);
2253 sDestroyedFrame
= aFrame
;
2255 SmallPointerArray
<DisplayItemData
>* array
=
2256 aFrame
->TakeProperty(nsIFrame::DisplayItemDataProperty());
2258 // Hold a reference to all the items so that they don't get
2259 // deleted from under us.
2260 nsTArray
<RefPtr
<DisplayItemData
>> arrayCopy
;
2263 for (DisplayItemData
* data
: *array
) {
2264 arrayCopy
.AppendElement(data
);
2268 #ifdef DEBUG_DISPLAY_ITEM_DATA
2269 if (array
&& array
->Length()) {
2270 LayerManagerData
* rootData
= array
->ElementAt(0)->mParent
;
2271 while (rootData
->mParent
) {
2272 rootData
= rootData
->mParent
;
2274 printf_stderr("Removing frame %p - dumping display data\n", aFrame
);
2279 for (DisplayItemData
* data
: arrayCopy
) {
2280 PaintedLayer
* t
= data
->mLayer
? data
->mLayer
->AsPaintedLayer() : nullptr;
2282 auto* paintedData
= GetPaintedDisplayItemLayerUserData(t
);
2283 if (paintedData
&& data
->mGeometry
) {
2284 const int32_t appUnitsPerDevPixel
= paintedData
->mAppUnitsPerDevPixel
;
2285 nsRegion rgn
= data
->mGeometry
->ComputeInvalidationRegion();
2286 nsIntRegion pixelRgn
= rgn
.ToOutsidePixels(appUnitsPerDevPixel
);
2288 if (data
->mTransform
) {
2289 pixelRgn
= data
->mTransform
->TransformRegion(pixelRgn
);
2293 pixelRgn
.ScaleRoundOut(paintedData
->mXScale
, paintedData
->mYScale
);
2295 pixelRgn
.MoveBy(-GetTranslationForPaintedLayer(t
));
2297 paintedData
->mRegionToInvalidate
.Or(paintedData
->mRegionToInvalidate
,
2299 paintedData
->mRegionToInvalidate
.SimplifyOutward(8);
2303 auto it
= std::find(data
->mParent
->mDisplayItems
.begin(),
2304 data
->mParent
->mDisplayItems
.end(), data
);
2305 MOZ_ASSERT(it
!= data
->mParent
->mDisplayItems
.end());
2306 std::iter_swap(it
, data
->mParent
->mDisplayItems
.end() - 1);
2307 data
->mParent
->mDisplayItems
.pop_back();
2310 if (aFrame
->IsSubDocumentFrame()) {
2311 const nsSubDocumentFrame
* subdoc
=
2312 static_cast<const nsSubDocumentFrame
*>(aFrame
);
2313 nsFrameLoader
* frameLoader
= subdoc
->FrameLoader();
2314 if (frameLoader
&& frameLoader
->GetRemoteBrowser()) {
2315 // This is a remote browser that is going away, notify it that it is now
2317 frameLoader
->GetRemoteBrowser()->UpdateEffects(
2318 mozilla::dom::EffectsInfo::FullyHidden());
2324 sDestroyedFrame
= nullptr;
2327 void FrameLayerBuilder::DidBeginRetainedLayerTransaction(
2328 LayerManager
* aManager
) {
2329 mRetainingManager
= aManager
;
2330 LayerManagerData
* data
= static_cast<LayerManagerData
*>(
2331 aManager
->GetUserData(&gLayerManagerUserData
));
2333 mInvalidateAllLayers
= data
->mInvalidateAllLayers
;
2335 data
= new LayerManagerData(aManager
);
2336 aManager
->SetUserData(&gLayerManagerUserData
, data
);
2340 void FrameLayerBuilder::DidEndTransaction() {
2341 GetMaskLayerImageCache()->Sweep();
2344 void FrameLayerBuilder::WillEndTransaction() {
2345 if (!mRetainingManager
) {
2349 // We need to save the data we'll need to support retaining.
2350 LayerManagerData
* data
= static_cast<LayerManagerData
*>(
2351 mRetainingManager
->GetUserData(&gLayerManagerUserData
));
2352 NS_ASSERTION(data
, "Must have data!");
2354 // Update all the frames that used to have layers.
2355 auto iter
= data
->mDisplayItems
.begin();
2356 while (iter
!= data
->mDisplayItems
.end()) {
2357 DisplayItemData
* did
= iter
->get();
2359 // This item was visible, but isn't anymore.
2360 PaintedLayer
* t
= did
->mLayer
->AsPaintedLayer();
2361 if (t
&& did
->mGeometry
) {
2362 #ifdef MOZ_DUMP_PAINTING
2363 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2365 "Invalidating unused display item (%i) belonging to "
2366 "frame %p from layer %p\n",
2367 did
->mDisplayItemKey
, did
->mFrameList
[0], t
);
2370 InvalidatePreTransformRect(
2371 t
, did
->mGeometry
->ComputeInvalidationRegion(), did
->mClip
,
2372 GetLastPaintOffset(t
), did
->mTransform
);
2375 did
->NotifyRemoved();
2377 // Remove this item. Swapping it with the last element first is
2378 // quicker than erasing from the middle.
2379 if (iter
!= data
->mDisplayItems
.end() - 1) {
2380 std::iter_swap(iter
, data
->mDisplayItems
.end() - 1);
2381 data
->mDisplayItems
.pop_back();
2383 data
->mDisplayItems
.pop_back();
2387 // Don't increment iter because we still need to process the item which
2391 ComputeGeometryChangeForItem(did
);
2396 data
->mInvalidateAllLayers
= false;
2400 DisplayItemData
* FrameLayerBuilder::GetDisplayItemDataForManager(
2401 nsPaintedDisplayItem
* aItem
, LayerManager
* aManager
) {
2402 for (auto* did
: GetDisplayItemDataArray(aItem
->Frame())) {
2403 DisplayItemData
* data
= DisplayItemData::AssertDisplayItemData(did
);
2404 if (data
->mDisplayItemKey
== aItem
->GetPerFrameKey() &&
2405 data
->mLayer
->Manager() == aManager
) {
2413 bool FrameLayerBuilder::HasRetainedDataFor(const nsIFrame
* aFrame
,
2414 uint32_t aDisplayItemKey
) {
2415 for (auto* did
: GetDisplayItemDataArray(aFrame
)) {
2416 DisplayItemData
* data
= DisplayItemData::AssertDisplayItemData(did
);
2417 if (data
->mDisplayItemKey
== aDisplayItemKey
) {
2422 if (RefPtr
<WebRenderUserData
> data
=
2423 GetWebRenderUserData
<WebRenderFallbackData
>(aFrame
,
2431 DisplayItemData
* FrameLayerBuilder::GetOldLayerForFrame(
2432 nsIFrame
* aFrame
, uint32_t aDisplayItemKey
,
2433 DisplayItemData
* aOldData
, /* = nullptr */
2434 LayerManager
* aOldLayerManager
/* = nullptr */) {
2435 // If we need to build a new layer tree, then just refuse to recycle
2437 if (!mRetainingManager
|| mInvalidateAllLayers
) {
2441 MOZ_ASSERT(!aOldData
|| aOldLayerManager
,
2442 "You must provide aOldLayerManager to check aOldData's validity.");
2443 MOZ_ASSERT_IF(aOldData
, aOldLayerManager
== aOldData
->mLayer
->Manager());
2445 DisplayItemData
* data
= aOldData
;
2446 if (!data
|| aOldLayerManager
!= mRetainingManager
) {
2447 data
= GetDisplayItemData(aFrame
, aDisplayItemKey
);
2450 MOZ_ASSERT(data
== GetDisplayItemData(aFrame
, aDisplayItemKey
));
2455 Layer
* FrameLayerBuilder::GetOldLayerFor(nsDisplayItem
* aItem
,
2456 nsDisplayItemGeometry
** aOldGeometry
,
2457 DisplayItemClip
** aOldClip
) {
2458 uint32_t key
= aItem
->GetPerFrameKey();
2459 nsIFrame
* frame
= aItem
->Frame();
2461 DisplayItemData
* oldData
= GetOldLayerForFrame(frame
, key
);
2464 *aOldGeometry
= oldData
->mGeometry
.get();
2467 *aOldClip
= &oldData
->mClip
;
2469 return oldData
->mLayer
;
2476 DisplayItemData
* FrameLayerBuilder::GetOldDataFor(nsDisplayItem
* aItem
) {
2477 for (auto* did
: GetDisplayItemDataArray(aItem
->Frame())) {
2478 DisplayItemData
* data
= DisplayItemData::AssertDisplayItemData(did
);
2479 if (data
->mDisplayItemKey
== aItem
->GetPerFrameKey()) {
2487 // Reset state that should not persist when a layer is recycled.
2488 static void ResetLayerStateForRecycling(Layer
* aLayer
) {
2489 // Currently, this clears the mask layer and ancestor mask layers.
2490 // Other cleanup may be added here.
2491 aLayer
->SetMaskLayer(nullptr);
2492 aLayer
->SetAncestorMaskLayers({});
2495 already_AddRefed
<ColorLayer
> ContainerState::CreateOrRecycleColorLayer(
2496 PaintedLayer
* aPainted
) {
2497 auto* data
= GetPaintedDisplayItemLayerUserData(aPainted
);
2498 RefPtr
<ColorLayer
> layer
= data
->mColorLayer
;
2500 ResetLayerStateForRecycling(layer
);
2501 layer
->ClearExtraDumpInfo();
2503 // Create a new layer
2504 layer
= mManager
->CreateColorLayer();
2508 // Mark this layer as being used for painting display items
2509 data
->mColorLayer
= layer
;
2510 layer
->SetUserData(&gColorLayerUserData
, nullptr);
2512 // Remove other layer types we might have stored for this PaintedLayer
2513 data
->mImageLayer
= nullptr;
2515 return layer
.forget();
2518 already_AddRefed
<ImageLayer
> ContainerState::CreateOrRecycleImageLayer(
2519 PaintedLayer
* aPainted
) {
2520 auto* data
= GetPaintedDisplayItemLayerUserData(aPainted
);
2521 RefPtr
<ImageLayer
> layer
= data
->mImageLayer
;
2523 ResetLayerStateForRecycling(layer
);
2524 layer
->ClearExtraDumpInfo();
2526 // Create a new layer
2527 layer
= mManager
->CreateImageLayer();
2531 // Mark this layer as being used for painting display items
2532 data
->mImageLayer
= layer
;
2533 layer
->SetUserData(&gImageLayerUserData
, nullptr);
2535 // Remove other layer types we might have stored for this PaintedLayer
2536 data
->mColorLayer
= nullptr;
2538 return layer
.forget();
2541 template <typename UserData
>
2542 already_AddRefed
<ImageLayer
> ContainerState::CreateOrRecycleMaskImageLayerFor(
2543 const MaskLayerKey
& aKey
, UserData
* (*aGetUserData
)(Layer
* aLayer
),
2544 void (*aSetDefaultUserData
)(Layer
* aLayer
)) {
2545 RefPtr
<ImageLayer
> result
= mRecycledMaskImageLayers
.Get(aKey
);
2547 if (result
&& aGetUserData(result
.get())) {
2548 mRecycledMaskImageLayers
.Remove(aKey
);
2549 aKey
.mLayer
->ClearExtraDumpInfo();
2550 // XXX if we use clip on mask layers, null it out here
2552 // Create a new layer
2553 result
= mManager
->CreateImageLayer();
2557 aSetDefaultUserData(result
);
2560 return result
.forget();
2563 static const double SUBPIXEL_OFFSET_EPSILON
= 0.02;
2566 * This normally computes NSToIntRoundUp(aValue). However, if that would
2567 * give a residual near 0.5 while aOldResidual is near -0.5, or
2568 * it would give a residual near -0.5 while aOldResidual is near 0.5, then
2569 * instead we return the integer in the other direction so that the residual
2570 * is close to aOldResidual.
2572 static int32_t RoundToMatchResidual(double aValue
, double aOldResidual
) {
2573 int32_t v
= NSToIntRoundUp(aValue
);
2574 double residual
= aValue
- v
;
2575 if (aOldResidual
< 0) {
2577 fabs(residual
- 1.0 - aOldResidual
) < SUBPIXEL_OFFSET_EPSILON
) {
2579 return int32_t(ceil(aValue
));
2581 } else if (aOldResidual
> 0) {
2583 fabs(residual
+ 1.0 - aOldResidual
) < SUBPIXEL_OFFSET_EPSILON
) {
2584 // Round down instead
2585 return int32_t(floor(aValue
));
2591 static void ResetScrollPositionForLayerPixelAlignment(
2592 AnimatedGeometryRoot
* aAnimatedGeometryRoot
) {
2593 nsIScrollableFrame
* sf
=
2594 nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot
);
2596 sf
->ResetScrollPositionForLayerPixelAlignment();
2600 static void InvalidateEntirePaintedLayer(
2601 PaintedLayer
* aLayer
, AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
2602 const char* aReason
) {
2603 #ifdef MOZ_DUMP_PAINTING
2604 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2605 printf_stderr("Invalidating entire layer %p: %s\n", aLayer
, aReason
);
2608 aLayer
->InvalidateWholeLayer();
2609 aLayer
->SetInvalidRectToVisibleRegion();
2610 ResetScrollPositionForLayerPixelAlignment(aAnimatedGeometryRoot
);
2613 LayerManager::PaintedLayerCreationHint
ContainerState::GetLayerCreationHint(
2614 AnimatedGeometryRoot
* aAnimatedGeometryRoot
) {
2615 // Check whether the layer will be scrollable. This is used as a hint to
2616 // influence whether tiled layers are used or not.
2618 // Check creation hint inherited from our parent.
2619 if (mParameters
.mLayerCreationHint
== LayerManager::SCROLLABLE
) {
2620 return LayerManager::SCROLLABLE
;
2623 // Check whether there's any active scroll frame on the animated geometry
2625 for (AnimatedGeometryRoot
* agr
= aAnimatedGeometryRoot
;
2626 agr
&& agr
!= mContainerAnimatedGeometryRoot
; agr
= agr
->mParentAGR
) {
2627 nsIFrame
* fParent
= nsLayoutUtils::GetCrossDocParentFrameInProcess(*agr
);
2631 nsIScrollableFrame
* scrollable
= do_QueryFrame(fParent
);
2633 return LayerManager::SCROLLABLE
;
2636 return LayerManager::NONE
;
2639 already_AddRefed
<PaintedLayer
> ContainerState::AttemptToRecyclePaintedLayer(
2640 AnimatedGeometryRoot
* aAnimatedGeometryRoot
, nsDisplayItem
* aItem
,
2641 const nsPoint
& aTopLeft
, const nsIFrame
* aReferenceFrame
) {
2642 Layer
* oldLayer
= mLayerBuilder
->GetOldLayerFor(aItem
);
2643 if (!oldLayer
|| !oldLayer
->AsPaintedLayer()) {
2647 if (!mPaintedLayersAvailableForRecycling
.EnsureRemoved(
2648 oldLayer
->AsPaintedLayer())) {
2653 // Try to recycle the layer.
2654 RefPtr
<PaintedLayer
> layer
= oldLayer
->AsPaintedLayer();
2656 // Check if the layer hint has changed and whether or not the layer should
2657 // be recreated because of it.
2658 if (!layer
->IsOptimizedFor(GetLayerCreationHint(aAnimatedGeometryRoot
))) {
2662 bool didResetScrollPositionForLayerPixelAlignment
= false;
2663 PaintedDisplayItemLayerUserData
* data
=
2664 RecyclePaintedLayer(layer
, aAnimatedGeometryRoot
,
2665 didResetScrollPositionForLayerPixelAlignment
);
2666 PreparePaintedLayerForUse(layer
, data
, aAnimatedGeometryRoot
, aReferenceFrame
,
2668 didResetScrollPositionForLayerPixelAlignment
);
2670 return layer
.forget();
2673 static void ReleaseLayerUserData(void* aData
) {
2674 PaintedDisplayItemLayerUserData
* userData
=
2675 static_cast<PaintedDisplayItemLayerUserData
*>(aData
);
2676 userData
->Release();
2679 already_AddRefed
<PaintedLayer
> ContainerState::CreatePaintedLayer(
2680 PaintedLayerData
* aData
) {
2681 LayerManager::PaintedLayerCreationHint creationHint
=
2682 GetLayerCreationHint(aData
->mAnimatedGeometryRoot
);
2684 // Create a new painted layer
2685 RefPtr
<PaintedLayer
> layer
=
2686 mManager
->CreatePaintedLayerWithHint(creationHint
);
2691 // Mark this layer as being used for painting display items
2692 RefPtr
<PaintedDisplayItemLayerUserData
> userData
=
2693 new PaintedDisplayItemLayerUserData();
2694 userData
->mDisabledAlpha
=
2695 mParameters
.mDisableSubpixelAntialiasingInDescendants
;
2696 userData
.get()->AddRef();
2697 layer
->SetUserData(&gPaintedDisplayItemLayerUserData
, userData
,
2698 ReleaseLayerUserData
);
2699 ResetScrollPositionForLayerPixelAlignment(aData
->mAnimatedGeometryRoot
);
2701 PreparePaintedLayerForUse(layer
, userData
, aData
->mAnimatedGeometryRoot
,
2702 aData
->mReferenceFrame
,
2703 aData
->mAnimatedGeometryRootOffset
, true);
2705 return layer
.forget();
2708 PaintedDisplayItemLayerUserData
* ContainerState::RecyclePaintedLayer(
2709 PaintedLayer
* aLayer
, AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
2710 bool& didResetScrollPositionForLayerPixelAlignment
) {
2711 // Clear clip rect and mask layer so we don't accidentally stay clipped.
2712 // We will reapply any necessary clipping.
2713 ResetLayerStateForRecycling(aLayer
);
2714 aLayer
->ClearExtraDumpInfo();
2716 auto* data
= GetPaintedDisplayItemLayerUserData(aLayer
);
2717 NS_ASSERTION(data
, "Recycled PaintedLayers must have user data");
2719 // This gets called on recycled PaintedLayers that are going to be in the
2720 // final layer tree, so it's a convenient time to invalidate the
2721 // content that changed where we don't know what PaintedLayer it belonged
2722 // to, or if we need to invalidate the entire layer, we can do that.
2723 // This needs to be done before we update the PaintedLayer to its new
2724 // transform. See nsGfxScrollFrame::InvalidateInternal, where
2725 // we ensure that mInvalidPaintedContent is updated according to the
2726 // scroll position as of the most recent paint.
2727 if (!FuzzyEqual(data
->mXScale
, mParameters
.mXScale
, 0.00001f
) ||
2728 !FuzzyEqual(data
->mYScale
, mParameters
.mYScale
, 0.00001f
) ||
2729 data
->mAppUnitsPerDevPixel
!= mAppUnitsPerDevPixel
) {
2730 #ifdef MOZ_DUMP_PAINTING
2731 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2732 printf_stderr("Recycled layer %p changed scale\n", aLayer
);
2735 InvalidateEntirePaintedLayer(aLayer
, aAnimatedGeometryRoot
,
2736 "recycled layer changed state");
2737 didResetScrollPositionForLayerPixelAlignment
= true;
2739 if (!data
->mRegionToInvalidate
.IsEmpty()) {
2740 #ifdef MOZ_DUMP_PAINTING
2741 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2742 printf_stderr("Invalidating deleted frame content from layer %p\n",
2746 aLayer
->InvalidateRegion(data
->mRegionToInvalidate
);
2747 #ifdef MOZ_DUMP_PAINTING
2748 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2750 AppendToString(str
, data
->mRegionToInvalidate
);
2751 printf_stderr("Invalidating layer %p: %s\n", aLayer
, str
.get());
2754 data
->mRegionToInvalidate
.SetEmpty();
2759 void ContainerState::PreparePaintedLayerForUse(
2760 PaintedLayer
* aLayer
, PaintedDisplayItemLayerUserData
* aData
,
2761 AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
2762 const nsIFrame
* aReferenceFrame
, const nsPoint
& aTopLeft
,
2763 bool didResetScrollPositionForLayerPixelAlignment
) {
2764 aData
->mXScale
= mParameters
.mXScale
;
2765 aData
->mYScale
= mParameters
.mYScale
;
2766 aData
->mLastAnimatedGeometryRootOrigin
= aData
->mAnimatedGeometryRootOrigin
;
2767 aData
->mAnimatedGeometryRootOrigin
= aTopLeft
;
2768 aData
->mAppUnitsPerDevPixel
= mAppUnitsPerDevPixel
;
2769 aLayer
->SetAllowResidualTranslation(mParameters
.AllowResidualTranslation());
2771 // Set up transform so that 0,0 in the PaintedLayer corresponds to the
2772 // (pixel-snapped) top-left of the aAnimatedGeometryRoot.
2774 (*aAnimatedGeometryRoot
)->GetOffsetToCrossDoc(aReferenceFrame
);
2775 nscoord appUnitsPerDevPixel
=
2776 (*aAnimatedGeometryRoot
)->PresContext()->AppUnitsPerDevPixel();
2777 gfxPoint
scaledOffset(
2778 NSAppUnitsToDoublePixels(offset
.x
, appUnitsPerDevPixel
) *
2779 mParameters
.mXScale
,
2780 NSAppUnitsToDoublePixels(offset
.y
, appUnitsPerDevPixel
) *
2781 mParameters
.mYScale
);
2782 // We call RoundToMatchResidual here so that the residual after rounding
2783 // is close to aData->mAnimatedGeometryRootPosition if possible.
2784 nsIntPoint
pixOffset(
2785 RoundToMatchResidual(scaledOffset
.x
,
2786 aData
->mAnimatedGeometryRootPosition
.x
),
2787 RoundToMatchResidual(scaledOffset
.y
,
2788 aData
->mAnimatedGeometryRootPosition
.y
));
2789 aData
->mTranslation
= pixOffset
;
2790 pixOffset
+= mParameters
.mOffset
;
2791 Matrix matrix
= Matrix::Translation(pixOffset
.x
, pixOffset
.y
);
2792 aLayer
->SetBaseTransform(Matrix4x4::From2D(matrix
));
2794 aData
->mVisibilityComputedRegion
.SetEmpty();
2796 // Calculate exact position of the top-left of the active scrolled root.
2797 // This might not be 0,0 due to the snapping in ScaleToNearestPixels.
2798 gfxPoint animatedGeometryRootTopLeft
=
2799 scaledOffset
- ThebesPoint(matrix
.GetTranslation()) + mParameters
.mOffset
;
2800 const bool disableAlpha
=
2801 mParameters
.mDisableSubpixelAntialiasingInDescendants
;
2802 if (aData
->mDisabledAlpha
!= disableAlpha
) {
2803 aData
->mAnimatedGeometryRootPosition
= animatedGeometryRootTopLeft
;
2804 InvalidateEntirePaintedLayer(aLayer
, aAnimatedGeometryRoot
,
2805 "change of subpixel-AA");
2806 aData
->mDisabledAlpha
= disableAlpha
;
2810 // FIXME: Temporary workaround for bug 681192 and bug 724786.
2811 #ifndef MOZ_WIDGET_ANDROID
2812 // If it has changed, then we need to invalidate the entire layer since the
2813 // pixels in the layer buffer have the content at a (subpixel) offset
2814 // from what we need.
2815 if (!animatedGeometryRootTopLeft
.WithinEpsilonOf(
2816 aData
->mAnimatedGeometryRootPosition
, SUBPIXEL_OFFSET_EPSILON
)) {
2817 aData
->mAnimatedGeometryRootPosition
= animatedGeometryRootTopLeft
;
2818 InvalidateEntirePaintedLayer(aLayer
, aAnimatedGeometryRoot
,
2820 } else if (didResetScrollPositionForLayerPixelAlignment
) {
2821 aData
->mAnimatedGeometryRootPosition
= animatedGeometryRootTopLeft
;
2824 Unused
<< didResetScrollPositionForLayerPixelAlignment
;
2828 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
2830 * Returns the appunits per dev pixel for the item's frame
2832 static int32_t AppUnitsPerDevPixel(nsDisplayItem
* aItem
) {
2833 // The underlying frame for zoom items is the root frame of the subdocument.
2834 // But zoom display items report their bounds etc using the parent document's
2835 // APD because zoom items act as a conversion layer between the two different
2837 if (aItem
->GetType() == DisplayItemType::TYPE_ZOOM
) {
2838 return static_cast<nsDisplayZoom
*>(aItem
)->GetParentAppUnitsPerDevPixel();
2840 return aItem
->Frame()->PresContext()->AppUnitsPerDevPixel();
2845 * Set the visible region for aLayer.
2846 * aOuterVisibleRegion is the visible region relative to the parent layer.
2847 * aLayerContentsVisibleRect, if non-null, is a rectangle in the layer's
2848 * own coordinate system to which the layer's visible region is restricted.
2849 * Consumes *aOuterVisibleRegion.
2851 static void SetOuterVisibleRegion(
2852 Layer
* aLayer
, nsIntRegion
* aOuterVisibleRegion
,
2853 const nsIntRect
* aLayerContentsVisibleRect
= nullptr,
2854 bool aOuterUntransformed
= false) {
2855 Matrix4x4 transform
= aLayer
->GetTransform();
2857 if (aOuterUntransformed
) {
2858 if (aLayerContentsVisibleRect
) {
2859 aOuterVisibleRegion
->And(*aOuterVisibleRegion
,
2860 *aLayerContentsVisibleRect
);
2862 } else if (transform
.Is2D(&transform2D
) &&
2863 !transform2D
.HasNonIntegerTranslation()) {
2864 aOuterVisibleRegion
->MoveBy(-int(transform2D
._31
), -int(transform2D
._32
));
2865 if (aLayerContentsVisibleRect
) {
2866 aOuterVisibleRegion
->And(*aOuterVisibleRegion
,
2867 *aLayerContentsVisibleRect
);
2870 nsIntRect outerRect
= aOuterVisibleRegion
->GetBounds();
2871 // if 'transform' is not invertible, then nothing will be displayed
2872 // for the layer, so it doesn't really matter what we do here
2873 Rect
outerVisible(outerRect
.x
, outerRect
.y
, outerRect
.width
,
2877 Rect layerContentsVisible
= Rect::MaxIntRect();
2879 if (aLayerContentsVisibleRect
) {
2880 NS_ASSERTION(aLayerContentsVisibleRect
->width
>= 0 &&
2881 aLayerContentsVisibleRect
->height
>= 0,
2882 "Bad layer contents rectangle");
2883 // restrict to aLayerContentsVisibleRect before call GfxRectToIntRect,
2884 // in case layerVisible is extremely large (as it can be when
2885 // projecting through the inverse of a 3D transform)
2886 layerContentsVisible
= Rect(
2887 aLayerContentsVisibleRect
->x
, aLayerContentsVisibleRect
->y
,
2888 aLayerContentsVisibleRect
->width
, aLayerContentsVisibleRect
->height
);
2892 transform
.ProjectRectBounds(outerVisible
, layerContentsVisible
);
2894 layerVisible
.RoundOut();
2897 if (!layerVisible
.ToIntRect(&intRect
)) {
2898 intRect
= IntRect::MaxIntRect();
2901 *aOuterVisibleRegion
= intRect
;
2904 aLayer
->SetVisibleRegion(
2905 LayerIntRegion::FromUnknownRegion(*aOuterVisibleRegion
));
2908 void ContainerState::SetOuterVisibleRegionForLayer(
2909 Layer
* aLayer
, const nsIntRegion
& aOuterVisibleRegion
,
2910 const nsIntRect
* aLayerContentsVisibleRect
,
2911 bool aOuterUntransformed
) const {
2912 nsIntRegion visRegion
= aOuterVisibleRegion
;
2913 if (!aOuterUntransformed
) {
2914 visRegion
.MoveBy(mParameters
.mOffset
);
2916 SetOuterVisibleRegion(aLayer
, &visRegion
, aLayerContentsVisibleRect
,
2917 aOuterUntransformed
);
2920 nscolor
ContainerState::FindOpaqueBackgroundColorInLayer(
2921 const PaintedLayerData
* aData
, const nsIntRect
& aRect
,
2922 bool* aOutIntersectsLayer
) const {
2923 *aOutIntersectsLayer
= true;
2925 // Scan the candidate's display items.
2926 nsIntRect deviceRect
= aRect
;
2927 nsRect appUnitRect
= ToAppUnits(deviceRect
, mAppUnitsPerDevPixel
);
2928 appUnitRect
.ScaleInverseRoundOut(mParameters
.mXScale
, mParameters
.mYScale
);
2930 for (auto& assignedItem
: Reversed(aData
->mAssignedDisplayItems
)) {
2931 if (assignedItem
.HasOpacity() || assignedItem
.HasTransform()) {
2932 // We cannot easily calculate the opaque background color for items inside
2933 // a flattened effect.
2937 if (IsEffectEndMarker(assignedItem
.mType
)) {
2938 // An optimization: the underlying display item for effect markers is the
2939 // same for both start and end markers. Skip the effect end markers.
2943 nsDisplayItem
* item
= assignedItem
.mItem
;
2945 nsRect bounds
= item
->GetBounds(mBuilder
, &snap
);
2946 if (snap
&& mSnappingEnabled
) {
2947 nsIntRect snappedBounds
= ScaleToNearestPixels(bounds
);
2948 if (!snappedBounds
.Intersects(deviceRect
)) continue;
2950 if (!snappedBounds
.Contains(deviceRect
)) return NS_RGBA(0, 0, 0, 0);
2953 // The layer's visible rect is already (close enough to) pixel
2954 // aligned, so no need to round out and in here.
2955 if (!bounds
.Intersects(appUnitRect
)) continue;
2957 if (!bounds
.Contains(appUnitRect
)) return NS_RGBA(0, 0, 0, 0);
2960 if (item
->IsInvisibleInRect(appUnitRect
)) {
2964 if (item
->GetClip().IsRectAffectedByClip(deviceRect
, mParameters
.mXScale
,
2965 mParameters
.mYScale
,
2966 mAppUnitsPerDevPixel
)) {
2967 return NS_RGBA(0, 0, 0, 0);
2970 MOZ_ASSERT(!assignedItem
.HasOpacity() && !assignedItem
.HasTransform());
2971 Maybe
<nscolor
> color
= item
->IsUniform(mBuilder
);
2973 if (color
&& NS_GET_A(*color
) == 255) {
2977 return NS_RGBA(0, 0, 0, 0);
2980 *aOutIntersectsLayer
= false;
2981 return NS_RGBA(0, 0, 0, 0);
2984 nscolor
PaintedLayerDataNode::FindOpaqueBackgroundColor(
2985 const nsIntRegion
& aTargetVisibleRegion
, int32_t aUnderIndex
) const {
2986 if (aUnderIndex
== ABOVE_TOP
) {
2987 aUnderIndex
= mPaintedLayerDataStack
.Length();
2989 for (int32_t i
= aUnderIndex
- 1; i
>= 0; --i
) {
2990 const PaintedLayerData
* candidate
= &mPaintedLayerDataStack
[i
];
2991 if (candidate
->VisibleAboveRegionIntersects(aTargetVisibleRegion
)) {
2992 // Some non-PaintedLayer content between target and candidate; this is
2994 return NS_RGBA(0, 0, 0, 0);
2997 if (!candidate
->VisibleRegionIntersects(aTargetVisibleRegion
)) {
2998 // The layer doesn't intersect our target, ignore it and move on
3002 bool intersectsLayer
= true;
3003 nsIntRect rect
= aTargetVisibleRegion
.GetBounds();
3004 nscolor color
= mTree
.ContState().FindOpaqueBackgroundColorInLayer(
3005 candidate
, rect
, &intersectsLayer
);
3006 if (!intersectsLayer
) {
3011 if (mAllDrawingAboveBackground
||
3012 !mVisibleAboveBackgroundRegion
.Intersect(aTargetVisibleRegion
)
3014 // Some non-PaintedLayer content is between this node's background and
3016 return NS_RGBA(0, 0, 0, 0);
3018 return FindOpaqueBackgroundColorInParentNode();
3021 nscolor
PaintedLayerDataNode::FindOpaqueBackgroundColorCoveringEverything()
3023 if (!mPaintedLayerDataStack
.IsEmpty() || mAllDrawingAboveBackground
||
3024 !mVisibleAboveBackgroundRegion
.IsEmpty()) {
3025 return NS_RGBA(0, 0, 0, 0);
3027 return FindOpaqueBackgroundColorInParentNode();
3030 nscolor
PaintedLayerDataNode::FindOpaqueBackgroundColorInParentNode() const {
3033 // Check whether our parent node has uniform content behind our whole
3035 // There's one tricky case here: If our parent node is also a scrollable,
3036 // and is currently scrolled in such a way that this inner one is
3037 // clipped by it, then it's not really clear how we should determine
3038 // whether we have a uniform background in the parent: There might be
3039 // non-uniform content in the parts that our scroll port covers in the
3040 // parent and that are currently outside the parent's clip.
3041 // For now, we'll fail to pull a background color in that case.
3042 return mParent
->FindOpaqueBackgroundColor(mClipRect
);
3044 return mParent
->FindOpaqueBackgroundColorCoveringEverything();
3047 return mTree
.UniformBackgroundColor();
3050 bool PaintedLayerData::CanOptimizeToImageLayer(nsDisplayListBuilder
* aBuilder
) {
3055 return mImage
->CanOptimizeToImageLayer(mLayer
->Manager(), aBuilder
);
3058 already_AddRefed
<ImageContainer
> PaintedLayerData::GetContainerForImageLayer(
3059 nsDisplayListBuilder
* aBuilder
) {
3064 return mImage
->GetContainer(mLayer
->Manager(), aBuilder
);
3067 PaintedLayerDataNode::PaintedLayerDataNode(
3068 PaintedLayerDataTree
& aTree
, PaintedLayerDataNode
* aParent
,
3069 AnimatedGeometryRoot
* aAnimatedGeometryRoot
)
3072 mAnimatedGeometryRoot(aAnimatedGeometryRoot
),
3073 mAllDrawingAboveBackground(false) {
3074 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(
3075 mTree
.Builder()->RootReferenceFrame(), *mAnimatedGeometryRoot
));
3076 mHasClip
= mTree
.IsClippedWithRespectToParentAnimatedGeometryRoot(
3077 mAnimatedGeometryRoot
, &mClipRect
);
3080 PaintedLayerDataNode::~PaintedLayerDataNode() {
3081 MOZ_ASSERT(mPaintedLayerDataStack
.IsEmpty());
3082 MOZ_ASSERT(mChildren
.IsEmpty());
3085 PaintedLayerDataNode
* PaintedLayerDataNode::AddChildNodeFor(
3086 AnimatedGeometryRoot
* aAnimatedGeometryRoot
) {
3087 MOZ_ASSERT(aAnimatedGeometryRoot
->mParentAGR
== mAnimatedGeometryRoot
);
3088 UniquePtr
<PaintedLayerDataNode
> child
=
3089 MakeUnique
<PaintedLayerDataNode
>(mTree
, this, aAnimatedGeometryRoot
);
3090 mChildren
.AppendElement(std::move(child
));
3091 return mChildren
.LastElement().get();
3094 template <typename NewPaintedLayerCallbackType
>
3095 PaintedLayerData
* PaintedLayerDataNode::FindPaintedLayerFor(
3096 const nsIntRect
& aVisibleRect
, const bool aBackfaceHidden
,
3097 const ActiveScrolledRoot
* aASR
, const DisplayItemClipChain
* aClipChain
,
3098 NewPaintedLayerCallbackType aNewPaintedLayerCallback
) {
3099 if (!mPaintedLayerDataStack
.IsEmpty()) {
3100 PaintedLayerData
* lowestUsableLayer
= nullptr;
3101 for (auto& data
: Reversed(mPaintedLayerDataStack
)) {
3102 if (data
.mVisibleAboveRegion
.Intersects(aVisibleRect
)) {
3105 if (data
.mBackfaceHidden
== aBackfaceHidden
&& data
.mASR
== aASR
&&
3106 data
.mClipChain
== aClipChain
) {
3107 lowestUsableLayer
= &data
;
3109 // Also check whether the event-regions intersect the visible rect,
3110 // unless we're in an inactive layer, in which case the event-regions
3111 // will be hoisted out into their own layer.
3112 // For performance reasons, we check the intersection with the bounds
3113 // of the event-regions.
3114 if (!mTree
.ContState().IsInInactiveLayer() &&
3115 (data
.mScaledHitRegionBounds
.Intersects(aVisibleRect
) ||
3116 data
.mScaledMaybeHitRegionBounds
.Intersects(aVisibleRect
))) {
3119 // If the visible region intersects with the current layer then we
3120 // can't possibly use any of the layers below it, so stop the search
3123 // If we're trying to minimize painted layer size and we don't
3124 // intersect the current visible region, then make sure we don't
3125 // use this painted layer.
3126 if (data
.mVisibleRegion
.Intersects(aVisibleRect
)) {
3130 if (StaticPrefs::layout_smaller_painted_layers()) {
3131 lowestUsableLayer
= nullptr;
3134 if (lowestUsableLayer
) {
3135 return lowestUsableLayer
;
3138 PaintedLayerData
* data
= mPaintedLayerDataStack
.AppendElement();
3139 aNewPaintedLayerCallback(data
);
3144 void PaintedLayerDataNode::FinishChildrenIntersecting(const nsIntRect
& aRect
) {
3145 for (int32_t i
= mChildren
.Length() - 1; i
>= 0; i
--) {
3146 if (mChildren
[i
]->Intersects(aRect
)) {
3147 mChildren
[i
]->Finish(true);
3148 mChildren
.RemoveElementAt(i
);
3153 void PaintedLayerDataNode::FinishAllChildren(
3154 bool aThisNodeNeedsAccurateVisibleAboveRegion
) {
3155 for (int32_t i
= mChildren
.Length() - 1; i
>= 0; i
--) {
3156 mChildren
[i
]->Finish(aThisNodeNeedsAccurateVisibleAboveRegion
);
3161 void PaintedLayerDataNode::Finish(bool aParentNeedsAccurateVisibleAboveRegion
) {
3162 // Skip "visible above region" maintenance, because this node is going away.
3163 FinishAllChildren(false);
3165 PopAllPaintedLayerData();
3167 if (mParent
&& aParentNeedsAccurateVisibleAboveRegion
) {
3169 mParent
->AddToVisibleAboveRegion(mClipRect
);
3171 mParent
->SetAllDrawingAbove();
3174 mTree
.NodeWasFinished(mAnimatedGeometryRoot
);
3177 void PaintedLayerDataNode::AddToVisibleAboveRegion(const nsIntRect
& aRect
) {
3178 nsIntRegion
& visibleAboveRegion
=
3179 mPaintedLayerDataStack
.IsEmpty()
3180 ? mVisibleAboveBackgroundRegion
3181 : mPaintedLayerDataStack
.LastElement().mVisibleAboveRegion
;
3182 visibleAboveRegion
.Or(visibleAboveRegion
, aRect
);
3183 visibleAboveRegion
.SimplifyOutward(8);
3186 void PaintedLayerDataNode::SetAllDrawingAbove() {
3187 PopAllPaintedLayerData();
3188 mAllDrawingAboveBackground
= true;
3189 mVisibleAboveBackgroundRegion
.SetEmpty();
3192 void PaintedLayerDataNode::PopAllPaintedLayerData() {
3193 for (int32_t index
= mPaintedLayerDataStack
.Length() - 1; index
>= 0;
3195 PaintedLayerData
& data
= mPaintedLayerDataStack
[index
];
3196 mTree
.ContState().FinishPaintedLayerData(data
, [this, &data
, index
]() {
3197 return this->FindOpaqueBackgroundColor(data
.mVisibleRegion
, index
);
3200 mPaintedLayerDataStack
.Clear();
3203 void PaintedLayerDataTree::InitializeForInactiveLayer(
3204 AnimatedGeometryRoot
* aAnimatedGeometryRoot
) {
3205 mForInactiveLayer
= true;
3206 mRoot
.emplace(*this, nullptr, aAnimatedGeometryRoot
);
3209 nsDisplayListBuilder
* PaintedLayerDataTree::Builder() const {
3210 return mContainerState
.Builder();
3213 void PaintedLayerDataTree::Finish() {
3215 mRoot
->Finish(false);
3217 MOZ_ASSERT(mNodes
.Count() == 0);
3221 void PaintedLayerDataTree::NodeWasFinished(
3222 AnimatedGeometryRoot
* aAnimatedGeometryRoot
) {
3223 mNodes
.Remove(aAnimatedGeometryRoot
);
3226 void PaintedLayerDataTree::AddingOwnLayer(
3227 AnimatedGeometryRoot
* aAnimatedGeometryRoot
, const nsIntRect
* aRect
,
3228 nscolor
* aOutUniformBackgroundColor
) {
3229 PaintedLayerDataNode
* node
= nullptr;
3230 if (mForInactiveLayer
) {
3233 FinishPotentiallyIntersectingNodes(aAnimatedGeometryRoot
, aRect
);
3234 node
= EnsureNodeFor(aAnimatedGeometryRoot
);
3237 if (aOutUniformBackgroundColor
) {
3238 *aOutUniformBackgroundColor
= node
->FindOpaqueBackgroundColor(*aRect
);
3240 node
->AddToVisibleAboveRegion(*aRect
);
3242 if (aOutUniformBackgroundColor
) {
3243 *aOutUniformBackgroundColor
=
3244 node
->FindOpaqueBackgroundColorCoveringEverything();
3246 node
->SetAllDrawingAbove();
3250 template <typename NewPaintedLayerCallbackType
>
3251 PaintedLayerData
* PaintedLayerDataTree::FindPaintedLayerFor(
3252 AnimatedGeometryRoot
* aAnimatedGeometryRoot
, const ActiveScrolledRoot
* aASR
,
3253 const DisplayItemClipChain
* aClipChain
, const nsIntRect
& aVisibleRect
,
3254 const bool aBackfaceHidden
,
3255 NewPaintedLayerCallbackType aNewPaintedLayerCallback
) {
3256 const nsIntRect
* bounds
= &aVisibleRect
;
3257 PaintedLayerDataNode
* node
= nullptr;
3258 if (mForInactiveLayer
) {
3261 FinishPotentiallyIntersectingNodes(aAnimatedGeometryRoot
, bounds
);
3262 node
= EnsureNodeFor(aAnimatedGeometryRoot
);
3265 PaintedLayerData
* data
=
3266 node
->FindPaintedLayerFor(aVisibleRect
, aBackfaceHidden
, aASR
, aClipChain
,
3267 aNewPaintedLayerCallback
);
3271 void PaintedLayerDataTree::FinishPotentiallyIntersectingNodes(
3272 AnimatedGeometryRoot
* aAnimatedGeometryRoot
, const nsIntRect
* aRect
) {
3273 AnimatedGeometryRoot
* ancestorThatIsChildOfCommonAncestor
= nullptr;
3274 PaintedLayerDataNode
* ancestorNode
= FindNodeForAncestorAnimatedGeometryRoot(
3275 aAnimatedGeometryRoot
, &ancestorThatIsChildOfCommonAncestor
);
3276 if (!ancestorNode
) {
3277 // None of our ancestors are in the tree. This should only happen if this
3278 // is the very first item we're looking at.
3283 if (ancestorNode
->GetAnimatedGeometryRoot() == aAnimatedGeometryRoot
) {
3284 // aAnimatedGeometryRoot already has a node in the tree.
3285 // This is the common case.
3286 MOZ_ASSERT(!ancestorThatIsChildOfCommonAncestor
);
3288 ancestorNode
->FinishChildrenIntersecting(*aRect
);
3290 ancestorNode
->FinishAllChildren();
3295 // We have found an existing ancestor, but it's a proper ancestor of our
3296 // animated geometry root.
3297 // ancestorThatIsChildOfCommonAncestor is the last animated geometry root
3298 // encountered on the way up from aAnimatedGeometryRoot to ancestorNode.
3299 MOZ_ASSERT(ancestorThatIsChildOfCommonAncestor
);
3300 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(
3301 *ancestorThatIsChildOfCommonAncestor
, *aAnimatedGeometryRoot
));
3302 MOZ_ASSERT(ancestorThatIsChildOfCommonAncestor
->mParentAGR
==
3303 ancestorNode
->GetAnimatedGeometryRoot());
3305 // ancestorThatIsChildOfCommonAncestor is not in the tree yet!
3306 MOZ_ASSERT(!mNodes
.Get(ancestorThatIsChildOfCommonAncestor
));
3308 // We're about to add a node for ancestorThatIsChildOfCommonAncestor, so we
3309 // finish all intersecting siblings.
3311 if (IsClippedWithRespectToParentAnimatedGeometryRoot(
3312 ancestorThatIsChildOfCommonAncestor
, &clip
)) {
3313 ancestorNode
->FinishChildrenIntersecting(clip
);
3315 ancestorNode
->FinishAllChildren();
3319 PaintedLayerDataNode
* PaintedLayerDataTree::EnsureNodeFor(
3320 AnimatedGeometryRoot
* aAnimatedGeometryRoot
) {
3321 MOZ_ASSERT(aAnimatedGeometryRoot
);
3322 PaintedLayerDataNode
* node
= mNodes
.Get(aAnimatedGeometryRoot
);
3327 AnimatedGeometryRoot
* parentAnimatedGeometryRoot
=
3328 aAnimatedGeometryRoot
->mParentAGR
;
3329 if (!parentAnimatedGeometryRoot
) {
3331 MOZ_ASSERT(*aAnimatedGeometryRoot
== Builder()->RootReferenceFrame());
3332 mRoot
.emplace(*this, nullptr, aAnimatedGeometryRoot
);
3335 PaintedLayerDataNode
* parentNode
=
3336 EnsureNodeFor(parentAnimatedGeometryRoot
);
3337 MOZ_ASSERT(parentNode
);
3338 node
= parentNode
->AddChildNodeFor(aAnimatedGeometryRoot
);
3341 mNodes
.InsertOrUpdate(aAnimatedGeometryRoot
, node
);
3345 bool PaintedLayerDataTree::IsClippedWithRespectToParentAnimatedGeometryRoot(
3346 AnimatedGeometryRoot
* aAnimatedGeometryRoot
, nsIntRect
* aOutClip
) {
3347 if (mForInactiveLayer
) {
3350 nsIScrollableFrame
* scrollableFrame
=
3351 nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot
);
3352 if (!scrollableFrame
) {
3355 nsIFrame
* scrollFrame
= do_QueryFrame(scrollableFrame
);
3356 nsRect scrollPort
= scrollableFrame
->GetScrollPortRect() +
3357 Builder()->ToReferenceFrame(scrollFrame
);
3358 *aOutClip
= mContainerState
.ScaleToNearestPixels(scrollPort
);
3362 PaintedLayerDataNode
*
3363 PaintedLayerDataTree::FindNodeForAncestorAnimatedGeometryRoot(
3364 AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
3365 AnimatedGeometryRoot
** aOutAncestorChild
) {
3366 if (!aAnimatedGeometryRoot
) {
3369 PaintedLayerDataNode
* node
= mNodes
.Get(aAnimatedGeometryRoot
);
3373 *aOutAncestorChild
= aAnimatedGeometryRoot
;
3374 return FindNodeForAncestorAnimatedGeometryRoot(
3375 aAnimatedGeometryRoot
->mParentAGR
, aOutAncestorChild
);
3378 static bool CanOptimizeAwayPaintedLayer(PaintedLayerData
* aData
,
3379 FrameLayerBuilder
* aLayerBuilder
) {
3380 if (!aLayerBuilder
->IsBuildingRetainedLayers()) {
3384 // If there's no painted layer with valid content in it that we can reuse,
3385 // always create a color or image layer (and potentially throw away an
3386 // existing completely invalid painted layer).
3387 if (aData
->mLayer
->GetValidRegion().IsEmpty()) {
3391 // There is an existing painted layer we can reuse. Throwing it away can make
3392 // compositing cheaper (see bug 946952), but it might cause us to re-allocate
3393 // the painted layer frequently due to an animation. So we only discard it if
3394 // we're in tree compression mode, which is triggered at a low frequency.
3395 return aLayerBuilder
->CheckInLayerTreeCompressionMode();
3399 static int32_t FindIndexOfLayerIn(nsTArray
<NewLayerEntry
>& aArray
,
3401 for (uint32_t i
= 0; i
< aArray
.Length(); ++i
) {
3402 if (aArray
[i
].mLayer
== aLayer
) {
3410 already_AddRefed
<Layer
> ContainerState::PrepareImageLayer(
3411 PaintedLayerData
* aData
) {
3412 RefPtr
<ImageContainer
> imageContainer
=
3413 aData
->GetContainerForImageLayer(mBuilder
);
3414 if (!imageContainer
) {
3418 RefPtr
<ImageLayer
> imageLayer
= CreateOrRecycleImageLayer(aData
->mLayer
);
3419 imageLayer
->SetContainer(imageContainer
);
3420 aData
->mImage
->ConfigureLayer(imageLayer
, mParameters
);
3421 imageLayer
->SetPostScale(mParameters
.mXScale
, mParameters
.mYScale
);
3423 if (aData
->mItemClip
->HasClip()) {
3424 ParentLayerIntRect clip
= ViewAs
<ParentLayerPixel
>(
3425 ScaleToNearestPixels(aData
->mItemClip
->GetClipRect()));
3426 clip
.MoveBy(ViewAs
<ParentLayerPixel
>(mParameters
.mOffset
));
3427 imageLayer
->SetClipRect(Some(clip
));
3429 imageLayer
->SetClipRect(Nothing());
3432 FLB_LOG_PAINTED_LAYER_DECISION(aData
, " Selected image layer=%p\n",
3435 return imageLayer
.forget();
3438 already_AddRefed
<Layer
> ContainerState::PrepareColorLayer(
3439 PaintedLayerData
* aData
) {
3440 RefPtr
<ColorLayer
> colorLayer
= CreateOrRecycleColorLayer(aData
->mLayer
);
3441 colorLayer
->SetColor(ToDeviceColor(aData
->mSolidColor
));
3444 colorLayer
->SetBaseTransform(aData
->mLayer
->GetBaseTransform());
3445 colorLayer
->SetPostScale(aData
->mLayer
->GetPostXScale(),
3446 aData
->mLayer
->GetPostYScale());
3448 nsIntRect visibleRect
= aData
->mVisibleRegion
.GetBounds();
3449 visibleRect
.MoveBy(-GetTranslationForPaintedLayer(aData
->mLayer
));
3450 colorLayer
->SetBounds(visibleRect
);
3451 colorLayer
->SetClipRect(Nothing());
3453 FLB_LOG_PAINTED_LAYER_DECISION(aData
, " Selected color layer=%p\n",
3456 return colorLayer
.forget();
3459 static void SetBackfaceHiddenForLayer(bool aBackfaceHidden
, Layer
* aLayer
) {
3460 if (aBackfaceHidden
) {
3461 aLayer
->SetContentFlags(aLayer
->GetContentFlags() |
3462 Layer::CONTENT_BACKFACE_HIDDEN
);
3464 aLayer
->SetContentFlags(aLayer
->GetContentFlags() &
3465 ~Layer::CONTENT_BACKFACE_HIDDEN
);
3469 template <typename FindOpaqueBackgroundColorCallbackType
>
3470 void ContainerState::FinishPaintedLayerData(
3471 PaintedLayerData
& aData
,
3472 FindOpaqueBackgroundColorCallbackType aFindOpaqueBackgroundColor
) {
3473 PaintedLayerData
* data
= &aData
;
3475 if (!data
->mLayer
) {
3476 // No layer was recycled, so we create a new one.
3477 RefPtr
<PaintedLayer
> paintedLayer
= CreatePaintedLayer(data
);
3478 data
->mLayer
= paintedLayer
;
3480 NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers
, paintedLayer
) < 0,
3481 "Layer already in list???");
3482 mNewChildLayers
[data
->mNewChildLayersIndex
].mLayer
=
3483 std::move(paintedLayer
);
3486 auto* userData
= GetPaintedDisplayItemLayerUserData(data
->mLayer
);
3487 NS_ASSERTION(userData
, "where did our user data go?");
3488 userData
->mLastItemCount
= data
->mAssignedDisplayItems
.size();
3490 NewLayerEntry
* newLayerEntry
= &mNewChildLayers
[data
->mNewChildLayersIndex
];
3492 RefPtr
<Layer
> layer
;
3493 bool canOptimizeToImageLayer
= data
->CanOptimizeToImageLayer(mBuilder
);
3495 FLB_LOG_PAINTED_LAYER_DECISION(data
, "Selecting layer for pld=%p\n", data
);
3496 FLB_LOG_PAINTED_LAYER_DECISION(
3497 data
, " Solid=%i, hasImage=%c, canOptimizeAwayPaintedLayer=%i\n",
3498 data
->mIsSolidColorInVisibleRegion
, canOptimizeToImageLayer
? 'y' : 'n',
3499 CanOptimizeAwayPaintedLayer(data
, mLayerBuilder
));
3501 if ((data
->mIsSolidColorInVisibleRegion
|| canOptimizeToImageLayer
) &&
3502 CanOptimizeAwayPaintedLayer(data
, mLayerBuilder
)) {
3504 !(data
->mIsSolidColorInVisibleRegion
&& canOptimizeToImageLayer
),
3505 "Can't be a solid color as well as an image!");
3507 layer
= canOptimizeToImageLayer
? PrepareImageLayer(data
)
3508 : PrepareColorLayer(data
);
3511 NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers
, layer
) < 0,
3512 "Layer already in list???");
3513 NS_ASSERTION(newLayerEntry
->mLayer
== data
->mLayer
,
3514 "Painted layer at wrong index");
3515 // Store optimized layer in reserved slot
3516 NewLayerEntry
* paintedLayerEntry
= newLayerEntry
;
3517 newLayerEntry
= &mNewChildLayers
[data
->mNewChildLayersIndex
+ 1];
3518 NS_ASSERTION(!newLayerEntry
->mLayer
, "Slot already occupied?");
3519 newLayerEntry
->mLayer
= layer
;
3520 newLayerEntry
->mAnimatedGeometryRoot
= data
->mAnimatedGeometryRoot
;
3521 newLayerEntry
->mASR
= paintedLayerEntry
->mASR
;
3522 newLayerEntry
->mClipChain
= paintedLayerEntry
->mClipChain
;
3523 newLayerEntry
->mScrollMetadataASR
= paintedLayerEntry
->mScrollMetadataASR
;
3525 // Hide the PaintedLayer. We leave it in the layer tree so that we
3526 // can find and recycle it later.
3527 ParentLayerIntRect emptyRect
;
3528 data
->mLayer
->SetClipRect(Some(emptyRect
));
3529 data
->mLayer
->SetVisibleRegion(LayerIntRegion());
3530 data
->mLayer
->InvalidateWholeLayer();
3531 data
->mLayer
->SetEventRegions(EventRegions());
3536 // We couldn't optimize to an image layer or a color layer above.
3537 layer
= data
->mLayer
;
3538 layer
->SetClipRect(Nothing());
3539 FLB_LOG_PAINTED_LAYER_DECISION(data
, " Selected painted layer=%p\n",
3543 for (auto& item
: data
->mAssignedDisplayItems
) {
3544 MOZ_ASSERT(item
.mItem
->GetType() !=
3545 DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
);
3547 if (IsEffectEndMarker(item
.mType
)) {
3548 // Do not invalidate for end markers.
3552 InvalidateForLayerChange(item
.mItem
, data
->mLayer
, item
.mDisplayItemData
);
3553 mLayerBuilder
->AddPaintedDisplayItem(data
, item
, layer
);
3554 item
.mDisplayItemData
= nullptr;
3557 if (mLayerBuilder
->IsBuildingRetainedLayers()) {
3558 newLayerEntry
->mVisibleRegion
= data
->mVisibleRegion
;
3559 newLayerEntry
->mOpaqueRegion
= data
->mOpaqueRegion
;
3560 newLayerEntry
->mHideAllLayersBelow
= data
->mHideAllLayersBelow
;
3561 newLayerEntry
->mOpaqueForAnimatedGeometryRootParent
=
3562 data
->mOpaqueForAnimatedGeometryRootParent
;
3564 SetOuterVisibleRegionForLayer(layer
, data
->mVisibleRegion
);
3567 #ifdef MOZ_DUMP_PAINTING
3568 if (!data
->mLog
.IsEmpty()) {
3569 PaintedLayerData
* containingPld
=
3570 mLayerBuilder
->GetContainingPaintedLayerData();
3571 if (containingPld
&& containingPld
->mLayer
) {
3572 containingPld
->mLayer
->AddExtraDumpInfo(nsCString(data
->mLog
));
3574 layer
->AddExtraDumpInfo(nsCString(data
->mLog
));
3579 mLayerBuilder
->AddPaintedLayerItemsEntry(userData
);
3581 nsIntRegion transparentRegion
;
3582 transparentRegion
.Sub(data
->mVisibleRegion
, data
->mOpaqueRegion
);
3583 bool isOpaque
= transparentRegion
.IsEmpty();
3584 // For translucent PaintedLayers, try to find an opaque background
3585 // color that covers the entire area beneath it so we can pull that
3586 // color into this layer to make it opaque.
3587 if (layer
== data
->mLayer
) {
3588 nscolor backgroundColor
= NS_RGBA(0, 0, 0, 0);
3590 backgroundColor
= aFindOpaqueBackgroundColor();
3591 if (NS_GET_A(backgroundColor
) == 255) {
3596 // Store the background color
3597 if (userData
->mForcedBackgroundColor
!= backgroundColor
) {
3598 // Invalidate the entire target PaintedLayer since we're changing
3599 // the background color
3600 #ifdef MOZ_DUMP_PAINTING
3601 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
3603 "Forced background color has changed from #%08X to #%08X "
3605 userData
->mForcedBackgroundColor
, backgroundColor
, data
->mLayer
);
3607 AppendToString(str
, data
->mLayer
->GetValidRegion());
3608 printf_stderr("Invalidating layer %p: %s\n", data
->mLayer
, str
.get());
3611 data
->mLayer
->InvalidateWholeLayer();
3613 userData
->mForcedBackgroundColor
= backgroundColor
;
3615 // mask layer for image and color layers
3616 SetupMaskLayer(layer
, *data
->mItemClip
);
3620 nsIWidget
* widget
= mContainerReferenceFrame
->PresContext()->GetRootWidget();
3621 // See bug 941095. Not quite ready to disable this.
3622 bool hidpi
= false && widget
&& widget
->GetDefaultScale().scale
>= 2;
3624 flags
|= Layer::CONTENT_DISABLE_SUBPIXEL_AA
;
3626 if (isOpaque
&& !data
->mForceTransparentSurface
) {
3627 flags
|= Layer::CONTENT_OPAQUE
;
3628 } else if (data
->mNeedComponentAlpha
&& !hidpi
) {
3629 flags
|= Layer::CONTENT_COMPONENT_ALPHA
;
3631 layer
->SetContentFlags(flags
);
3633 userData
->mItems
= std::move(data
->mAssignedDisplayItems
);
3634 userData
->mContainerLayerFrame
= GetContainerFrame();
3636 PaintedLayerData
* containingPaintedLayerData
=
3637 mLayerBuilder
->GetContainingPaintedLayerData();
3638 // If we're building layers for an inactive layer, the event regions are
3639 // clipped to the inactive layer's clip prior to being combined into the
3640 // event regions of the containing PLD.
3641 // For the dispatch-to-content and maybe-hit regions, rounded corners on
3642 // the clip are ignored, since these are approximate regions. For the
3643 // remaining regions, rounded corners in the clip cause the region to
3644 // be combined into the corresponding "imprecise" region of the
3645 // containing's PLD (e.g. the maybe-hit region instead of the hit region).
3646 const DisplayItemClip
* inactiveLayerClip
=
3647 mLayerBuilder
->GetInactiveLayerClip();
3648 if (containingPaintedLayerData
) {
3649 if (!data
->mDispatchToContentHitRegion
.GetBounds().IsEmpty()) {
3650 nsRect rect
= nsLayoutUtils::TransformFrameRectToAncestor(
3651 mContainerReferenceFrame
,
3652 data
->mDispatchToContentHitRegion
.GetBounds(),
3653 containingPaintedLayerData
->mReferenceFrame
);
3654 if (inactiveLayerClip
) {
3655 rect
= inactiveLayerClip
->ApplyNonRoundedIntersection(rect
);
3657 containingPaintedLayerData
->mDispatchToContentHitRegion
.Or(
3658 containingPaintedLayerData
->mDispatchToContentHitRegion
, rect
);
3659 containingPaintedLayerData
->mDispatchToContentHitRegion
.SimplifyOutward(
3661 if (data
->mDTCRequiresTargetConfirmation
) {
3662 containingPaintedLayerData
->mDTCRequiresTargetConfirmation
= true;
3665 if (!data
->mMaybeHitRegion
.GetBounds().IsEmpty()) {
3666 nsRect rect
= nsLayoutUtils::TransformFrameRectToAncestor(
3667 mContainerReferenceFrame
, data
->mMaybeHitRegion
.GetBounds(),
3668 containingPaintedLayerData
->mReferenceFrame
);
3669 if (inactiveLayerClip
) {
3670 rect
= inactiveLayerClip
->ApplyNonRoundedIntersection(rect
);
3672 containingPaintedLayerData
->mMaybeHitRegion
.Or(
3673 containingPaintedLayerData
->mMaybeHitRegion
, rect
);
3674 containingPaintedLayerData
->mMaybeHitRegion
.SimplifyOutward(8);
3676 Maybe
<Matrix4x4Flagged
> matrixCache
;
3677 nsLayoutUtils::TransformToAncestorAndCombineRegions(
3678 data
->mHitRegion
, mContainerReferenceFrame
,
3679 containingPaintedLayerData
->mReferenceFrame
,
3680 &containingPaintedLayerData
->mHitRegion
,
3681 &containingPaintedLayerData
->mMaybeHitRegion
, &matrixCache
,
3683 // See the comment in nsDisplayList::AddFrame, where the touch action
3684 // regions are handled. The same thing applies here.
3685 bool alreadyHadRegions
=
3686 !containingPaintedLayerData
->mNoActionRegion
.IsEmpty() ||
3687 !containingPaintedLayerData
->mHorizontalPanRegion
.IsEmpty() ||
3688 !containingPaintedLayerData
->mVerticalPanRegion
.IsEmpty();
3689 nsLayoutUtils::TransformToAncestorAndCombineRegions(
3690 data
->mNoActionRegion
, mContainerReferenceFrame
,
3691 containingPaintedLayerData
->mReferenceFrame
,
3692 &containingPaintedLayerData
->mNoActionRegion
,
3693 &containingPaintedLayerData
->mDispatchToContentHitRegion
, &matrixCache
,
3695 nsLayoutUtils::TransformToAncestorAndCombineRegions(
3696 data
->mHorizontalPanRegion
, mContainerReferenceFrame
,
3697 containingPaintedLayerData
->mReferenceFrame
,
3698 &containingPaintedLayerData
->mHorizontalPanRegion
,
3699 &containingPaintedLayerData
->mDispatchToContentHitRegion
, &matrixCache
,
3701 nsLayoutUtils::TransformToAncestorAndCombineRegions(
3702 data
->mVerticalPanRegion
, mContainerReferenceFrame
,
3703 containingPaintedLayerData
->mReferenceFrame
,
3704 &containingPaintedLayerData
->mVerticalPanRegion
,
3705 &containingPaintedLayerData
->mDispatchToContentHitRegion
, &matrixCache
,
3707 if (alreadyHadRegions
) {
3708 containingPaintedLayerData
->mDispatchToContentHitRegion
.OrWith(
3709 containingPaintedLayerData
->CombinedTouchActionRegion());
3711 containingPaintedLayerData
->HitRegionsUpdated();
3713 EventRegions
regions(
3714 ScaleRegionToOutsidePixels(data
->mHitRegion
),
3715 ScaleRegionToOutsidePixels(data
->mMaybeHitRegion
),
3716 ScaleRegionToOutsidePixels(data
->mDispatchToContentHitRegion
),
3717 ScaleRegionToOutsidePixels(data
->mNoActionRegion
),
3718 ScaleRegionToOutsidePixels(data
->mHorizontalPanRegion
),
3719 ScaleRegionToOutsidePixels(data
->mVerticalPanRegion
),
3720 data
->mDTCRequiresTargetConfirmation
);
3722 Matrix mat
= layer
->GetTransform().As2D();
3724 regions
.ApplyTranslationAndScale(mat
._31
, mat
._32
, mat
._11
, mat
._22
);
3726 layer
->SetEventRegions(regions
);
3729 SetBackfaceHiddenForLayer(data
->mBackfaceHidden
, data
->mLayer
);
3730 if (layer
!= data
->mLayer
) {
3731 SetBackfaceHiddenForLayer(data
->mBackfaceHidden
, layer
);
3735 static bool IsItemAreaInWindowOpaqueRegion(
3736 nsDisplayListBuilder
* aBuilder
, nsDisplayItem
* aItem
,
3737 const nsRect
& aComponentAlphaBounds
) {
3738 if (!aItem
->Frame()->PresContext()->IsChrome()) {
3739 // Assume that Web content is always in the window opaque region.
3742 if (aItem
->ReferenceFrame() != aBuilder
->RootReferenceFrame()) {
3743 // aItem is probably in some transformed subtree.
3744 // We're not going to bother figuring out where this landed, we're just
3745 // going to assume it might have landed over a transparent part of
3749 return aBuilder
->GetWindowOpaqueRegion().Contains(aComponentAlphaBounds
);
3752 void PaintedLayerData::UpdateEffectStatus(DisplayItemEntryType aType
,
3753 nsTArray
<size_t>& aOpacityIndices
) {
3755 case DisplayItemEntryType::PushOpacity
:
3756 // The index of the new assigned display item in |mAssignedDisplayItems|
3757 // array will be the current length of the array.
3758 aOpacityIndices
.AppendElement(mAssignedDisplayItems
.size());
3760 case DisplayItemEntryType::PopOpacity
:
3761 MOZ_ASSERT(!aOpacityIndices
.IsEmpty());
3762 aOpacityIndices
.RemoveLastElement();
3765 case DisplayItemEntryType::PopTransform
:
3766 MOZ_ASSERT(mTransformLevel
>= 0);
3769 case DisplayItemEntryType::PushTransform
:
3778 bool PaintedLayerData::SetupComponentAlpha(
3779 ContainerState
* aState
, nsPaintedDisplayItem
* aItem
,
3780 const nsIntRect
& aVisibleRect
, const TransformClipNode
* aTransform
) {
3781 nsRect componentAlphaBounds
=
3782 aItem
->GetComponentAlphaBounds(aState
->mBuilder
);
3784 if (componentAlphaBounds
.IsEmpty()) {
3785 // The item does not require component alpha, nothing do do here.
3790 componentAlphaBounds
= aTransform
->TransformRect(
3791 componentAlphaBounds
, aState
->mAppUnitsPerDevPixel
);
3794 const nsIntRect pixelBounds
=
3795 aState
->ScaleToOutsidePixels(componentAlphaBounds
, false);
3797 const nsIntRect visibleRect
= pixelBounds
.Intersect(aVisibleRect
);
3799 if (!mOpaqueRegion
.Contains(visibleRect
)) {
3800 nsRect buildingRect
= aItem
->GetBuildingRect();
3804 aTransform
->TransformRect(buildingRect
, aState
->mAppUnitsPerDevPixel
);
3807 const nsRect tightBounds
= componentAlphaBounds
.Intersect(buildingRect
);
3809 if (IsItemAreaInWindowOpaqueRegion(aState
->mBuilder
, aItem
, tightBounds
)) {
3810 mNeedComponentAlpha
= true;
3812 // There is no opaque background below the item, disable component alpha.
3813 aItem
->DisableComponentAlpha();
3821 UniquePtr
<InactiveLayerData
> PaintedLayerData::CreateInactiveLayerData(
3822 ContainerState
* aState
, nsPaintedDisplayItem
* aItem
,
3823 DisplayItemData
* aData
) {
3824 RefPtr
<BasicLayerManager
> tempManager
;
3826 tempManager
= aData
->InactiveManager();
3829 tempManager
= new BasicLayerManager(BasicLayerManager::BLM_INACTIVE
);
3831 UniquePtr
<InactiveLayerData
> data
= MakeUnique
<InactiveLayerData
>();
3832 data
->mLayerManager
= tempManager
;
3834 FrameLayerBuilder
* layerBuilder
= new FrameLayerBuilder();
3835 // Ownership of layerBuilder is passed to tempManager.
3836 layerBuilder
->Init(aState
->Builder(), tempManager
, this, true,
3839 tempManager
->BeginTransaction();
3840 if (aState
->LayerBuilder()->GetRetainingLayerManager()) {
3841 layerBuilder
->DidBeginRetainedLayerTransaction(tempManager
);
3844 data
->mProps
= LayerProperties::CloneFrom(tempManager
->GetRoot());
3845 data
->mLayer
= aItem
->BuildLayer(aState
->Builder(), tempManager
,
3846 ContainerLayerParameters());
3850 void PaintedLayerData::Accumulate(ContainerState
* aState
, nsDisplayItem
* aItem
,
3851 const nsIntRect
& aVisibleRect
,
3852 const nsRect
& aContentRect
,
3853 const DisplayItemClip
& aClip
,
3854 LayerState aLayerState
, nsDisplayList
* aList
,
3855 DisplayItemEntryType aType
,
3856 nsTArray
<size_t>& aOpacityIndices
,
3857 const RefPtr
<TransformClipNode
>& aTransform
) {
3858 if (aItem
->HasHitTestInfo()) {
3859 AccumulateHitTestItem(aState
, aItem
, aClip
, aTransform
);
3862 if (aItem
->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
) {
3863 // These items only carry hit test information.
3867 nsPaintedDisplayItem
* item
= aItem
->AsPaintedDisplayItem();
3868 // If aItem is nullptr, the cast to nsPaintedDisplayItem failed.
3869 MOZ_ASSERT(item
, "Can only accumulate display items that are painted!");
3871 FLB_LOG_PAINTED_LAYER_DECISION(
3872 this, "Accumulating dp=%s(%p), f=%p against pld=%p\n", item
->Name(), item
,
3873 item
->Frame(), this);
3875 const bool hasOpacity
= aOpacityIndices
.Length() > 0;
3876 UpdateEffectStatus(aType
, aOpacityIndices
);
3878 const DisplayItemClip
* oldClip
= mItemClip
;
3881 const bool isMerged
=
3882 item
->AsDisplayWrapList() && item
->AsDisplayWrapList()->HasMergedFrames();
3884 if (IsEffectEndMarker(aType
)) {
3885 mAssignedDisplayItems
.emplace_back(item
, aLayerState
, nullptr, aContentRect
,
3886 aType
, hasOpacity
, aTransform
, isMerged
);
3891 (oldClip
== mItemClip
) || (oldClip
&& *oldClip
== *mItemClip
);
3893 DisplayItemData
* oldData
= aState
->mLayerBuilder
->GetOldLayerForFrame(
3894 item
->Frame(), item
->GetPerFrameKey());
3896 mAssignedDisplayItems
.emplace_back(item
, aLayerState
, oldData
, aContentRect
,
3897 aType
, hasOpacity
, aTransform
, isMerged
);
3899 if (aLayerState
!= LayerState::LAYER_NONE
) {
3900 FLB_LOG_PAINTED_LAYER_DECISION(this, "Creating nested FLB for item %p\n",
3902 mAssignedDisplayItems
.back().mInactiveLayerData
=
3903 CreateInactiveLayerData(aState
, item
, oldData
);
3906 if (aState
->mBuilder
->NeedToForceTransparentSurfaceForItem(item
)) {
3907 mForceTransparentSurface
= true;
3910 if (aState
->mParameters
.mDisableSubpixelAntialiasingInDescendants
) {
3911 // Disable component alpha.
3912 // Note that the transform (if any) on the PaintedLayer is always an integer
3913 // translation so we don't have to factor that in here.
3914 item
->DisableComponentAlpha();
3916 const bool needsComponentAlpha
=
3917 SetupComponentAlpha(aState
, item
, aVisibleRect
, aTransform
);
3919 if (needsComponentAlpha
) {
3920 // This display item needs background copy when pushing opacity group.
3921 for (size_t i
: aOpacityIndices
) {
3922 AssignedDisplayItem
& item
= mAssignedDisplayItems
[i
];
3923 MOZ_ASSERT(item
.mType
== DisplayItemEntryType::PushOpacity
||
3924 item
.mType
== DisplayItemEntryType::PushOpacityWithBg
);
3925 item
.mType
= DisplayItemEntryType::PushOpacityWithBg
;
3930 if (aTransform
&& aType
== DisplayItemEntryType::Item
) {
3931 // Bounds transformed with axis-aligned transforms could be included in the
3932 // opaque region calculations. For simplicity, this is currently not done.
3936 if (!mIsSolidColorInVisibleRegion
&& mOpaqueRegion
.Contains(aVisibleRect
) &&
3937 mVisibleRegion
.Contains(aVisibleRect
) && !mImage
) {
3938 // A very common case! Most pages have a PaintedLayer with the page
3939 // background (opaque) visible and most or all of the page content over the
3940 // top of that background.
3941 // The rest of this method won't do anything. mVisibleRegion and
3942 // mOpaqueRegion don't need updating. mVisibleRegion contains aVisibleRect
3943 // already, mOpaqueRegion contains aVisibleRect and therefore whatever the
3944 // opaque region of the item is. mVisibleRegion must contain mOpaqueRegion
3945 // and therefore aVisibleRect.
3949 nsIntRegion opaquePixels
;
3951 // Active opacity means no opaque pixels.
3953 opaquePixels
= aState
->ComputeOpaqueRect(
3954 item
, mAnimatedGeometryRoot
, mASR
, aClip
, aList
, &mHideAllLayersBelow
,
3955 &mOpaqueForAnimatedGeometryRootParent
);
3956 opaquePixels
.AndWith(aVisibleRect
);
3959 /* Mark as available for conversion to image layer if this is a nsDisplayImage
3960 * and it's the only thing visible in this layer.
3962 if (nsIntRegion(aVisibleRect
).Contains(mVisibleRegion
) &&
3963 opaquePixels
.Contains(mVisibleRegion
) &&
3964 item
->SupportsOptimizingToImage()) {
3965 mImage
= static_cast<nsDisplayImageContainer
*>(item
);
3966 FLB_LOG_PAINTED_LAYER_DECISION(
3967 this, " Tracking image: nsDisplayImageContainer covers the layer\n");
3968 } else if (mImage
) {
3969 FLB_LOG_PAINTED_LAYER_DECISION(this, " No longer tracking image\n");
3973 bool isFirstVisibleItem
= mVisibleRegion
.IsEmpty();
3975 Maybe
<nscolor
> uniformColor
;
3977 uniformColor
= item
->IsUniform(aState
->mBuilder
);
3980 // Some display items have to exist (so they can set forceTransparentSurface
3981 // below) but don't draw anything. They'll return true for isUniform but
3982 // a color with opacity 0.
3983 if (!uniformColor
|| NS_GET_A(*uniformColor
) > 0) {
3984 // Make sure that the visible area is covered by uniform pixels. In
3985 // particular this excludes cases where the edges of the item are not
3986 // pixel-aligned (thus the item will not be truly uniform).
3989 nsRect bounds
= item
->GetBounds(aState
->mBuilder
, &snap
);
3990 if (!aState
->ScaleToInsidePixels(bounds
, snap
).Contains(aVisibleRect
)) {
3991 uniformColor
= Nothing();
3992 FLB_LOG_PAINTED_LAYER_DECISION(
3993 this, " Display item does not cover the visible rect\n");
3997 if (isFirstVisibleItem
) {
3998 // This color is all we have
3999 mSolidColor
= *uniformColor
;
4000 mIsSolidColorInVisibleRegion
= true;
4001 } else if (mIsSolidColorInVisibleRegion
&&
4002 mVisibleRegion
.IsEqual(nsIntRegion(aVisibleRect
)) &&
4004 // we can just blend the colors together
4005 mSolidColor
= NS_ComposeColors(mSolidColor
, *uniformColor
);
4007 FLB_LOG_PAINTED_LAYER_DECISION(
4008 this, " Layer not a solid color: Can't blend colors togethers\n");
4009 mIsSolidColorInVisibleRegion
= false;
4012 FLB_LOG_PAINTED_LAYER_DECISION(this,
4013 " Layer is not a solid color: Display "
4014 "item is not uniform over the visible "
4016 mIsSolidColorInVisibleRegion
= false;
4019 mVisibleRegion
.Or(mVisibleRegion
, aVisibleRect
);
4020 mVisibleRegion
.SimplifyOutward(4);
4023 if (!opaquePixels
.IsEmpty()) {
4024 for (auto iter
= opaquePixels
.RectIter(); !iter
.Done(); iter
.Next()) {
4025 // We don't use SimplifyInward here since it's not defined exactly
4026 // what it will discard. For our purposes the most important case
4027 // is a large opaque background at the bottom of z-order (e.g.,
4028 // a canvas background), so we need to make sure that the first rect
4029 // we see doesn't get discarded.
4031 tmp
.Or(mOpaqueRegion
, iter
.Get());
4032 // Opaque display items in chrome documents whose window is partially
4033 // transparent are always added to the opaque region. This helps ensure
4034 // that we get as much subpixel-AA as possible in the chrome.
4035 if (tmp
.GetNumRects() <= 4 || item
->Frame()->PresContext()->IsChrome()) {
4036 mOpaqueRegion
= std::move(tmp
);
4042 nsRegion
PaintedLayerData::CombinedTouchActionRegion() {
4044 result
.Or(mHorizontalPanRegion
, mVerticalPanRegion
);
4045 result
.OrWith(mNoActionRegion
);
4049 void PaintedLayerData::AccumulateHitTestItem(ContainerState
* aState
,
4050 nsDisplayItem
* aItem
,
4051 const DisplayItemClip
& aClip
,
4052 TransformClipNode
* aTransform
) {
4053 const auto& hitTestInfo
= aItem
->GetHitTestInfo();
4054 nsRect area
= hitTestInfo
.Area();
4055 const CompositorHitTestInfo
& flags
= hitTestInfo
.Info();
4057 FLB_LOG_PAINTED_LAYER_DECISION(
4059 "Accumulating hit test info %p against pld=%p, "
4060 "area: [%d, %d, %d, %d], flags: 0x%x]\n",
4061 aItem
, this, area
.x
, area
.y
, area
.width
, area
.height
, flags
.serialize());
4063 area
= aClip
.ApplyNonRoundedIntersection(area
);
4066 area
= aTransform
->TransformRect(area
, aState
->mAppUnitsPerDevPixel
);
4069 if (area
.IsEmpty()) {
4070 FLB_LOG_PAINTED_LAYER_DECISION(
4071 this, "Discarded empty hit test info %p for pld=%p\n", aItem
, this);
4075 bool hasRoundedCorners
= aClip
.GetRoundedRectCount() > 0;
4077 // use the NS_FRAME_SIMPLE_EVENT_REGIONS to avoid calling the slightly
4078 // expensive HasNonZeroCorner function if we know from a previous run that
4079 // the frame has zero corners.
4080 nsIFrame
* frame
= aItem
->Frame();
4081 bool simpleRegions
= frame
->HasAnyStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS
);
4082 if (!simpleRegions
) {
4083 if (nsLayoutUtils::HasNonZeroCorner(frame
->StyleBorder()->mBorderRadius
)) {
4084 hasRoundedCorners
= true;
4086 frame
->AddStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS
);
4090 if (hasRoundedCorners
|| frame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
4091 mMaybeHitRegion
.OrWith(area
);
4093 mHitRegion
.OrWith(area
);
4096 const auto dtcFlags
= flags
& CompositorHitTestDispatchToContent
;
4097 if (!dtcFlags
.isEmpty()) {
4098 mDispatchToContentHitRegion
.OrWith(area
);
4100 if (flags
.contains(CompositorHitTestFlags::eRequiresTargetConfirmation
)) {
4101 mDTCRequiresTargetConfirmation
= true;
4105 const auto touchFlags
= flags
& CompositorHitTestTouchActionMask
;
4106 if (!touchFlags
.isEmpty()) {
4107 // If there are multiple touch-action areas, there are multiple elements
4108 // with touch-action properties. We don't know what the relationship is
4109 // between those elements in terms of DOM ancestry, and so we don't know how
4110 // to combine the regions properly. Instead, we just add all the areas to
4111 // the dispatch-to-content region, so that the APZ knows to check with the
4112 // main thread. See bug 1286957.
4113 if (mCollapsedTouchActions
) {
4114 mDispatchToContentHitRegion
.OrWith(area
);
4115 } else if (touchFlags
== CompositorHitTestTouchActionMask
) {
4116 // everything was disabled, so touch-action:none
4117 mNoActionRegion
.OrWith(area
);
4119 // The event regions code does not store enough information to actually
4120 // represent all the different states. Prior to the introduction of
4121 // CompositorHitTestInfo here in bug 1389149, the following two cases
4122 // were effectively getting collapsed:
4123 // (1) touch-action: auto
4124 // (2) touch-action: manipulation
4125 // In both of these cases, none of {mNoActionRegion, mHorizontalPanRegion,
4126 // mVerticalPanRegion} were modified, and so the fact that case (2) should
4127 // have prevented double-tap-zooming was getting lost.
4128 // With CompositorHitTestInfo we can now represent that case correctly,
4129 // but only if we use CompositorHitTestInfo all the way to the compositor
4130 // (i.e. in the WebRender-enabled case). In the non-WebRender case where
4131 // we still use the event regions, we must collapse these two cases back
4132 // together. Or add another region to the event regions to fix this
4135 CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled
) {
4136 if (!flags
.contains(CompositorHitTestFlags::eTouchActionPanXDisabled
)) {
4138 mHorizontalPanRegion
.OrWith(area
);
4140 if (!flags
.contains(CompositorHitTestFlags::eTouchActionPanYDisabled
)) {
4142 mVerticalPanRegion
.OrWith(area
);
4145 // the touch-action: manipulation case described above. To preserve the
4146 // existing behaviour, don't touch either mHorizontalPanRegion or
4147 // mVerticalPanRegion
4152 if (!mCollapsedTouchActions
) {
4153 // If there are multiple touch-action areas, there are multiple elements
4154 // with touch-action properties. We don't know what the relationship is
4155 // between those elements in terms of DOM ancestry, and so we don't know how
4156 // to combine the regions properly. Instead, we just add all the areas to
4157 // the dispatch-to-content region, so that the APZ knows to check with the
4158 // main thread. See bug 1286957.
4159 const int alreadyHadRegions
= mNoActionRegion
.GetNumRects() +
4160 mHorizontalPanRegion
.GetNumRects() +
4161 mVerticalPanRegion
.GetNumRects();
4163 if (alreadyHadRegions
> 1) {
4164 mDispatchToContentHitRegion
.OrWith(CombinedTouchActionRegion());
4165 mNoActionRegion
.SetEmpty();
4166 mHorizontalPanRegion
.SetEmpty();
4167 mVerticalPanRegion
.SetEmpty();
4168 mCollapsedTouchActions
= true;
4172 // Avoid quadratic performance as a result of the region growing to include
4173 // and arbitrarily large number of rects, which can happen on some pages.
4174 mMaybeHitRegion
.SimplifyOutward(8);
4175 mDispatchToContentHitRegion
.SimplifyOutward(8);
4177 HitRegionsUpdated();
4180 void PaintedLayerData::HitRegionsUpdated() {
4181 // Calculate scaled versions of the bounds of mHitRegion and mMaybeHitRegion
4182 // for quick access in FindPaintedLayerFor().
4183 mScaledHitRegionBounds
= mState
->ScaleToOutsidePixels(mHitRegion
.GetBounds());
4184 mScaledMaybeHitRegionBounds
=
4185 mState
->ScaleToOutsidePixels(mMaybeHitRegion
.GetBounds());
4188 void ContainerState::NewPaintedLayerData(
4189 PaintedLayerData
* aData
, AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
4190 const ActiveScrolledRoot
* aASR
, const DisplayItemClipChain
* aClipChain
,
4191 const ActiveScrolledRoot
* aScrollMetadataASR
, const nsPoint
& aTopLeft
,
4192 const nsIFrame
* aReferenceFrame
, const bool aBackfaceHidden
) {
4193 aData
->mState
= this;
4194 aData
->mAnimatedGeometryRoot
= aAnimatedGeometryRoot
;
4196 aData
->mClipChain
= aClipChain
;
4197 aData
->mAnimatedGeometryRootOffset
= aTopLeft
;
4198 aData
->mReferenceFrame
= aReferenceFrame
;
4199 aData
->mBackfaceHidden
= aBackfaceHidden
;
4201 aData
->mNewChildLayersIndex
= mNewChildLayers
.Length();
4202 NewLayerEntry
* newLayerEntry
= mNewChildLayers
.AppendElement();
4203 newLayerEntry
->mAnimatedGeometryRoot
= aAnimatedGeometryRoot
;
4204 newLayerEntry
->mASR
= aASR
;
4205 newLayerEntry
->mScrollMetadataASR
= aScrollMetadataASR
;
4206 newLayerEntry
->mClipChain
= aClipChain
;
4207 // newLayerEntry->mOpaqueRegion is filled in later from
4208 // paintedLayerData->mOpaqueRegion, if necessary.
4210 // Allocate another entry for this layer's optimization to
4211 // ColorLayer/ImageLayer
4212 mNewChildLayers
.AppendElement();
4215 #ifdef MOZ_DUMP_PAINTING
4216 static void DumpPaintedImage(nsDisplayItem
* aItem
, SourceSurface
* aSurface
) {
4217 nsCString
string(aItem
->Name());
4219 string
.AppendInt((uint64_t)aItem
);
4220 fprintf_stderr(gfxUtils::sDumpPaintFile
, "<script>array[\"%s\"]=\"",
4221 string
.BeginReading());
4222 gfxUtils::DumpAsDataURI(aSurface
, gfxUtils::sDumpPaintFile
);
4223 fprintf_stderr(gfxUtils::sDumpPaintFile
, "\";</script>\n");
4227 static void PaintInactiveLayer(nsDisplayListBuilder
* aBuilder
,
4228 LayerManager
* aManager
, nsDisplayItem
* aItem
,
4229 gfxContext
* aContext
, gfxContext
* aCtx
) {
4230 // This item has an inactive layer. Render it to a PaintedLayer
4231 // using a temporary BasicLayerManager.
4232 BasicLayerManager
* basic
= static_cast<BasicLayerManager
*>(aManager
);
4233 RefPtr
<gfxContext
> context
= aContext
;
4234 #ifdef MOZ_DUMP_PAINTING
4235 int32_t appUnitsPerDevPixel
= AppUnitsPerDevPixel(aItem
);
4236 nsIntRect itemVisibleRect
=
4237 aItem
->GetPaintRect().ToOutsidePixels(appUnitsPerDevPixel
);
4239 RefPtr
<DrawTarget
> tempDT
;
4240 if (gfxEnv::DumpPaint()) {
4241 tempDT
= gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
4242 itemVisibleRect
.Size(), SurfaceFormat::B8G8R8A8
);
4244 context
= gfxContext::CreateOrNull(tempDT
);
4246 // Leave this as crash, it's in the debugging code, we want to know
4247 gfxDevCrash(LogReason::InvalidContext
)
4248 << "PaintInactive context problem " << gfx::hexa(tempDT
);
4252 Matrix::Translation(-itemVisibleRect
.x
, -itemVisibleRect
.y
));
4256 basic
->BeginTransaction();
4257 basic
->SetTarget(context
);
4259 if (aItem
->GetType() == DisplayItemType::TYPE_MASK
) {
4260 static_cast<nsDisplayMasksAndClipPaths
*>(aItem
)->PaintAsLayer(aBuilder
,
4262 if (basic
->InTransaction()) {
4263 basic
->AbortTransaction();
4265 } else if (aItem
->GetType() == DisplayItemType::TYPE_FILTER
) {
4266 static_cast<nsDisplayFilters
*>(aItem
)->PaintAsLayer(aBuilder
, aCtx
, basic
);
4267 if (basic
->InTransaction()) {
4268 basic
->AbortTransaction();
4271 basic
->EndTransaction(FrameLayerBuilder::DrawPaintedLayer
, aBuilder
);
4273 FrameLayerBuilder
* builder
= static_cast<FrameLayerBuilder
*>(
4274 basic
->GetUserData(&gLayerManagerLayerBuilder
));
4276 builder
->DidEndTransaction();
4279 basic
->SetTarget(nullptr);
4281 #ifdef MOZ_DUMP_PAINTING
4282 if (gfxEnv::DumpPaint() && tempDT
) {
4283 RefPtr
<SourceSurface
> surface
= tempDT
->Snapshot();
4284 DumpPaintedImage(aItem
, surface
);
4286 DrawTarget
* drawTarget
= aContext
->GetDrawTarget();
4287 Rect
rect(itemVisibleRect
.x
, itemVisibleRect
.y
, itemVisibleRect
.width
,
4288 itemVisibleRect
.height
);
4289 drawTarget
->DrawSurface(surface
, rect
, Rect(Point(0, 0), rect
.Size()));
4291 aItem
->SetPainted();
4296 nsRect
ContainerState::GetDisplayPortForAnimatedGeometryRoot(
4297 AnimatedGeometryRoot
* aAnimatedGeometryRoot
) {
4298 if (mLastDisplayPortAGR
== aAnimatedGeometryRoot
) {
4299 return mLastDisplayPortRect
;
4302 mLastDisplayPortAGR
= aAnimatedGeometryRoot
;
4304 nsIScrollableFrame
* sf
=
4305 nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot
);
4306 if (sf
== nullptr ||
4307 nsLayoutUtils::UsesAsyncScrolling(*aAnimatedGeometryRoot
)) {
4308 mLastDisplayPortRect
= nsRect();
4309 return mLastDisplayPortRect
;
4312 bool usingDisplayport
= DisplayPortUtils::GetDisplayPort(
4313 (*aAnimatedGeometryRoot
)->GetContent(), &mLastDisplayPortRect
,
4314 DisplayPortOptions().With(DisplayportRelativeTo::ScrollFrame
));
4315 if (!usingDisplayport
) {
4316 // No async scrolling, so all that matters is that the layer contents
4317 // cover the scrollport.
4318 mLastDisplayPortRect
= sf
->GetScrollPortRect();
4320 nsIFrame
* scrollFrame
= do_QueryFrame(sf
);
4321 mLastDisplayPortRect
+=
4322 scrollFrame
->GetOffsetToCrossDoc(mContainerReferenceFrame
);
4323 return mLastDisplayPortRect
;
4326 nsIntRegion
ContainerState::ComputeOpaqueRect(
4327 nsDisplayItem
* aItem
, AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
4328 const ActiveScrolledRoot
* aASR
, const DisplayItemClip
& aClip
,
4329 nsDisplayList
* aList
, bool* aHideAllLayersBelow
,
4330 bool* aOpaqueForAnimatedGeometryRootParent
) {
4332 nsRegion opaque
= aItem
->GetOpaqueRegion(mBuilder
, &snapOpaque
);
4333 MOZ_ASSERT(!opaque
.IsComplex());
4334 if (opaque
.IsEmpty()) {
4335 return nsIntRegion();
4338 nsIntRegion opaquePixels
;
4339 nsRegion opaqueClipped
;
4340 for (auto iter
= opaque
.RectIter(); !iter
.Done(); iter
.Next()) {
4341 opaqueClipped
.Or(opaqueClipped
,
4342 aClip
.ApproximateIntersectInward(iter
.Get()));
4344 if (aAnimatedGeometryRoot
== mContainerAnimatedGeometryRoot
&&
4345 aASR
== mContainerASR
&& opaqueClipped
.Contains(mContainerBounds
)) {
4346 *aHideAllLayersBelow
= true;
4347 aList
->SetIsOpaque();
4349 // Add opaque areas to the "exclude glass" region. Only do this when our
4350 // container layer is going to be the rootmost layer, otherwise transforms
4351 // etc will mess us up (and opaque contributions from other containers are
4353 if (!nsLayoutUtils::GetCrossDocParentFrameInProcess(mContainerFrame
)) {
4354 mBuilder
->AddWindowOpaqueRegion(aItem
->Frame(), opaqueClipped
.GetBounds());
4356 opaquePixels
= ScaleRegionToInsidePixels(opaqueClipped
, snapOpaque
);
4358 if (IsInInactiveLayer()) {
4359 return opaquePixels
;
4362 const nsRect
& displayport
=
4363 GetDisplayPortForAnimatedGeometryRoot(aAnimatedGeometryRoot
);
4364 if (!displayport
.IsEmpty() &&
4365 opaquePixels
.Contains(ScaleRegionToNearestPixels(displayport
))) {
4366 *aOpaqueForAnimatedGeometryRootParent
= true;
4368 return opaquePixels
;
4371 Maybe
<size_t> ContainerState::SetupMaskLayerForScrolledClip(
4372 Layer
* aLayer
, const DisplayItemClip
& aClip
) {
4373 if (aClip
.GetRoundedRectCount() > 0) {
4374 Maybe
<size_t> maskLayerIndex
= Some(aLayer
->GetAncestorMaskLayerCount());
4375 if (RefPtr
<Layer
> maskLayer
=
4376 CreateMaskLayer(aLayer
, aClip
, maskLayerIndex
)) {
4377 aLayer
->AddAncestorMaskLayer(maskLayer
);
4378 return maskLayerIndex
;
4380 // Fall through to |return Nothing()|.
4385 static const ActiveScrolledRoot
* GetASRForPerspective(
4386 const ActiveScrolledRoot
* aASR
, nsIFrame
* aPerspectiveFrame
) {
4387 for (const ActiveScrolledRoot
* asr
= aASR
; asr
; asr
= asr
->mParent
) {
4388 nsIFrame
* scrolledFrame
= asr
->mScrollableFrame
->GetScrolledFrame();
4389 // In OOP documents, the root scrollable frame of the in-process root
4390 // document is always active, so using IsAncestorFrameCrossDocInProcess
4391 // should be fine here.
4392 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(scrolledFrame
,
4393 aPerspectiveFrame
)) {
4400 static CSSMaskLayerUserData
* GetCSSMaskLayerUserData(Layer
* aMaskLayer
) {
4405 return static_cast<CSSMaskLayerUserData
*>(
4406 aMaskLayer
->GetUserData(&gCSSMaskLayerUserData
));
4409 static void SetCSSMaskLayerUserData(Layer
* aMaskLayer
) {
4410 MOZ_ASSERT(aMaskLayer
);
4412 aMaskLayer
->SetUserData(&gCSSMaskLayerUserData
, new CSSMaskLayerUserData());
4415 void ContainerState::SetupMaskLayerForCSSMask(
4416 Layer
* aLayer
, nsDisplayMasksAndClipPaths
* aMaskItem
) {
4417 RefPtr
<ImageLayer
> maskLayer
= CreateOrRecycleMaskImageLayerFor(
4418 MaskLayerKey(aLayer
, Nothing()), GetCSSMaskLayerUserData
,
4419 SetCSSMaskLayerUserData
);
4420 CSSMaskLayerUserData
* oldUserData
= GetCSSMaskLayerUserData(maskLayer
.get());
4421 MOZ_ASSERT(oldUserData
);
4424 nsRect bounds
= aMaskItem
->GetBounds(mBuilder
, &snap
);
4425 nsIntRect itemRect
= ScaleToOutsidePixels(bounds
, snap
);
4427 // Setup mask layer offset.
4428 // We do not repaint mask for mask position change, so update base transform
4429 // each time is required.
4431 matrix
.PreTranslate(itemRect
.x
, itemRect
.y
, 0);
4432 matrix
.PreTranslate(mParameters
.mOffset
.x
, mParameters
.mOffset
.y
, 0);
4433 maskLayer
->SetBaseTransform(matrix
);
4435 nsPoint maskLayerOffset
= aMaskItem
->ToReferenceFrame() - bounds
.TopLeft();
4437 CSSMaskLayerUserData
newUserData(aMaskItem
->Frame(), itemRect
,
4440 if (!aMaskItem
->IsInvalid(dirtyRect
) && *oldUserData
== newUserData
) {
4441 aLayer
->SetMaskLayer(maskLayer
);
4445 int32_t maxSize
= mManager
->GetMaxTextureSize();
4446 IntSize
surfaceSize(std::min(itemRect
.width
, maxSize
),
4447 std::min(itemRect
.height
, maxSize
));
4449 if (surfaceSize
.IsEmpty()) {
4450 // Return early if we know that the size of this mask surface is empty.
4454 MaskImageData
imageData(surfaceSize
, mManager
);
4455 RefPtr
<DrawTarget
> dt
= imageData
.CreateDrawTarget();
4456 if (!dt
|| !dt
->IsValid()) {
4457 NS_WARNING("Could not create DrawTarget for mask layer.");
4461 RefPtr
<gfxContext
> maskCtx
= gfxContext::CreateOrNull(dt
);
4462 maskCtx
->SetMatrix(Matrix::Translation(-itemRect
.TopLeft()));
4464 gfxMatrix::Scaling(mParameters
.mXScale
, mParameters
.mYScale
));
4466 bool isPaintFinished
= aMaskItem
->PaintMask(mBuilder
, maskCtx
);
4468 RefPtr
<ImageContainer
> imgContainer
=
4469 imageData
.CreateImageAndImageContainer();
4470 if (!imgContainer
) {
4473 maskLayer
->SetContainer(imgContainer
);
4475 if (isPaintFinished
) {
4476 *oldUserData
= std::move(newUserData
);
4478 aLayer
->SetMaskLayer(maskLayer
);
4481 static bool IsScrollThumbLayer(nsDisplayItem
* aItem
) {
4482 return aItem
->GetType() == DisplayItemType::TYPE_OWN_LAYER
&&
4483 static_cast<nsDisplayOwnLayer
*>(aItem
)->IsScrollThumbLayer();
4486 template <typename ClearFn
, typename SelectFn
>
4487 static void ProcessDisplayItemMarker(DisplayItemEntryType aMarker
,
4488 ClearFn ClearLayerSelectionIfNeeded
,
4489 SelectFn SelectLayerIfNeeded
) {
4491 case DisplayItemEntryType::PushTransform
:
4492 case DisplayItemEntryType::PushOpacity
:
4493 SelectLayerIfNeeded();
4495 case DisplayItemEntryType::PopTransform
:
4496 case DisplayItemEntryType::PopOpacity
:
4497 ClearLayerSelectionIfNeeded();
4504 * Iterate through the non-clip items in aList and its descendants.
4505 * For each item we compute the effective clip rect. Each item is assigned
4506 * to a layer. We invalidate the areas in PaintedLayers where an item
4507 * has moved from one PaintedLayer to another. Also,
4508 * aState->mInvalidPaintedContent is invalidated in every PaintedLayer.
4509 * We set the clip rect for items that generated their own layer, and
4510 * create a mask layer to do any rounded rect clipping.
4511 * (PaintedLayers don't need a clip rect on the layer, we clip the items
4512 * individually when we draw them.)
4513 * We set the visible rect for all layers, although the actual setting
4514 * of visible rects for some PaintedLayers is deferred until the calling
4515 * of ContainerState::Finish.
4517 void ContainerState::ProcessDisplayItems(nsDisplayList
* aList
) {
4518 AUTO_PROFILER_LABEL("ContainerState::ProcessDisplayItems",
4519 GRAPHICS_LayerBuilding
);
4520 PerfStats::AutoMetricRecording
<PerfStats::Metric::LayerBuilding
>
4523 nsPoint
topLeft(0, 0);
4525 int32_t maxLayers
= StaticPrefs::layers_max_active();
4528 if (!mManager
->IsWidgetLayerManager()) {
4529 mPaintedLayerDataTree
.InitializeForInactiveLayer(
4530 mContainerAnimatedGeometryRoot
);
4533 AnimatedGeometryRoot
* lastAnimatedGeometryRoot
= nullptr;
4534 nsPoint lastTopLeft
;
4536 // Tracks the PaintedLayerData that the item will be accumulated in, if it is
4538 PaintedLayerData
* selectedLayer
= nullptr;
4539 AutoTArray
<size_t, 2> opacityIndices
;
4541 // AGR and ASR for the container item that was flattened.
4542 AnimatedGeometryRoot
* containerAGR
= nullptr;
4543 const ActiveScrolledRoot
* containerASR
= nullptr;
4544 nsIFrame
* containerReferenceFrame
= nullptr;
4545 RefPtr
<TransformClipNode
> transformNode
= nullptr;
4547 const auto InTransform
= [&]() { return transformNode
; };
4549 const auto InOpacity
= [&]() {
4550 return selectedLayer
&& opacityIndices
.Length() > 0;
4553 FLBDisplayListIterator
iter(mBuilder
, aList
, this);
4554 while (iter
.HasNext()) {
4555 DisplayItemEntry e
= iter
.GetNextEntry();
4556 DisplayItemEntryType marker
= e
.mType
;
4557 nsDisplayItem
* item
= e
.mItem
;
4559 DisplayItemType itemType
= item
->GetType();
4560 const bool isHitTestItem
=
4561 itemType
== DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
;
4563 NS_ASSERTION(mAppUnitsPerDevPixel
== AppUnitsPerDevPixel(item
),
4564 "items in a container layer should all have the same app "
4565 "units per dev pixel");
4567 if (mBuilder
->NeedToForceTransparentSurfaceForItem(item
)) {
4568 aList
->SetNeedsTransparentSurface();
4571 LayerState layerState
= LayerState::LAYER_NONE
;
4572 if (marker
== DisplayItemEntryType::Item
) {
4573 layerState
= item
->GetLayerState(mBuilder
, mManager
, mParameters
);
4575 if (layerState
== LayerState::LAYER_INACTIVE
&&
4576 nsDisplayItem::ForceActiveLayers()) {
4577 layerState
= LayerState::LAYER_ACTIVE
;
4581 AnimatedGeometryRoot
* itemAGR
= item
->GetAnimatedGeometryRoot();
4582 const ActiveScrolledRoot
* itemASR
= item
->GetActiveScrolledRoot();
4583 const DisplayItemClipChain
* itemClipChain
= item
->GetClipChain();
4584 const DisplayItemClipChain
* layerClipChain
= nullptr;
4585 const DisplayItemClip
* itemClipPtr
= &item
->GetClip();
4587 const bool inEffect
= InTransform() || InOpacity();
4588 if (mManager
->IsWidgetLayerManager() && !inEffect
) {
4589 if (itemClipChain
&& itemClipChain
->mASR
== itemASR
&&
4590 itemType
!= DisplayItemType::TYPE_STICKY_POSITION
) {
4591 layerClipChain
= itemClipChain
->mParent
;
4593 layerClipChain
= itemClipChain
;
4596 // Inside a flattened effect or inactive layer, use container AGR and ASR.
4597 itemAGR
= inEffect
? containerAGR
: mContainerAnimatedGeometryRoot
;
4598 itemASR
= inEffect
? containerASR
: mContainerASR
;
4600 if (!IsEffectEndMarker(marker
)) {
4601 // No need to fuse clip chain for effect end markers, since it was
4602 // already done for effect start markers.
4603 item
->FuseClipChainUpTo(mBuilder
, itemASR
);
4604 itemClipChain
= item
->GetClipChain();
4607 itemClipPtr
= itemClipChain
? &itemClipChain
->mClip
: nullptr;
4610 const DisplayItemClip
& itemClip
=
4611 itemClipPtr
? *itemClipPtr
: DisplayItemClip::NoClip();
4613 if (inEffect
&& marker
== DisplayItemEntryType::Item
) {
4614 // Fast-path for items inside flattened inactive layers. This works
4615 // because the layer state of the item cannot be active, otherwise the
4616 // parent item would not have been flattened.
4617 MOZ_ASSERT(selectedLayer
);
4618 selectedLayer
->Accumulate(this, item
, nsIntRect(), nsRect(), itemClip
,
4619 layerState
, aList
, marker
, opacityIndices
,
4624 // Items outside of flattened effects and non-item markers inside flattened
4625 // effects are processed here.
4626 MOZ_ASSERT(!inEffect
|| (marker
!= DisplayItemEntryType::Item
));
4628 if (itemAGR
== lastAnimatedGeometryRoot
) {
4629 topLeft
= lastTopLeft
;
4631 lastTopLeft
= topLeft
=
4632 (*itemAGR
)->GetOffsetToCrossDoc(mContainerReferenceFrame
);
4633 lastAnimatedGeometryRoot
= itemAGR
;
4636 const ActiveScrolledRoot
* scrollMetadataASR
=
4638 ? ActiveScrolledRoot::PickDescendant(itemASR
, layerClipChain
->mASR
)
4641 const bool prerenderedTransform
=
4642 itemType
== DisplayItemType::TYPE_TRANSFORM
&&
4643 static_cast<nsDisplayTransform
*>(item
)->MayBeAnimated(mBuilder
);
4646 nsRect itemContent
= isHitTestItem
? item
->GetHitTestInfo().Area()
4647 : item
->GetBounds(mBuilder
, &snap
);
4649 nsIntRect itemDrawRect
= ScaleToOutsidePixels(itemContent
, snap
);
4650 ParentLayerIntRect clipRect
;
4651 if (itemClip
.HasClip()) {
4652 const nsRect
& itemClipRect
= itemClip
.GetClipRect();
4653 itemContent
.IntersectRect(itemContent
, itemClipRect
);
4654 clipRect
= ViewAs
<ParentLayerPixel
>(ScaleToNearestPixels(itemClipRect
));
4656 if (!prerenderedTransform
&& !IsScrollThumbLayer(item
)) {
4657 itemDrawRect
.IntersectRect(itemDrawRect
, clipRect
.ToUnknownRect());
4660 clipRect
.MoveBy(ViewAs
<ParentLayerPixel
>(mParameters
.mOffset
));
4663 if (marker
== DisplayItemEntryType::PopTransform
) {
4664 MOZ_ASSERT(transformNode
);
4665 transformNode
= transformNode
->Parent();
4668 nsRect itemVisibleRectAu
= itemContent
;
4669 if (transformNode
) {
4670 // If we are within transform, transform itemContent and itemDrawRect.
4671 MOZ_ASSERT(transformNode
);
4674 transformNode
->TransformRect(itemContent
, mAppUnitsPerDevPixel
);
4676 itemDrawRect
= transformNode
->TransformRect(itemDrawRect
);
4680 nsRect bounds
= itemContent
;
4682 if (inEffect
|| isHitTestItem
) {
4686 if (!bounds
.IsEmpty() && itemASR
!= mContainerASR
) {
4687 if (Maybe
<nsRect
> clip
=
4688 item
->GetClipWithRespectToASR(mBuilder
, mContainerASR
)) {
4689 bounds
= clip
.ref();
4693 ((nsRect
&)mAccumulatedChildBounds
)
4694 .UnionRect(mAccumulatedChildBounds
, bounds
);
4697 nsIntRect itemVisibleRect
= itemDrawRect
;
4699 // We intersect the building rect with the clipped item bounds to get a
4700 // tighter visible rect.
4701 if (!prerenderedTransform
) {
4702 nsRect itemBuildingRect
= item
->GetBuildingRect();
4704 if (transformNode
) {
4705 itemBuildingRect
= transformNode
->TransformRect(itemBuildingRect
,
4706 mAppUnitsPerDevPixel
);
4709 itemVisibleRect
= itemVisibleRect
.Intersect(
4710 ScaleToOutsidePixels(itemBuildingRect
, false));
4713 const bool forceInactive
= maxLayers
!= -1 && layerCount
>= maxLayers
;
4715 // Assign the item to a layer
4716 bool treatInactiveItemAsActive
=
4717 (layerState
== LayerState::LAYER_INACTIVE
&&
4718 mLayerBuilder
->GetContainingPaintedLayerData());
4719 if (layerState
== LayerState::LAYER_ACTIVE_FORCE
||
4720 treatInactiveItemAsActive
||
4721 (!forceInactive
&& (layerState
== LayerState::LAYER_ACTIVE_EMPTY
||
4722 layerState
== LayerState::LAYER_ACTIVE
))) {
4725 // Currently we do not support flattening effects within nested inactive
4727 MOZ_ASSERT(selectedLayer
== nullptr);
4728 MOZ_ASSERT(marker
== DisplayItemEntryType::Item
);
4730 // LayerState::LAYER_ACTIVE_EMPTY means the layer is created just for its
4731 // metadata. We should never see an empty layer with any visible content!
4733 layerState
!= LayerState::LAYER_ACTIVE_EMPTY
||
4734 itemVisibleRect
.IsEmpty(),
4735 "State is LayerState::LAYER_ACTIVE_EMPTY but visible rect is not.");
4737 // As long as the new layer isn't going to be a PaintedLayer,
4738 // InvalidateForLayerChange doesn't need the new layer pointer.
4739 // We also need to check the old data now, because BuildLayer
4740 // can overwrite it.
4741 DisplayItemData
* oldData
= mLayerBuilder
->GetOldLayerForFrame(
4742 item
->Frame(), item
->GetPerFrameKey());
4743 InvalidateForLayerChange(item
, nullptr, oldData
);
4745 // 3D-transformed layers don't necessarily draw in the order in which
4746 // they're added to their parent container layer.
4747 bool mayDrawOutOfOrder
= itemType
== DisplayItemType::TYPE_TRANSFORM
&&
4748 (item
->Combines3DTransformWithAncestors() ||
4749 item
->Frame()->Extend3DContext());
4751 // Let mPaintedLayerDataTree know about this item, so that
4752 // FindPaintedLayerFor and FindOpaqueBackgroundColor are aware of this
4753 // item, even though it's not in any PaintedLayerDataStack.
4754 // Ideally we'd only need the "else" case here and have
4755 // mPaintedLayerDataTree figure out the right clip from the animated
4756 // geometry root that we give it, but it can't easily figure about
4757 // overflow:hidden clips on ancestors just by looking at the frame.
4758 // So we'll do a little hand holding and pass the clip instead of the
4759 // visible rect for the two important cases.
4760 nscolor uniformColor
= NS_RGBA(0, 0, 0, 0);
4761 nscolor
* uniformColorPtr
=
4762 (mayDrawOutOfOrder
|| IsInInactiveLayer()) ? nullptr : &uniformColor
;
4763 nsIntRect clipRectUntyped
;
4764 nsIntRect
* clipPtr
= nullptr;
4765 if (itemClip
.HasClip()) {
4766 clipRectUntyped
= clipRect
.ToUnknownRect();
4767 clipPtr
= &clipRectUntyped
;
4770 bool isStickyNotClippedToDisplayPort
=
4771 itemType
== DisplayItemType::TYPE_STICKY_POSITION
&&
4772 !static_cast<nsDisplayStickyPosition
*>(item
)
4773 ->IsClippedToDisplayPort();
4774 bool hasScrolledClip
=
4775 layerClipChain
&& layerClipChain
->mClip
.HasClip() &&
4776 (!ActiveScrolledRoot::IsAncestor(layerClipChain
->mASR
, itemASR
) ||
4777 isStickyNotClippedToDisplayPort
);
4779 if (hasScrolledClip
) {
4780 // If the clip is scrolled, reserve just the area of the clip for
4781 // layerization, so that elements outside the clip can still merge
4782 // into the same layer.
4783 const ActiveScrolledRoot
* clipASR
= layerClipChain
->mASR
;
4784 AnimatedGeometryRoot
* clipAGR
=
4785 mBuilder
->AnimatedGeometryRootForASR(clipASR
);
4786 nsIntRect scrolledClipRect
=
4787 ScaleToNearestPixels(layerClipChain
->mClip
.GetClipRect()) +
4788 mParameters
.mOffset
;
4789 mPaintedLayerDataTree
.AddingOwnLayer(clipAGR
, &scrolledClipRect
,
4791 } else if (item
->ShouldFixToViewport(mBuilder
) && itemClip
.HasClip() &&
4792 item
->AnimatedGeometryRootForScrollMetadata() != itemAGR
&&
4793 !nsLayoutUtils::UsesAsyncScrolling(item
->Frame())) {
4794 // This is basically the same as the case above, but for the non-APZ
4795 // case. At the moment, when APZ is off, there is only the root ASR
4796 // (because scroll frames without display ports don't create ASRs) and
4797 // the whole clip chain is always just one fused clip.
4798 // Bug 1336516 aims to change that and to remove this workaround.
4799 AnimatedGeometryRoot
* clipAGR
=
4800 item
->AnimatedGeometryRootForScrollMetadata();
4801 nsIntRect scrolledClipRect
=
4802 ScaleToNearestPixels(itemClip
.GetClipRect()) + mParameters
.mOffset
;
4803 mPaintedLayerDataTree
.AddingOwnLayer(clipAGR
, &scrolledClipRect
,
4805 } else if (IsScrollThumbLayer(item
) && mManager
->IsWidgetLayerManager()) {
4806 // For scrollbar thumbs, the clip we care about is the clip added by the
4808 mPaintedLayerDataTree
.AddingOwnLayer(itemAGR
->mParentAGR
, clipPtr
,
4810 } else if (prerenderedTransform
&& mManager
->IsWidgetLayerManager()) {
4811 if (itemAGR
->mParentAGR
) {
4812 mPaintedLayerDataTree
.AddingOwnLayer(itemAGR
->mParentAGR
, clipPtr
,
4815 mPaintedLayerDataTree
.AddingOwnLayer(itemAGR
, nullptr,
4819 // Using itemVisibleRect here isn't perfect. itemVisibleRect can be
4820 // larger or smaller than the potential bounds of item's contents in
4821 // itemAGR: It's too large if there's a clipped display
4822 // port somewhere among item's contents (see bug 1147673), and it can
4823 // be too small if the contents can move, because it only looks at the
4824 // contents' current bounds and doesn't anticipate any animations.
4825 // Time will tell whether this is good enough, or whether we need to do
4826 // something more sophisticated here.
4827 mPaintedLayerDataTree
.AddingOwnLayer(itemAGR
, &itemVisibleRect
,
4831 ContainerLayerParameters params
= mParameters
;
4832 params
.mBackgroundColor
= uniformColor
;
4833 params
.mLayerCreationHint
= GetLayerCreationHint(itemAGR
);
4834 if (!transformNode
) {
4835 params
.mItemVisibleRect
= &itemVisibleRectAu
;
4837 // We only use mItemVisibleRect for getting the visible rect for
4838 // remote browsers (which should never have inactive transforms), so we
4839 // avoid doing transforms on itemVisibleRectAu above and can't report
4840 // an accurate bounds here.
4841 params
.mItemVisibleRect
= nullptr;
4843 params
.mScrollMetadataASR
=
4844 ActiveScrolledRoot::IsAncestor(scrollMetadataASR
,
4845 mContainerScrollMetadataASR
)
4846 ? mContainerScrollMetadataASR
4847 : scrollMetadataASR
;
4848 params
.mCompositorASR
=
4849 params
.mScrollMetadataASR
!= mContainerScrollMetadataASR
4850 ? params
.mScrollMetadataASR
4851 : mContainerCompositorASR
;
4852 if (itemType
== DisplayItemType::TYPE_FIXED_POSITION
) {
4853 params
.mCompositorASR
= itemASR
;
4856 // Perspective items have a single child item, an nsDisplayTransform.
4857 // If the perspective item is scrolled, but the perspective-inducing
4858 // frame is outside the scroll frame (indicated by item->Frame()
4859 // being outside that scroll frame), we have to take special care to
4860 // make APZ scrolling work properly. APZ needs us to put the scroll
4861 // frame's FrameMetrics on our child transform ContainerLayer instead.
4862 // We make a similar adjustment for OwnLayer items built for frames
4863 // with perspective transforms (e.g. when they have rounded corners).
4864 // It's worth investigating whether this ASR adjustment can be done at
4865 // display item creation time.
4866 bool deferASRForPerspective
=
4867 itemType
== DisplayItemType::TYPE_PERSPECTIVE
||
4868 (itemType
== DisplayItemType::TYPE_OWN_LAYER
&&
4869 item
->Frame()->IsTransformed() && item
->Frame()->HasPerspective());
4870 if (deferASRForPerspective
) {
4871 scrollMetadataASR
= GetASRForPerspective(
4873 item
->Frame()->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME
));
4874 params
.mScrollMetadataASR
= scrollMetadataASR
;
4875 itemASR
= scrollMetadataASR
;
4878 // Just use its layer.
4879 // Set layerContentsVisibleRect.width/height to -1 to indicate we
4880 // currently don't know. If BuildContainerLayerFor gets called by
4881 // item->BuildLayer, this will be set to a proper rect.
4882 nsIntRect
layerContentsVisibleRect(0, 0, -1, -1);
4883 params
.mLayerContentsVisibleRect
= &layerContentsVisibleRect
;
4885 // If this display item wants to build inactive layers but we are treating
4886 // it as active because we are already inside an inactive layer tree,
4887 // we need to make sure that the display item's clip is reflected in
4888 // FrameLayerBuilder::mInactiveLayerClip (which is normally set in
4889 // AddPaintedDisplayItem() when entering an inactive layer tree).
4890 // We intersect the display item's clip into any existing inactive layer
4892 const DisplayItemClip
* originalInactiveClip
= nullptr;
4893 DisplayItemClip combinedInactiveClip
;
4894 if (treatInactiveItemAsActive
) {
4895 originalInactiveClip
= mLayerBuilder
->GetInactiveLayerClip();
4896 if (originalInactiveClip
) {
4897 combinedInactiveClip
= *originalInactiveClip
;
4899 DisplayItemClip nestedClip
= item
->GetClip();
4900 if (nestedClip
.HasClip()) {
4901 nsRect nestedClipRect
= nestedClip
.NonRoundedIntersection();
4903 // Transform the nested clip to be relative to the same reference
4904 // frame as the existing mInactiveLayerClip, so that we can intersect
4906 nestedClipRect
= nsLayoutUtils::TransformFrameRectToAncestor(
4907 item
->ReferenceFrame(), nestedClipRect
,
4908 mLayerBuilder
->GetContainingPaintedLayerData()->mReferenceFrame
);
4910 nestedClip
.SetTo(nestedClipRect
);
4911 combinedInactiveClip
.IntersectWith(nestedClip
);
4912 mLayerBuilder
->SetInactiveLayerClip(&combinedInactiveClip
);
4916 RefPtr
<Layer
> ownLayer
=
4917 item
->AsPaintedDisplayItem()->BuildLayer(mBuilder
, mManager
, params
);
4919 // If above we combined a nested clip into mInactiveLayerClip, restore
4920 // the original inactive layer clip here.
4921 if (treatInactiveItemAsActive
) {
4922 mLayerBuilder
->SetInactiveLayerClip(originalInactiveClip
);
4929 NS_ASSERTION(!ownLayer
->AsPaintedLayer(),
4930 "Should never have created a dedicated Painted layer!");
4932 SetBackfaceHiddenForLayer(item
->BackfaceIsHidden(), ownLayer
);
4935 if (item
->IsInvalid(invalid
)) {
4936 ownLayer
->SetInvalidRectToVisibleRegion();
4939 // If it's not a ContainerLayer, we need to apply the scale transform
4941 if (!ownLayer
->AsContainerLayer()) {
4942 ownLayer
->SetPostScale(mParameters
.mXScale
, mParameters
.mYScale
);
4945 // Update that layer's clip and visible rects.
4946 NS_ASSERTION(ownLayer
->Manager() == mManager
, "Wrong manager");
4947 NS_ASSERTION(!ownLayer
->HasUserData(&gLayerManagerUserData
),
4948 "We shouldn't have a FrameLayerBuilder-managed layer here!");
4949 NS_ASSERTION(itemClip
.HasClip() || itemClip
.GetRoundedRectCount() == 0,
4950 "If we have rounded rects, we must have a clip rect");
4952 // It has its own layer. Update that layer's clip and visible rects.
4953 ownLayer
->SetClipRect(Nothing());
4954 ownLayer
->SetScrolledClip(Nothing());
4955 ownLayer
->SetAncestorMaskLayers({});
4956 if (itemClip
.HasClip()) {
4957 ownLayer
->SetClipRect(Some(clipRect
));
4959 // rounded rectangle clipping using mask layers
4960 // (must be done after visible rect is set on layer)
4961 if (itemClip
.GetRoundedRectCount() > 0) {
4962 SetupMaskLayer(ownLayer
, itemClip
);
4966 if (hasScrolledClip
) {
4967 const DisplayItemClip
& scrolledClip
= layerClipChain
->mClip
;
4968 LayerClip scrolledLayerClip
;
4969 scrolledLayerClip
.SetClipRect(ViewAs
<ParentLayerPixel
>(
4970 ScaleToNearestPixels(scrolledClip
.GetClipRect()) +
4971 mParameters
.mOffset
));
4972 if (scrolledClip
.GetRoundedRectCount() > 0) {
4973 scrolledLayerClip
.SetMaskLayerIndex(
4974 SetupMaskLayerForScrolledClip(ownLayer
.get(), scrolledClip
));
4976 ownLayer
->SetScrolledClip(Some(scrolledLayerClip
));
4979 if (item
->GetType() == DisplayItemType::TYPE_MASK
) {
4980 MOZ_ASSERT(itemClip
.GetRoundedRectCount() == 0);
4982 nsDisplayMasksAndClipPaths
* maskItem
=
4983 static_cast<nsDisplayMasksAndClipPaths
*>(item
);
4984 SetupMaskLayerForCSSMask(ownLayer
, maskItem
);
4986 if (iter
.PeekNext() && iter
.PeekNext()->GetType() ==
4987 DisplayItemType::TYPE_SCROLL_INFO_LAYER
) {
4988 // Since we do build a layer for mask, there is no need for this
4989 // scroll info layer anymore.
4994 // Convert the visible rect to a region and give the item
4995 // a chance to try restrict it further.
4996 nsIntRegion itemVisibleRegion
= itemVisibleRect
;
4997 nsRegion tightBounds
= item
->GetTightBounds(mBuilder
, &snap
);
4998 if (!tightBounds
.IsEmpty()) {
4999 itemVisibleRegion
.AndWith(
5000 ScaleRegionToOutsidePixels(tightBounds
, snap
));
5003 ContainerLayer
* oldContainer
= ownLayer
->GetParent();
5004 if (oldContainer
&& oldContainer
!= mContainerLayer
) {
5005 oldContainer
->RemoveChild(ownLayer
);
5007 NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers
, ownLayer
) < 0,
5008 "Layer already in list???");
5010 // NewLayerEntry::mClipChain is used by SetupScrollingMetadata() to
5011 // populate any scroll clips in the scroll metadata. Perspective layers
5012 // have their ASR adjusted such that a scroll metadata that would normally
5013 // go on the perspective layer goes on its transform layer child instead.
5014 // However, the transform item's clip chain does not contain the
5015 // corresponding scroll clip, so we use the perspective item's clip
5017 const DisplayItemClipChain
* clipChainForScrollClips
= layerClipChain
;
5018 if (itemType
== DisplayItemType::TYPE_TRANSFORM
&& mContainerItem
&&
5019 mContainerItem
->GetType() == DisplayItemType::TYPE_PERSPECTIVE
) {
5020 clipChainForScrollClips
= mContainerItem
->GetClipChain();
5023 NewLayerEntry
* newLayerEntry
= mNewChildLayers
.AppendElement();
5024 newLayerEntry
->mLayer
= ownLayer
;
5025 newLayerEntry
->mAnimatedGeometryRoot
= itemAGR
;
5026 newLayerEntry
->mASR
= itemASR
;
5027 newLayerEntry
->mScrollMetadataASR
= scrollMetadataASR
;
5028 newLayerEntry
->mClipChain
= clipChainForScrollClips
;
5029 newLayerEntry
->mLayerState
= layerState
;
5030 if (itemType
== DisplayItemType::TYPE_FIXED_POSITION
) {
5031 newLayerEntry
->mIsFixedToRootScrollFrame
=
5032 item
->Frame()->StyleDisplay()->mPosition
==
5033 StylePositionProperty::Fixed
&&
5034 nsLayoutUtils::IsReallyFixedPos(item
->Frame());
5037 float contentXScale
= 1.0f
;
5038 float contentYScale
= 1.0f
;
5039 if (ContainerLayer
* ownContainer
= ownLayer
->AsContainerLayer()) {
5040 contentXScale
= 1 / ownContainer
->GetPreXScale();
5041 contentYScale
= 1 / ownContainer
->GetPreYScale();
5043 // nsDisplayTransform::BuildLayer must set layerContentsVisibleRect.
5044 // We rely on this to ensure 3D transforms compute a reasonable
5045 // layer visible region.
5046 NS_ASSERTION(itemType
!= DisplayItemType::TYPE_TRANSFORM
||
5047 layerContentsVisibleRect
.width
>= 0,
5048 "Transform items must set layerContentsVisibleRect!");
5049 if (mLayerBuilder
->IsBuildingRetainedLayers()) {
5050 newLayerEntry
->mLayerContentsVisibleRect
= layerContentsVisibleRect
;
5051 if (itemType
== DisplayItemType::TYPE_PERSPECTIVE
||
5052 (itemType
== DisplayItemType::TYPE_TRANSFORM
&&
5053 (item
->Combines3DTransformWithAncestors() ||
5054 item
->Frame()->Extend3DContext() ||
5055 item
->Frame()->HasPerspective()))) {
5056 // Give untransformed visible region as outer visible region
5057 // to avoid failure caused by singular transforms.
5058 newLayerEntry
->mUntransformedVisibleRegion
= true;
5059 newLayerEntry
->mVisibleRegion
=
5060 item
->GetBuildingRectForChildren().ScaleToOutsidePixels(
5061 contentXScale
, contentYScale
, mAppUnitsPerDevPixel
);
5063 newLayerEntry
->mVisibleRegion
= itemVisibleRegion
;
5065 newLayerEntry
->mOpaqueRegion
= ComputeOpaqueRect(
5066 item
, itemAGR
, itemASR
, itemClip
, aList
,
5067 &newLayerEntry
->mHideAllLayersBelow
,
5068 &newLayerEntry
->mOpaqueForAnimatedGeometryRootParent
);
5070 bool useChildrenVisible
= itemType
== DisplayItemType::TYPE_TRANSFORM
&&
5071 (item
->Frame()->IsPreserve3DLeaf() ||
5072 item
->Frame()->HasPerspective());
5073 const nsIntRegion
& visible
=
5075 ? item
->GetBuildingRectForChildren().ScaleToOutsidePixels(
5076 contentXScale
, contentYScale
, mAppUnitsPerDevPixel
)
5077 : itemVisibleRegion
;
5079 SetOuterVisibleRegionForLayer(ownLayer
, visible
,
5080 layerContentsVisibleRect
.width
>= 0
5081 ? &layerContentsVisibleRect
5083 useChildrenVisible
);
5085 if (itemType
== DisplayItemType::TYPE_SCROLL_INFO_LAYER
) {
5086 nsDisplayScrollInfoLayer
* scrollItem
=
5087 static_cast<nsDisplayScrollInfoLayer
*>(item
);
5088 newLayerEntry
->mOpaqueForAnimatedGeometryRootParent
= false;
5089 newLayerEntry
->mBaseScrollMetadata
= scrollItem
->ComputeScrollMetadata(
5090 mBuilder
, ownLayer
->Manager(), mParameters
);
5094 * No need to allocate geometry for items that aren't
5095 * part of a PaintedLayer.
5097 if (ownLayer
->Manager() == mLayerBuilder
->GetRetainingLayerManager()) {
5098 oldData
= mLayerBuilder
->GetOldLayerForFrame(item
->Frame(),
5099 item
->GetPerFrameKey());
5101 mLayerBuilder
->StoreDataForFrame(item
->AsPaintedDisplayItem(), ownLayer
,
5102 layerState
, oldData
);
5105 const bool backfaceHidden
= item
->In3DContextAndBackfaceIsHidden();
5107 // When container item hit test info is processed, we need to use the same
5108 // reference frame as the container children.
5109 const nsIFrame
* referenceFrame
= item
== mContainerItem
5110 ? mContainerReferenceFrame
5111 : item
->ReferenceFrame();
5113 PaintedLayerData
* paintedLayerData
= selectedLayer
;
5115 if (!paintedLayerData
) {
5116 paintedLayerData
= mPaintedLayerDataTree
.FindPaintedLayerFor(
5117 itemAGR
, itemASR
, layerClipChain
, itemVisibleRect
, backfaceHidden
,
5118 [&](PaintedLayerData
* aData
) {
5119 NewPaintedLayerData(aData
, itemAGR
, itemASR
, layerClipChain
,
5120 scrollMetadataASR
, topLeft
, referenceFrame
,
5124 MOZ_ASSERT(paintedLayerData
);
5126 paintedLayerData
->Accumulate(this, item
, itemVisibleRect
, itemContent
,
5127 itemClip
, layerState
, aList
, marker
,
5128 opacityIndices
, transformNode
);
5130 if (!paintedLayerData
->mLayer
) {
5131 // Try to recycle the old layer of this display item.
5132 RefPtr
<PaintedLayer
> layer
= AttemptToRecyclePaintedLayer(
5133 itemAGR
, item
, topLeft
,
5134 inEffect
? containerReferenceFrame
: referenceFrame
);
5136 paintedLayerData
->mLayer
= layer
;
5138 auto* userData
= GetPaintedDisplayItemLayerUserData(layer
);
5139 paintedLayerData
->mAssignedDisplayItems
.reserve(
5140 userData
->mLastItemCount
);
5142 NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers
, layer
) < 0,
5143 "Layer already in list???");
5144 mNewChildLayers
[paintedLayerData
->mNewChildLayersIndex
].mLayer
=
5149 const auto ClearLayerSelectionIfNeeded
= [&]() {
5150 if (!InOpacity() && !InTransform()) {
5151 selectedLayer
= nullptr;
5152 containerAGR
= nullptr;
5153 containerASR
= nullptr;
5154 containerReferenceFrame
= nullptr;
5158 const auto SelectLayerIfNeeded
= [&]() {
5159 if (!selectedLayer
) {
5160 selectedLayer
= paintedLayerData
;
5161 containerAGR
= itemAGR
;
5162 containerASR
= itemASR
;
5163 containerReferenceFrame
= const_cast<nsIFrame
*>(referenceFrame
);
5167 if (marker
== DisplayItemEntryType::PushTransform
) {
5168 nsDisplayTransform
* transform
= static_cast<nsDisplayTransform
*>(item
);
5170 const Matrix4x4Flagged
& matrix
= transform
->GetTransformForRendering();
5172 Maybe
<gfx::IntRect
> clip
;
5173 if (itemClip
.HasClip()) {
5174 const nsRect nonRoundedClip
= itemClip
.NonRoundedIntersection();
5175 clip
.emplace(nonRoundedClip
.ToNearestPixels(mAppUnitsPerDevPixel
));
5178 transformNode
= new TransformClipNode(transformNode
, matrix
, clip
);
5181 ProcessDisplayItemMarker(marker
, ClearLayerSelectionIfNeeded
,
5182 SelectLayerIfNeeded
);
5185 nsDisplayList
* childItems
= item
->GetSameCoordinateSystemChildren();
5186 if (childItems
&& childItems
->NeedsTransparentSurface()) {
5187 aList
->SetNeedsTransparentSurface();
5191 MOZ_ASSERT(selectedLayer
== nullptr);
5194 void ContainerState::InvalidateForLayerChange(nsDisplayItem
* aItem
,
5195 PaintedLayer
* aNewLayer
,
5196 DisplayItemData
* aData
) {
5197 NS_ASSERTION(aItem
->GetPerFrameKey(),
5198 "Display items that render using Thebes must have a key");
5199 Layer
* oldLayer
= aData
? aData
->mLayer
.get() : nullptr;
5200 if (aNewLayer
!= oldLayer
&& oldLayer
) {
5201 // The item has changed layers.
5202 // Invalidate the old bounds in the old layer and new bounds in the new
5204 PaintedLayer
* t
= oldLayer
->AsPaintedLayer();
5205 if (t
&& aData
->mGeometry
) {
5206 // Note that whenever the layer's scale changes, we invalidate the whole
5207 // thing, so it doesn't matter whether we are using the old scale at last
5208 // paint or a new scale here
5209 #ifdef MOZ_DUMP_PAINTING
5210 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
5211 printf_stderr("Display item type %s(%p) changed layers %p to %p!\n",
5212 aItem
->Name(), aItem
->Frame(), t
, aNewLayer
);
5215 InvalidatePreTransformRect(
5216 t
, aData
->mGeometry
->ComputeInvalidationRegion(), aData
->mClip
,
5217 GetLastPaintOffset(t
), aData
->mTransform
);
5219 // Clear the old geometry so that invalidation thinks the item has been
5220 // added this paint.
5221 aData
->mGeometry
= nullptr;
5225 static nsRect
GetInvalidationRect(nsDisplayItemGeometry
* aGeometry
,
5226 const DisplayItemClip
& aClip
,
5227 TransformClipNode
* aTransform
,
5228 const int32_t aA2D
) {
5229 const nsRect
& rect
= aGeometry
->ComputeInvalidationRegion();
5230 const nsRect clipped
= aClip
.ApplyNonRoundedIntersection(rect
);
5233 return aTransform
->TransformRect(clipped
, aA2D
);
5239 void FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData
* aData
) {
5240 nsDisplayItem
* item
= aData
->mItem
;
5241 PaintedLayer
* paintedLayer
= aData
->mLayer
->AsPaintedLayer();
5242 // If aData->mOptLayer is presence, means this item has been optimized to the
5243 // separate layer. Thus, skip geometry change calculation.
5244 if (aData
->mOptLayer
|| !item
|| !paintedLayer
) {
5249 // If we're a reused display item, then we can't be invalid, so no need to
5250 // do an in-depth comparison. If we haven't previously stored geometry
5251 // for this item (if it was an active layer), then we can't skip this
5253 UniquePtr
<nsDisplayItemGeometry
> geometry
;
5254 if (aData
->mReusedItem
&& aData
->mGeometry
) {
5259 auto* layerData
= GetPaintedDisplayItemLayerUserData(aData
->mLayer
);
5260 nsPoint shift
= layerData
->mAnimatedGeometryRootOrigin
-
5261 layerData
->mLastAnimatedGeometryRootOrigin
;
5263 const DisplayItemClip
& clip
= item
->GetClip();
5264 const int32_t appUnitsPerDevPixel
= layerData
->mAppUnitsPerDevPixel
;
5266 // If the frame is marked as invalidated, and didn't specify a rect to
5267 // invalidate then we want to invalidate both the old and new bounds,
5268 // otherwise we only want to invalidate the changed areas. If we do get an
5269 // invalid rect, then we want to add this on top of the change areas.
5271 nsIntRegion invalidPixels
;
5273 if (!aData
->mGeometry
) {
5274 // This item is being added for the first time, invalidate its entire area.
5275 geometry
= WrapUnique(item
->AllocateGeometry(mDisplayListBuilder
));
5277 const nsRect bounds
= GetInvalidationRect(
5278 geometry
.get(), clip
, aData
->mTransform
, appUnitsPerDevPixel
);
5280 invalidPixels
= bounds
.ScaleToOutsidePixels(
5281 layerData
->mXScale
, layerData
->mYScale
, appUnitsPerDevPixel
);
5282 #ifdef MOZ_DUMP_PAINTING
5283 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
5284 printf_stderr("Display item type %s(%p) added to layer %p!\n",
5285 item
->Name(), item
->Frame(), aData
->mLayer
.get());
5288 } else if (aData
->mIsInvalid
||
5289 (item
->IsInvalid(invalid
) && invalid
.IsEmpty())) {
5290 // Layout marked item/frame as needing repainting (without an explicit
5291 // rect), invalidate the entire old and new areas.
5292 geometry
= WrapUnique(item
->AllocateGeometry(mDisplayListBuilder
));
5295 GetInvalidationRect(aData
->mGeometry
.get(), aData
->mClip
,
5296 aData
->mOldTransform
, appUnitsPerDevPixel
);
5297 oldArea
.MoveBy(shift
);
5299 nsRect newArea
= GetInvalidationRect(
5300 geometry
.get(), clip
, aData
->mTransform
, appUnitsPerDevPixel
);
5303 combined
.Or(oldArea
, newArea
);
5304 invalidPixels
= combined
.ScaleToOutsidePixels(
5305 layerData
->mXScale
, layerData
->mYScale
, appUnitsPerDevPixel
);
5306 #ifdef MOZ_DUMP_PAINTING
5307 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
5309 "Display item type %s(%p) (in layer %p) belongs to an "
5310 "invalidated frame!\n",
5311 item
->Name(), item
->Frame(), aData
->mLayer
.get());
5315 // Let the display item check for geometry changes and decide what needs to
5317 const nsRegion
& changedFrameInvalidations
=
5318 aData
->GetChangedFrameInvalidations();
5320 if (aData
->mTransform
) {
5321 // If this display item is inside a flattened transform the offset is
5322 // already included in the root transform, so there is no need to shift.
5326 aData
->mGeometry
->MoveBy(shift
);
5329 item
->ComputeInvalidationRegion(mDisplayListBuilder
, aData
->mGeometry
.get(),
5332 // Only allocate a new geometry object if something actually changed,
5333 // otherwise the existing one should be fine. We always reallocate for
5334 // inactive layers, since these types don't implement
5335 // ComputeInvalidateRegion (and rely on the ComputeDifferences call in
5336 // AddPaintedDisplayItem instead).
5337 if (!combined
.IsEmpty() ||
5338 aData
->mLayerState
== LayerState::LAYER_INACTIVE
||
5339 item
->NeedsGeometryUpdates()) {
5340 geometry
= WrapUnique(item
->AllocateGeometry(mDisplayListBuilder
));
5343 aData
->mClip
.AddOffsetAndComputeDifference(
5344 shift
, aData
->mGeometry
->ComputeInvalidationRegion(), clip
,
5345 geometry
? geometry
->ComputeInvalidationRegion()
5346 : aData
->mGeometry
->ComputeInvalidationRegion(),
5349 // Add in any rect that the frame specified
5350 combined
.Or(combined
, invalid
);
5351 combined
.Or(combined
, changedFrameInvalidations
);
5353 // Restrict invalidation to the clipped region
5354 nsRegion clipRegion
;
5355 if (clip
.ComputeRegionInClips(&aData
->mClip
, shift
, &clipRegion
)) {
5356 combined
.And(combined
, clipRegion
);
5359 invalidPixels
= combined
.ToOutsidePixels(appUnitsPerDevPixel
);
5361 if (aData
->mTransform
) {
5362 invalidPixels
= aData
->mTransform
->TransformRegion(invalidPixels
);
5365 invalidPixels
.ScaleRoundOut(layerData
->mXScale
, layerData
->mYScale
);
5367 #ifdef MOZ_DUMP_PAINTING
5368 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
5369 if (!combined
.IsEmpty()) {
5371 "Display item type %s(%p) (in layer %p) changed geometry!\n",
5372 item
->Name(), item
->Frame(), aData
->mLayer
.get());
5378 if (!invalidPixels
.IsEmpty()) {
5379 InvalidatePostTransformRegion(paintedLayer
, invalidPixels
,
5380 layerData
->mTranslation
);
5383 aData
->EndUpdate(std::move(geometry
));
5386 void FrameLayerBuilder::AddPaintedDisplayItem(PaintedLayerData
* aLayerData
,
5387 AssignedDisplayItem
& aItem
,
5389 PaintedLayer
* layer
= aLayerData
->mLayer
;
5390 auto* paintedData
= GetPaintedDisplayItemLayerUserData(layer
);
5392 if (layer
->Manager() == mRetainingManager
) {
5393 DisplayItemData
* data
= aItem
.mDisplayItemData
;
5394 if (data
&& !data
->mUsed
) {
5395 data
->BeginUpdate(layer
, aItem
.mLayerState
, aItem
.mItem
, aItem
.mReused
,
5398 data
= StoreDataForFrame(aItem
.mItem
, layer
, aItem
.mLayerState
, nullptr);
5400 data
->mInactiveManager
= aItem
.mInactiveLayerData
5401 ? aItem
.mInactiveLayerData
->mLayerManager
5403 // We optimized this PaintedLayer into a ColorLayer/ImageLayer. Store the
5404 // optimized layer here.
5405 if (aLayer
!= layer
) {
5406 data
->mOptLayer
= aLayer
;
5409 data
->mOldTransform
= data
->mTransform
;
5410 data
->mTransform
= aItem
.mTransform
;
5413 if (aItem
.mInactiveLayerData
) {
5414 RefPtr
<BasicLayerManager
> tempManager
=
5415 aItem
.mInactiveLayerData
->mLayerManager
;
5416 FrameLayerBuilder
* layerBuilder
= tempManager
->GetLayerBuilder();
5417 Layer
* tmpLayer
= aItem
.mInactiveLayerData
->mLayer
;
5419 // We have no easy way of detecting if this transaction will ever actually
5420 // get finished. For now, I've just silenced the warning with nested
5421 // transactions in BasicLayers.cpp
5423 tempManager
->EndTransaction(nullptr, nullptr);
5424 tempManager
->SetUserData(&gLayerManagerLayerBuilder
, nullptr);
5425 aItem
.mItem
= nullptr;
5430 nsRect visibleRect
= aItem
.mItem
->GetBuildingRect().Intersect(
5431 aItem
.mItem
->GetBounds(mDisplayListBuilder
, &snap
));
5433 visibleRect
.ToOutsidePixels(paintedData
->mAppUnitsPerDevPixel
);
5435 // Convert the visible rect to a region and give the item
5436 // a chance to try restrict it further.
5437 nsRegion tightBounds
=
5438 aItem
.mItem
->GetTightBounds(mDisplayListBuilder
, &snap
);
5439 if (!tightBounds
.IsEmpty()) {
5441 tightBounds
.ToOutsidePixels(paintedData
->mAppUnitsPerDevPixel
));
5443 SetOuterVisibleRegion(tmpLayer
, &rgn
);
5445 DisplayItemData
* data
= nullptr;
5446 // If BuildLayer didn't call BuildContainerLayerFor, then our new layer
5447 // won't have been stored in layerBuilder. Manually add it now.
5448 if (mRetainingManager
) {
5449 #ifdef DEBUG_DISPLAY_ITEM_DATA
5450 LayerManagerData
* parentLmd
= static_cast<LayerManagerData
*>(
5451 layer
->Manager()->GetUserData(&gLayerManagerUserData
));
5452 LayerManagerData
* lmd
= static_cast<LayerManagerData
*>(
5453 tempManager
->GetUserData(&gLayerManagerUserData
));
5454 lmd
->mParent
= parentLmd
;
5457 layerBuilder
->GetDisplayItemDataForManager(aItem
.mItem
, tempManager
);
5458 data
= layerBuilder
->StoreDataForFrame(aItem
.mItem
, tmpLayer
,
5459 LayerState::LAYER_ACTIVE
, data
);
5460 data
->mOldTransform
= data
->mTransform
;
5461 data
->mTransform
= aItem
.mTransform
;
5464 tempManager
->SetRoot(tmpLayer
);
5465 layerBuilder
->WillEndTransaction();
5466 tempManager
->AbortTransaction();
5468 if (gfxUtils::DumpDisplayList() || gfxEnv::DumpPaint()) {
5470 gfxUtils::sDumpPaintFile
,
5471 "Basic layer tree for painting contents of display item %s(%p):\n",
5472 aItem
.mItem
->Name(), aItem
.mItem
->Frame());
5473 std::stringstream stream
;
5474 tempManager
->Dump(stream
, "", gfxEnv::DumpPaintToFile());
5475 fprint_stderr(gfxUtils::sDumpPaintFile
,
5476 stream
); // not a typo, fprint_stderr declared in nsDebug.h
5480 GetLastPaintOffset(layer
) - GetTranslationForPaintedLayer(layer
);
5481 aItem
.mInactiveLayerData
->mProps
->MoveBy(-offset
);
5482 // Effective transforms are needed by ComputeDifferences().
5483 tmpLayer
->ComputeEffectiveTransforms(Matrix4x4());
5484 nsIntRegion invalid
;
5485 if (!aItem
.mInactiveLayerData
->mProps
->ComputeDifferences(tmpLayer
, invalid
,
5487 nsRect visible
= aItem
.mItem
->Frame()->InkOverflowRect();
5488 invalid
= visible
.ToOutsidePixels(paintedData
->mAppUnitsPerDevPixel
);
5490 if (aItem
.mLayerState
== LayerState::LAYER_SVG_EFFECTS
) {
5491 invalid
= SVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(
5492 aItem
.mItem
->Frame(), aItem
.mItem
->ToReferenceFrame(), invalid
);
5494 if (!invalid
.IsEmpty()) {
5495 #ifdef MOZ_DUMP_PAINTING
5496 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
5498 "Inactive LayerManager(%p) for display item %s(%p) has "
5499 "an invalid region - invalidating layer %p\n",
5500 tempManager
.get(), aItem
.mItem
->Name(), aItem
.mItem
->Frame(),
5505 if (data
&& data
->mTransform
) {
5506 invalid
= data
->mTransform
->TransformRegion(invalid
);
5509 invalid
.ScaleRoundOut(paintedData
->mXScale
, paintedData
->mYScale
);
5511 InvalidatePostTransformRegion(layer
, invalid
,
5512 GetTranslationForPaintedLayer(layer
));
5517 DisplayItemData
* FrameLayerBuilder::StoreDataForFrame(
5518 nsPaintedDisplayItem
* aItem
, Layer
* aLayer
, LayerState aState
,
5519 DisplayItemData
* aData
) {
5523 if (!aData
->mUsed
) {
5524 aData
->BeginUpdate(aLayer
, aState
, false, aItem
);
5529 LayerManagerData
* lmd
= static_cast<LayerManagerData
*>(
5530 mRetainingManager
->GetUserData(&gLayerManagerUserData
));
5532 RefPtr
<DisplayItemData
> data
= new (aItem
->Frame()->PresContext())
5533 DisplayItemData(lmd
, aItem
->GetPerFrameKey(), aLayer
);
5535 data
->BeginUpdate(aLayer
, aState
, true, aItem
);
5537 lmd
->mDisplayItems
.push_back(data
);
5541 void FrameLayerBuilder::StoreDataForFrame(nsIFrame
* aFrame
,
5542 uint32_t aDisplayItemKey
,
5543 Layer
* aLayer
, LayerState aState
) {
5544 DisplayItemData
* oldData
= GetDisplayItemData(aFrame
, aDisplayItemKey
);
5545 if (oldData
&& oldData
->mFrameList
.Length() == 1) {
5546 oldData
->BeginUpdate(aLayer
, aState
, false);
5550 LayerManagerData
* lmd
= static_cast<LayerManagerData
*>(
5551 mRetainingManager
->GetUserData(&gLayerManagerUserData
));
5553 RefPtr
<DisplayItemData
> data
= new (aFrame
->PresContext())
5554 DisplayItemData(lmd
, aDisplayItemKey
, aLayer
, aFrame
);
5556 data
->BeginUpdate(aLayer
, aState
, true);
5558 lmd
->mDisplayItems
.push_back(data
);
5561 AssignedDisplayItem::AssignedDisplayItem(
5562 nsPaintedDisplayItem
* aItem
, LayerState aLayerState
, DisplayItemData
* aData
,
5563 const nsRect
& aContentRect
, DisplayItemEntryType aType
,
5564 const bool aHasOpacity
, const RefPtr
<TransformClipNode
>& aTransform
,
5565 const bool aIsMerged
)
5567 mDisplayItemData(aData
),
5568 mTransform(aTransform
),
5569 mContentRect(aContentRect
),
5570 mLayerState(aLayerState
),
5572 mReused(aItem
->IsReused()),
5574 mHasOpacity(aHasOpacity
),
5575 mHasPaintRect(aItem
->HasPaintRect()) {}
5577 InactiveLayerData::~InactiveLayerData() {
5578 if (mLayerManager
) {
5579 mLayerManager
->SetUserData(&gLayerManagerLayerBuilder
, nullptr);
5583 bool FrameLayerBuilder::CheckInLayerTreeCompressionMode() {
5584 if (mInLayerTreeCompressionMode
) {
5588 // If we wanted to be in layer tree compression mode, but weren't, then
5589 // scheduled a delayed repaint where we will be.
5590 mRootPresContext
->PresShell()->GetRootFrame()->SchedulePaint(
5591 nsIFrame::PAINT_DELAYED_COMPRESS
, false);
5596 void ContainerState::CollectOldLayers() {
5597 for (Layer
* layer
= mContainerLayer
->GetFirstChild(); layer
;
5598 layer
= layer
->GetNextSibling()) {
5599 NS_ASSERTION(!layer
->HasUserData(&gMaskLayerUserData
),
5600 "Mask layers should not be part of the layer tree.");
5601 if (layer
->HasUserData(&gPaintedDisplayItemLayerUserData
)) {
5602 NS_ASSERTION(layer
->AsPaintedLayer(), "Wrong layer type");
5603 mPaintedLayersAvailableForRecycling
.Insert(
5604 static_cast<PaintedLayer
*>(layer
));
5607 if (Layer
* maskLayer
= layer
->GetMaskLayer()) {
5608 NS_ASSERTION(maskLayer
->GetType() == Layer::TYPE_IMAGE
,
5609 "Could not recycle mask layer, unsupported layer type.");
5610 mRecycledMaskImageLayers
.InsertOrUpdate(
5611 MaskLayerKey(layer
, Nothing()),
5612 RefPtr
{static_cast<ImageLayer
*>(maskLayer
)});
5614 for (size_t i
= 0; i
< layer
->GetAncestorMaskLayerCount(); i
++) {
5615 Layer
* maskLayer
= layer
->GetAncestorMaskLayerAt(i
);
5617 NS_ASSERTION(maskLayer
->GetType() == Layer::TYPE_IMAGE
,
5618 "Could not recycle mask layer, unsupported layer type.");
5619 mRecycledMaskImageLayers
.InsertOrUpdate(
5620 MaskLayerKey(layer
, Some(i
)),
5621 RefPtr
{static_cast<ImageLayer
*>(maskLayer
)});
5626 struct OpaqueRegionEntry
{
5627 AnimatedGeometryRoot
* mAnimatedGeometryRoot
;
5628 const ActiveScrolledRoot
* mASR
;
5629 nsIntRegion mOpaqueRegion
;
5632 static OpaqueRegionEntry
* FindOpaqueRegionEntry(
5633 nsTArray
<OpaqueRegionEntry
>& aEntries
,
5634 AnimatedGeometryRoot
* aAnimatedGeometryRoot
,
5635 const ActiveScrolledRoot
* aASR
) {
5636 for (uint32_t i
= 0; i
< aEntries
.Length(); ++i
) {
5637 OpaqueRegionEntry
* d
= &aEntries
[i
];
5638 if (d
->mAnimatedGeometryRoot
== aAnimatedGeometryRoot
&& d
->mASR
== aASR
) {
5645 static const ActiveScrolledRoot
* FindDirectChildASR(
5646 const ActiveScrolledRoot
* aParent
, const ActiveScrolledRoot
* aDescendant
) {
5647 MOZ_ASSERT(aDescendant
, "can't start at the root when looking for a child");
5648 MOZ_ASSERT(ActiveScrolledRoot::IsAncestor(aParent
, aDescendant
));
5649 const ActiveScrolledRoot
* directChild
= aDescendant
;
5650 while (directChild
->mParent
!= aParent
) {
5651 directChild
= directChild
->mParent
;
5652 MOZ_RELEASE_ASSERT(directChild
, "this must not be null");
5657 static void FixUpFixedPositionLayer(
5658 Layer
* aLayer
, const ActiveScrolledRoot
* aTargetASR
,
5659 const ActiveScrolledRoot
* aLeafScrollMetadataASR
,
5660 const ActiveScrolledRoot
* aContainerScrollMetadataASR
,
5661 const ActiveScrolledRoot
* aContainerCompositorASR
,
5662 bool aIsFixedToRootScrollFrame
) {
5663 if (!aLayer
->GetIsFixedPosition()) {
5667 // Analyze ASRs to figure out if we need to fix up fixedness annotations on
5668 // the layer. Fixed annotations are required in multiple cases:
5669 // - Sometimes we set scroll metadata on a layer for a scroll frame that we
5670 // don't want the layer to be moved by. (We have to do this if there is a
5671 // scrolled clip that is moved by that scroll frame.) So we set the fixed
5672 // annotation so that the compositor knows that it should ignore that
5673 // scroll metadata when determining the layer's position.
5674 // - Sometimes there is a scroll meta data on aLayer's parent layer for a
5675 // scroll frame that we don't want aLayer to be moved by. The most common
5676 // way for this to happen is with containerful root scrolling, where the
5677 // scroll metadata for the root scroll frame is on a container layer that
5678 // wraps the whole document's contents.
5679 // - Sometimes it's just needed for hit testing, i.e. figuring out what
5680 // scroll frame should be scrolled by events over the layer.
5681 // A fixed layer needs to be annotated with the scroll ID of the scroll frame
5682 // that it is *fixed with respect to*, i.e. the outermost scroll frame which
5683 // does not move the layer. nsDisplayFixedPosition only ever annotates layers
5684 // with the scroll ID of the presshell's root scroll frame, which is
5685 // sometimes the wrong thing to do, so we correct it here. Specifically,
5686 // it's the wrong thing to do if the fixed frame's containing block is a
5687 // transformed frame - in that case, the fixed frame needs to scroll along
5688 // with the transformed frame instead of being fixed with respect to the rsf.
5689 // (It would be nice to compute the annotation only in one place and get it
5690 // right, instead of fixing it up after the fact like this, but this will
5691 // need to do for now.)
5692 // compositorASR is the ASR that the layer would move with on the compositor
5693 // if there were no fixed annotation on it.
5694 const ActiveScrolledRoot
* compositorASR
=
5695 aLeafScrollMetadataASR
== aContainerScrollMetadataASR
5696 ? aContainerCompositorASR
5697 : aLeafScrollMetadataASR
;
5699 // The goal of the annotation is to have the layer move with aTargetASR.
5700 if (compositorASR
&& aTargetASR
!= compositorASR
) {
5701 // Mark this layer as fixed with respect to the child scroll frame of
5703 aLayer
->SetFixedPositionData(
5704 FindDirectChildASR(aTargetASR
, compositorASR
)->GetViewId(),
5705 aLayer
->GetFixedPositionAnchor(), aLayer
->GetFixedPositionSides());
5707 // Remove the fixed annotation from the layer, unless this layers is fixed
5708 // to the document's root scroll frame - in that case, the annotation is
5709 // needed for hit testing, because fixed layers in iframes should scroll
5710 // the iframe, even though their position is not affected by scrolling in
5711 // the iframe. (The APZ hit testing code has a special case for this.)
5712 // nsDisplayFixedPosition has annotated this layer with the document's
5713 // root scroll frame's scroll id.
5714 aLayer
->SetIsFixedPosition(aIsFixedToRootScrollFrame
);
5718 void ContainerState::SetupScrollingMetadata(NewLayerEntry
* aEntry
) {
5719 if (!mBuilder
->IsPaintingToWindow()) {
5720 // async scrolling not possible, and async scrolling info not computed
5725 const ActiveScrolledRoot
* startASR
= aEntry
->mScrollMetadataASR
;
5726 const ActiveScrolledRoot
* stopASR
= mContainerScrollMetadataASR
;
5727 if (!ActiveScrolledRoot::IsAncestor(stopASR
, startASR
)) {
5728 if (ActiveScrolledRoot::IsAncestor(startASR
, stopASR
)) {
5729 // startASR and stopASR are in the same branch of the ASR tree, but
5730 // startASR is closer to the root. Just start at stopASR so that the loop
5731 // below doesn't actually do anything.
5734 // startASR and stopASR are in different branches of the
5735 // ASR tree. Find a common ancestor and make that the stopASR.
5736 // This can happen when there's a scrollable frame inside a fixed layer
5737 // which has a scrolled clip. As far as scroll metadata is concerned,
5738 // the scroll frame's scroll metadata will be a child of the scroll ID
5739 // that scrolls the clip on the fixed layer. But as far as ASRs are
5740 // concerned, those two ASRs are siblings, parented to the ASR of the
5743 stopASR
= stopASR
->mParent
;
5744 } while (!ActiveScrolledRoot::IsAncestor(stopASR
, startASR
));
5748 FixUpFixedPositionLayer(aEntry
->mLayer
, aEntry
->mASR
, startASR
,
5749 mContainerScrollMetadataASR
, mContainerCompositorASR
,
5750 aEntry
->mIsFixedToRootScrollFrame
);
5752 AutoTArray
<ScrollMetadata
, 2> metricsArray
;
5753 if (aEntry
->mBaseScrollMetadata
) {
5754 metricsArray
.AppendElement(*aEntry
->mBaseScrollMetadata
);
5756 // The base FrameMetrics was not computed by the nsIScrollableframe, so it
5757 // should not have a mask layer.
5758 MOZ_ASSERT(!aEntry
->mBaseScrollMetadata
->HasMaskLayer());
5761 // Any extra mask layers we need to attach to ScrollMetadatas.
5762 // The list may already contain an entry added for the layer's scrolled clip
5763 // so add to it rather than overwriting it (we clear the list when recycling
5765 nsTArray
<RefPtr
<Layer
>> maskLayers(
5766 aEntry
->mLayer
->GetAllAncestorMaskLayers().Clone());
5768 // Iterate over the ASR chain and create the corresponding scroll metadatas.
5769 // This loop is slightly tricky because the scrollframe-to-clip relationship
5770 // is reversed between DisplayItemClipChain and ScrollMetadata:
5771 // - DisplayItemClipChain associates the clip with the scroll frame that
5772 // this clip is *moved by*, i.e. the clip is moving inside the scroll
5774 // - ScrollMetaData associates the scroll frame with the clip that's
5775 // *just outside* the scroll frame, i.e. not moved by the scroll frame
5777 // This discrepancy means that the leaf clip item of the clip chain is never
5778 // applied to any scroll meta data. Instead, it was applied earlier as the
5779 // layer's clip (or fused with the painted layer contents), or it was applied
5780 // as a ScrolledClip on the layer.
5781 const DisplayItemClipChain
* clipChain
= aEntry
->mClipChain
;
5783 for (const ActiveScrolledRoot
* asr
= startASR
; asr
!= stopASR
;
5784 asr
= asr
->mParent
) {
5786 MOZ_ASSERT_UNREACHABLE("Should have encountered stopASR on the way up.");
5789 if (clipChain
&& clipChain
->mASR
== asr
) {
5790 clipChain
= clipChain
->mParent
;
5793 nsIScrollableFrame
* scrollFrame
= asr
->mScrollableFrame
;
5794 const DisplayItemClip
* clip
= (clipChain
&& clipChain
->mASR
== asr
->mParent
)
5798 scrollFrame
->ClipLayerToDisplayPort(aEntry
->mLayer
, clip
, mParameters
);
5800 Maybe
<ScrollMetadata
> metadata
;
5801 if (mCachedScrollMetadata
.mASR
== asr
&&
5802 mCachedScrollMetadata
.mClip
== clip
) {
5803 metadata
= mCachedScrollMetadata
.mMetadata
;
5805 metadata
= scrollFrame
->ComputeScrollMetadata(aEntry
->mLayer
->Manager(),
5806 mContainerReferenceFrame
,
5807 Some(mParameters
), clip
);
5808 mBuilder
->AddScrollFrameToNotify(scrollFrame
);
5809 mCachedScrollMetadata
.mASR
= asr
;
5810 mCachedScrollMetadata
.mClip
= clip
;
5811 mCachedScrollMetadata
.mMetadata
= metadata
;
5818 if (clip
&& clip
->HasClip() && clip
->GetRoundedRectCount() > 0) {
5819 // The clip in between this scrollframe and its ancestor scrollframe
5820 // requires a mask layer. Since this mask layer should not move with
5821 // the APZC associated with this FrameMetrics, we attach the mask
5822 // layer as an additional, separate clip.
5823 Maybe
<size_t> nextIndex
= Some(maskLayers
.Length());
5824 RefPtr
<Layer
> maskLayer
=
5825 CreateMaskLayer(aEntry
->mLayer
, *clip
, nextIndex
);
5827 MOZ_ASSERT(metadata
->HasScrollClip());
5828 metadata
->ScrollClip().SetMaskLayerIndex(nextIndex
);
5829 maskLayers
.AppendElement(maskLayer
);
5833 metricsArray
.AppendElement(*metadata
);
5836 // Watch out for FrameMetrics copies in profiles
5837 aEntry
->mLayer
->SetScrollMetadata(metricsArray
);
5838 aEntry
->mLayer
->SetAncestorMaskLayers(maskLayers
);
5841 static inline Maybe
<ParentLayerIntRect
> GetStationaryClipInContainer(
5843 if (size_t metricsCount
= aLayer
->GetScrollMetadataCount()) {
5844 return aLayer
->GetScrollMetadata(metricsCount
- 1).GetClipRect();
5846 return aLayer
->GetClipRect();
5849 void ContainerState::PostprocessRetainedLayers(
5850 nsIntRegion
* aOpaqueRegionForContainer
) {
5851 AutoTArray
<OpaqueRegionEntry
, 4> opaqueRegions
;
5852 bool hideAll
= false;
5853 int32_t opaqueRegionForContainer
= -1;
5855 for (int32_t i
= mNewChildLayers
.Length() - 1; i
>= 0; --i
) {
5856 NewLayerEntry
* e
= &mNewChildLayers
.ElementAt(i
);
5861 OpaqueRegionEntry
* data
=
5862 FindOpaqueRegionEntry(opaqueRegions
, e
->mAnimatedGeometryRoot
, e
->mASR
);
5864 SetupScrollingMetadata(e
);
5867 e
->mVisibleRegion
.SetEmpty();
5868 } else if (!e
->mLayer
->IsScrollbarContainer()) {
5869 Maybe
<ParentLayerIntRect
> clipRect
=
5870 GetStationaryClipInContainer(e
->mLayer
);
5871 if (clipRect
&& opaqueRegionForContainer
>= 0 &&
5872 opaqueRegions
[opaqueRegionForContainer
].mOpaqueRegion
.Contains(
5873 clipRect
->ToUnknownRect())) {
5874 e
->mVisibleRegion
.SetEmpty();
5876 e
->mVisibleRegion
.Sub(e
->mVisibleRegion
, data
->mOpaqueRegion
);
5880 SetOuterVisibleRegionForLayer(e
->mLayer
, e
->mVisibleRegion
,
5881 e
->mLayerContentsVisibleRect
.width
>= 0
5882 ? &e
->mLayerContentsVisibleRect
5884 e
->mUntransformedVisibleRegion
);
5886 if (!e
->mOpaqueRegion
.IsEmpty()) {
5887 AnimatedGeometryRoot
* animatedGeometryRootToCover
=
5888 e
->mAnimatedGeometryRoot
;
5889 const ActiveScrolledRoot
* asrToCover
= e
->mASR
;
5890 if (e
->mOpaqueForAnimatedGeometryRootParent
&&
5891 e
->mAnimatedGeometryRoot
->mParentAGR
==
5892 mContainerAnimatedGeometryRoot
) {
5893 animatedGeometryRootToCover
= mContainerAnimatedGeometryRoot
;
5894 asrToCover
= mContainerASR
;
5895 data
= FindOpaqueRegionEntry(opaqueRegions
, animatedGeometryRootToCover
,
5900 if (animatedGeometryRootToCover
== mContainerAnimatedGeometryRoot
&&
5901 asrToCover
== mContainerASR
) {
5902 NS_ASSERTION(opaqueRegionForContainer
== -1, "Already found it?");
5903 opaqueRegionForContainer
= opaqueRegions
.Length();
5905 data
= opaqueRegions
.AppendElement();
5906 data
->mAnimatedGeometryRoot
= animatedGeometryRootToCover
;
5907 data
->mASR
= asrToCover
;
5910 nsIntRegion clippedOpaque
= e
->mOpaqueRegion
;
5911 Maybe
<ParentLayerIntRect
> clipRect
= e
->mLayer
->GetCombinedClipRect();
5913 clippedOpaque
.AndWith(clipRect
->ToUnknownRect());
5915 if (e
->mLayer
->GetScrolledClip()) {
5916 // The clip can move asynchronously, so we can't rely on opaque parts
5918 clippedOpaque
.SetEmpty();
5919 } else if (e
->mHideAllLayersBelow
) {
5922 data
->mOpaqueRegion
.Or(data
->mOpaqueRegion
, clippedOpaque
);
5925 if (e
->mLayer
->GetType() == Layer::TYPE_READBACK
) {
5926 // ReadbackLayers need to accurately read what's behind them. So,
5927 // we don't want to do any occlusion culling of layers behind them.
5928 // Theoretically we could just punch out the ReadbackLayer's rectangle
5929 // from all mOpaqueRegions, but that's probably not worth doing.
5930 opaqueRegions
.Clear();
5931 opaqueRegionForContainer
= -1;
5935 if (opaqueRegionForContainer
>= 0) {
5936 aOpaqueRegionForContainer
->Or(
5937 *aOpaqueRegionForContainer
,
5938 opaqueRegions
[opaqueRegionForContainer
].mOpaqueRegion
);
5942 void ContainerState::Finish(uint32_t* aTextContentFlags
,
5943 const nsIntRect
& aContainerPixelBounds
,
5944 nsDisplayList
* aChildItems
) {
5945 mPaintedLayerDataTree
.Finish();
5947 NS_ASSERTION(mContainerBounds
.IsEqualInterior(mAccumulatedChildBounds
),
5948 "Bounds computation mismatch");
5950 if (mLayerBuilder
->IsBuildingRetainedLayers()) {
5951 nsIntRegion containerOpaqueRegion
;
5952 PostprocessRetainedLayers(&containerOpaqueRegion
);
5953 if (containerOpaqueRegion
.Contains(aContainerPixelBounds
)) {
5954 aChildItems
->SetIsOpaque();
5958 uint32_t textContentFlags
= 0;
5960 // Make sure that current/existing layers are added to the parent and are
5961 // in the correct order.
5962 Layer
* layer
= nullptr;
5963 Layer
* prevChild
= nullptr;
5964 for (uint32_t i
= 0; i
< mNewChildLayers
.Length(); ++i
, prevChild
= layer
) {
5965 if (!mNewChildLayers
[i
].mLayer
) {
5969 layer
= mNewChildLayers
[i
].mLayer
;
5971 if (!layer
->GetVisibleRegion().IsEmpty()) {
5972 textContentFlags
|= layer
->GetContentFlags() &
5973 (Layer::CONTENT_COMPONENT_ALPHA
|
5974 Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT
|
5975 Layer::CONTENT_DISABLE_FLATTENING
);
5978 if (!layer
->GetParent()) {
5979 // This is not currently a child of the container, so just add it
5981 mContainerLayer
->InsertAfter(layer
, prevChild
);
5983 NS_ASSERTION(layer
->GetParent() == mContainerLayer
,
5984 "Layer shouldn't be the child of some other container");
5985 if (layer
->GetPrevSibling() != prevChild
) {
5986 mContainerLayer
->RepositionChild(layer
, prevChild
);
5991 // Remove old layers that have become unused.
5993 layer
= mContainerLayer
->GetFirstChild();
5995 layer
= layer
->GetNextSibling();
5998 Layer
* layerToRemove
= layer
;
5999 layer
= layer
->GetNextSibling();
6000 mContainerLayer
->RemoveChild(layerToRemove
);
6003 *aTextContentFlags
= textContentFlags
;
6006 static void RestrictScaleToMaxLayerSize(Size
& aScale
,
6007 const nsRect
& aVisibleRect
,
6008 nsIFrame
* aContainerFrame
,
6009 Layer
* aContainerLayer
) {
6010 if (!aContainerLayer
->Manager()->IsWidgetLayerManager()) {
6014 nsIntRect pixelSize
= aVisibleRect
.ScaleToOutsidePixels(
6015 aScale
.width
, aScale
.height
,
6016 aContainerFrame
->PresContext()->AppUnitsPerDevPixel());
6018 int32_t maxLayerSize
= aContainerLayer
->GetMaxLayerSize();
6020 if (pixelSize
.width
> maxLayerSize
) {
6021 float scale
= (float)pixelSize
.width
/ maxLayerSize
;
6022 scale
= gfxUtils::ClampToScaleFactor(scale
);
6023 aScale
.width
/= scale
;
6025 if (pixelSize
.height
> maxLayerSize
) {
6026 float scale
= (float)pixelSize
.height
/ maxLayerSize
;
6027 scale
= gfxUtils::ClampToScaleFactor(scale
);
6028 aScale
.height
/= scale
;
6032 static nsSize
ComputeDesiredDisplaySizeForAnimation(nsIFrame
* aContainerFrame
) {
6033 // Use the size of the nearest widget as the maximum size. This
6034 // is important since it might be a popup that is bigger than the
6035 // pres context's size.
6036 nsPresContext
* presContext
= aContainerFrame
->PresContext();
6037 nsIWidget
* widget
= aContainerFrame
->GetNearestWidget();
6039 return LayoutDevicePixel::ToAppUnits(widget
->GetClientSize(),
6040 presContext
->AppUnitsPerDevPixel());
6043 return presContext
->GetVisibleArea().Size();
6047 Size
FrameLayerBuilder::ChooseScale(nsIFrame
* aContainerFrame
,
6048 nsDisplayItem
* aContainerItem
,
6049 const nsRect
& aVisibleRect
, float aXScale
,
6050 float aYScale
, const Matrix
& aTransform2d
,
6053 // XXX Should we do something for 3D transforms?
6054 if (aCanDraw2D
&& !aContainerFrame
->Combines3DTransformWithAncestors() &&
6055 !aContainerFrame
->HasPerspective()) {
6056 // If the container's transform is animated off main thread, fix a suitable
6057 // scale size for animation
6058 if (aContainerItem
&&
6059 aContainerItem
->GetType() == DisplayItemType::TYPE_TRANSFORM
&&
6060 // FIXME: What we need is only transform, rotate, and scale, not
6061 // translate, so it's be better to use a property set, instead of
6062 // display item type here.
6063 EffectCompositor::HasAnimationsForCompositor(
6064 aContainerFrame
, DisplayItemType::TYPE_TRANSFORM
)) {
6065 nsSize displaySize
=
6066 ComputeDesiredDisplaySizeForAnimation(aContainerFrame
);
6067 // compute scale using the animation on the container, taking ancestors in
6069 nsSize scaledVisibleSize
= nsSize(aVisibleRect
.Width() * aXScale
,
6070 aVisibleRect
.Height() * aYScale
);
6071 scale
= nsLayoutUtils::ComputeSuitableScaleForAnimation(
6072 aContainerFrame
, scaledVisibleSize
, displaySize
);
6073 // multiply by the scale inherited from ancestors--we use a uniform
6074 // scale factor to prevent blurring when the layer is rotated.
6075 float incomingScale
= std::max(aXScale
, aYScale
);
6076 scale
.width
*= incomingScale
;
6077 scale
.height
*= incomingScale
;
6079 // Scale factors are normalized to a power of 2 to reduce the number of
6080 // resolution changes
6081 scale
= aTransform2d
.ScaleFactors();
6082 // For frames with a changing scale transform round scale factors up to
6083 // nearest power-of-2 boundary so that we don't keep having to redraw
6084 // the content as it scales up and down. Rounding up to nearest
6085 // power-of-2 boundary ensures we never scale up, only down --- avoiding
6086 // jaggies. It also ensures we never scale down by more than a factor of
6087 // 2, avoiding bad downscaling quality.
6088 Matrix frameTransform
;
6089 if (ActiveLayerTracker::IsScaleSubjectToAnimation(aContainerFrame
)) {
6090 scale
.width
= gfxUtils::ClampToScaleFactor(scale
.width
);
6091 scale
.height
= gfxUtils::ClampToScaleFactor(scale
.height
);
6093 // Limit animated scale factors to not grow excessively beyond the
6095 nsSize
maxScale(4, 4);
6096 if (!aVisibleRect
.IsEmpty()) {
6097 nsSize displaySize
=
6098 ComputeDesiredDisplaySizeForAnimation(aContainerFrame
);
6099 maxScale
= Max(maxScale
, displaySize
/ aVisibleRect
.Size());
6101 if (scale
.width
> maxScale
.width
) {
6102 scale
.width
= gfxUtils::ClampToScaleFactor(maxScale
.width
, true);
6104 if (scale
.height
> maxScale
.height
) {
6105 scale
.height
= gfxUtils::ClampToScaleFactor(maxScale
.height
, true);
6108 // XXX Do we need to move nearly-integer values to integers here?
6111 // If the scale factors are too small, just use 1.0. The content is being
6112 // scaled out of sight anyway.
6113 if (fabs(scale
.width
) < 1e-8 || fabs(scale
.height
) < 1e-8) {
6114 scale
= Size(1.0, 1.0);
6117 scale
= Size(1.0, 1.0);
6120 // Prevent the scale from getting too large, to avoid excessive memory
6121 // allocation. Usually memory allocation is limited by the visible region,
6122 // which should be restricted to the display port. But at very large scales
6123 // the visible region itself can become excessive due to rounding errors.
6124 // Clamping the scale here prevents that.
6126 Size(std::min(scale
.width
, 32768.0f
), std::min(scale
.height
, 32768.0f
));
6131 static bool ChooseScaleAndSetTransform(
6132 FrameLayerBuilder
* aLayerBuilder
, nsDisplayListBuilder
* aDisplayListBuilder
,
6133 nsIFrame
* aContainerFrame
, nsDisplayItem
* aContainerItem
,
6134 const nsRect
& aVisibleRect
, const Matrix4x4
* aTransform
,
6135 const ContainerLayerParameters
& aIncomingScale
, ContainerLayer
* aLayer
,
6136 ContainerLayerParameters
& aOutgoingScale
) {
6139 Matrix4x4 transform
=
6140 Matrix4x4::Scaling(aIncomingScale
.mXScale
, aIncomingScale
.mYScale
, 1.0);
6142 // aTransform is applied first, then the scale is applied to the result
6143 transform
= (*aTransform
) * transform
;
6144 // Set any matrix entries close to integers to be those exact integers.
6145 // This protects against floating-point inaccuracies causing problems
6146 // in the checks below.
6147 // We use the fixed epsilon version here because we don't want the nudging
6148 // to depend on the scroll position.
6149 transform
.NudgeToIntegersFixedEpsilon();
6152 if (aContainerFrame
&& aLayerBuilder
->GetContainingPaintedLayerData() &&
6154 (aTransform
->Is2D(&transform2d
) && !transform2d
.HasNonTranslation()))) {
6155 // When we have an inactive ContainerLayer, translate the container by the
6156 // offset to the reference frame (and offset all child layers by the
6157 // reverse) so that the coordinate space of the child layers isn't affected
6158 // by scrolling. This gets confusing for complicated transform (since we'd
6159 // have to compute the scale factors for the matrix), so we don't bother.
6160 // Any frames that are building an nsDisplayTransform for a css transform
6161 // would have 0,0 as their offset to the reference frame, so this doesn't
6163 nsPoint appUnitOffset
=
6164 aDisplayListBuilder
->ToReferenceFrame(aContainerFrame
);
6165 nscoord appUnitsPerDevPixel
=
6166 aContainerFrame
->PresContext()->AppUnitsPerDevPixel();
6167 offset
= nsIntPoint(NS_lround(NSAppUnitsToDoublePixels(
6168 appUnitOffset
.x
, appUnitsPerDevPixel
) *
6169 aIncomingScale
.mXScale
),
6170 NS_lround(NSAppUnitsToDoublePixels(
6171 appUnitOffset
.y
, appUnitsPerDevPixel
) *
6172 aIncomingScale
.mYScale
));
6174 transform
.PostTranslate(offset
.x
+ aIncomingScale
.mOffset
.x
,
6175 offset
.y
+ aIncomingScale
.mOffset
.y
, 0);
6177 if (transform
.IsSingular()) {
6181 bool canDraw2D
= transform
.CanDraw2D(&transform2d
);
6182 Size scale
= FrameLayerBuilder::ChooseScale(
6183 aContainerFrame
, aContainerItem
, aVisibleRect
, aIncomingScale
.mXScale
,
6184 aIncomingScale
.mYScale
, transform2d
, canDraw2D
);
6186 // If this is a transform container layer, then pre-rendering might
6187 // mean we try render a layer bigger than the max texture size. If we have
6188 // tiling, that's not a problem, since we'll automatically choose a tiled
6189 // layer for layers of that size. If not, we need to apply clamping to
6191 if (aTransform
&& !StaticPrefs::layers_enable_tiles_AtStartup()) {
6192 RestrictScaleToMaxLayerSize(scale
, aVisibleRect
, aContainerFrame
, aLayer
);
6195 // Store the inverse of our resolution-scale on the layer
6196 aLayer
->SetBaseTransform(transform
);
6197 aLayer
->SetPreScale(1.0f
/ scale
.width
, 1.0f
/ scale
.height
);
6198 aLayer
->SetInheritedScale(aIncomingScale
.mXScale
, aIncomingScale
.mYScale
);
6200 aOutgoingScale
= ContainerLayerParameters(scale
.width
, scale
.height
, -offset
,
6203 aOutgoingScale
.mInTransformedSubtree
= true;
6204 if (ActiveLayerTracker::IsTransformAnimated(aDisplayListBuilder
,
6206 aOutgoingScale
.mInActiveTransformedSubtree
= true;
6209 if ((aLayerBuilder
->IsBuildingRetainedLayers() &&
6210 (!canDraw2D
|| transform2d
.HasNonIntegerTranslation())) ||
6211 aContainerFrame
->Extend3DContext() ||
6212 aContainerFrame
->Combines3DTransformWithAncestors() ||
6213 // For async transform animation, the value would be changed at
6214 // any time, integer translation is not always true.
6215 aContainerFrame
->HasAnimationOfTransform()) {
6216 aOutgoingScale
.mDisableSubpixelAntialiasingInDescendants
= true;
6221 already_AddRefed
<ContainerLayer
> FrameLayerBuilder::BuildContainerLayerFor(
6222 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
6223 nsIFrame
* aContainerFrame
, nsDisplayItem
* aContainerItem
,
6224 nsDisplayList
* aChildren
, const ContainerLayerParameters
& aParameters
,
6225 const Matrix4x4
* aTransform
, uint32_t aFlags
) {
6226 uint32_t containerDisplayItemKey
=
6227 aContainerItem
? aContainerItem
->GetPerFrameKey() : 0;
6228 NS_ASSERTION(aContainerFrame
,
6229 "Container display items here should have a frame");
6230 NS_ASSERTION(!aContainerItem
|| aContainerItem
->Frame() == aContainerFrame
,
6231 "Container display item must match given frame");
6233 if (!aParameters
.mXScale
|| !aParameters
.mYScale
) {
6237 RefPtr
<ContainerLayer
> containerLayer
;
6238 if (aManager
== mRetainingManager
) {
6239 // Using GetOldLayerFor will search merged frames, as well as the underlying
6240 // frame. The underlying frame can change when a page scrolls, so this
6241 // avoids layer recreation in the situation that a new underlying frame is
6242 // picked for a layer.
6243 Layer
* oldLayer
= nullptr;
6244 if (aContainerItem
) {
6245 oldLayer
= GetOldLayerFor(aContainerItem
);
6247 DisplayItemData
* data
=
6248 GetOldLayerForFrame(aContainerFrame
, containerDisplayItemKey
);
6250 oldLayer
= data
->mLayer
;
6255 NS_ASSERTION(oldLayer
->Manager() == aManager
, "Wrong manager");
6256 if (oldLayer
->HasUserData(&gPaintedDisplayItemLayerUserData
)) {
6257 // The old layer for this item is actually our PaintedLayer
6258 // because we rendered its layer into that PaintedLayer. So we
6259 // don't actually have a retained container layer.
6261 NS_ASSERTION(oldLayer
->GetType() == Layer::TYPE_CONTAINER
,
6262 "Wrong layer type");
6263 containerLayer
= static_cast<ContainerLayer
*>(oldLayer
);
6264 ResetLayerStateForRecycling(containerLayer
);
6268 if (!containerLayer
) {
6269 // No suitable existing layer was found.
6270 containerLayer
= aManager
->CreateContainerLayer();
6271 if (!containerLayer
) return nullptr;
6274 if (aContainerItem
&&
6275 aContainerItem
->GetType() == DisplayItemType::TYPE_SCROLL_INFO_LAYER
) {
6276 // Empty layers only have metadata and should never have display items. We
6277 // early exit because later, invalidation will walk up the frame tree to
6278 // determine which painted layer gets invalidated. Since an empty layer
6279 // should never have anything to paint, it should never be invalidated.
6280 NS_ASSERTION(aChildren
->IsEmpty(), "Should have no children");
6281 return containerLayer
.forget();
6284 const ActiveScrolledRoot
* containerASR
=
6285 aContainerItem
? aContainerItem
->GetActiveScrolledRoot() : nullptr;
6286 const ActiveScrolledRoot
* containerScrollMetadataASR
=
6287 aParameters
.mScrollMetadataASR
;
6288 const ActiveScrolledRoot
* containerCompositorASR
= aParameters
.mCompositorASR
;
6290 ContainerLayerParameters scaleParameters
;
6292 aChildren
->GetClippedBoundsWithRespectToASR(aBuilder
, containerASR
);
6293 nsRect childrenVisible
=
6294 aContainerItem
? aContainerItem
->GetBuildingRectForChildren()
6295 : aContainerFrame
->InkOverflowRectRelativeToSelf();
6296 if (!ChooseScaleAndSetTransform(
6297 this, aBuilder
, aContainerFrame
, aContainerItem
,
6298 bounds
.Intersect(childrenVisible
), aTransform
, aParameters
,
6299 containerLayer
, scaleParameters
)) {
6303 if (mRetainingManager
) {
6304 if (aContainerItem
) {
6305 nsPaintedDisplayItem
* item
= aContainerItem
->AsPaintedDisplayItem();
6306 MOZ_ASSERT(item
, "Only painted display items should build layers");
6308 DisplayItemData
* data
=
6309 GetDisplayItemDataForManager(item
, mRetainingManager
);
6310 StoreDataForFrame(item
, containerLayer
, LayerState::LAYER_ACTIVE
, data
);
6312 StoreDataForFrame(aContainerFrame
, containerDisplayItemKey
,
6313 containerLayer
, LayerState::LAYER_ACTIVE
);
6317 nsIntRect pixBounds
;
6318 nscoord appUnitsPerDevPixel
;
6320 nscolor backgroundColor
= NS_RGBA(0, 0, 0, 0);
6321 if (aFlags
& CONTAINER_ALLOW_PULL_BACKGROUND_COLOR
) {
6322 backgroundColor
= aParameters
.mBackgroundColor
;
6326 ContainerState
state(aBuilder
, aManager
, aManager
->GetLayerBuilder(),
6327 aContainerFrame
, aContainerItem
, bounds
, containerLayer
,
6328 scaleParameters
, backgroundColor
, containerASR
,
6329 containerScrollMetadataASR
, containerCompositorASR
);
6331 state
.ProcessDisplayItems(aChildren
);
6333 // Set CONTENT_COMPONENT_ALPHA if any of our children have it.
6334 // This is suboptimal ... a child could have text that's over transparent
6335 // pixels in its own layer, but over opaque parts of previous siblings.
6336 pixBounds
= state
.ScaleToOutsidePixels(bounds
, false);
6337 appUnitsPerDevPixel
= state
.GetAppUnitsPerDevPixel();
6338 state
.Finish(&flags
, pixBounds
, aChildren
);
6340 // CONTENT_COMPONENT_ALPHA is propogated up to the nearest CONTENT_OPAQUE
6341 // ancestor so that BasicLayerManager knows when to copy the background into
6342 // pushed groups. Accelerated layers managers can't necessarily do this (only
6343 // when the visible region is a simple rect), so we propogate
6344 // CONTENT_COMPONENT_ALPHA_DESCENDANT all the way to the root.
6345 if (flags
& Layer::CONTENT_COMPONENT_ALPHA
) {
6346 flags
|= Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT
;
6349 // Make sure that rounding the visible region out didn't add any area
6351 if (aChildren
->IsOpaque() && !aChildren
->NeedsTransparentSurface()) {
6352 bounds
.ScaleRoundIn(scaleParameters
.mXScale
, scaleParameters
.mYScale
);
6353 if (bounds
.Contains(ToAppUnits(pixBounds
, appUnitsPerDevPixel
))) {
6354 // Clear CONTENT_COMPONENT_ALPHA and add CONTENT_OPAQUE instead.
6355 flags
&= ~Layer::CONTENT_COMPONENT_ALPHA
;
6356 flags
|= Layer::CONTENT_OPAQUE
;
6359 if (nsLayoutUtils::ShouldSnapToGrid(aContainerFrame
)) {
6360 flags
|= Layer::CONTENT_SNAP_TO_GRID
;
6362 containerLayer
->SetContentFlags(flags
);
6363 // If aContainerItem is non-null some BuildContainerLayer further up the
6364 // call stack is responsible for setting containerLayer's visible region.
6365 if (!aContainerItem
) {
6366 containerLayer
->SetVisibleRegion(
6367 LayerIntRegion::FromUnknownRegion(pixBounds
));
6369 if (aParameters
.mLayerContentsVisibleRect
) {
6370 *aParameters
.mLayerContentsVisibleRect
=
6371 pixBounds
+ scaleParameters
.mOffset
;
6374 nsPresContext::ClearNotifySubDocInvalidationData(containerLayer
);
6376 return containerLayer
.forget();
6379 Layer
* FrameLayerBuilder::GetLeafLayerFor(nsDisplayListBuilder
* aBuilder
,
6380 nsDisplayItem
* aItem
) {
6381 Layer
* layer
= GetOldLayerFor(aItem
);
6385 if (layer
->HasUserData(&gPaintedDisplayItemLayerUserData
)) {
6386 // This layer was created to render Thebes-rendered content for this
6387 // display item. The display item should not use it for its own
6391 ResetLayerStateForRecycling(layer
);
6396 void FrameLayerBuilder::InvalidateAllLayers(LayerManager
* aManager
) {
6397 LayerManagerData
* data
= static_cast<LayerManagerData
*>(
6398 aManager
->GetUserData(&gLayerManagerUserData
));
6400 data
->mInvalidateAllLayers
= true;
6405 void FrameLayerBuilder::InvalidateAllLayersForFrame(nsIFrame
* aFrame
) {
6406 for (auto* did
: GetDisplayItemDataArray(aFrame
)) {
6407 DisplayItemData
* data
= DisplayItemData::AssertDisplayItemData(did
);
6408 data
->mParent
->mInvalidateAllLayers
= true;
6413 Layer
* FrameLayerBuilder::GetDedicatedLayer(nsIFrame
* aFrame
,
6414 DisplayItemType aDisplayItemKey
) {
6415 // TODO: This isn't completely correct, since a frame could exist as a layer
6416 // in the normal widget manager, and as a different layer (or no layer)
6417 // in the secondary manager
6418 for (auto* did
: GetDisplayItemDataArray(aFrame
)) {
6419 DisplayItemData
* data
= DisplayItemData::AssertDisplayItemData(did
);
6420 if (!data
->mParent
->mLayerManager
->IsWidgetLayerManager()) {
6424 if (GetDisplayItemTypeFromKey(data
->mDisplayItemKey
) == aDisplayItemKey
) {
6425 if (data
->mOptLayer
) {
6426 return data
->mOptLayer
;
6429 Layer
* layer
= data
->mLayer
;
6430 if (!layer
->HasUserData(&gColorLayerUserData
) &&
6431 !layer
->HasUserData(&gImageLayerUserData
) &&
6432 !layer
->HasUserData(&gPaintedDisplayItemLayerUserData
)) {
6442 void FrameLayerBuilder::EnumerateGenerationForDedicatedLayers(
6443 const nsIFrame
* aFrame
, AnimationGenerationCallback aCallback
) {
6444 std::bitset
<static_cast<uint32_t>(DisplayItemType::TYPE_MAX
)> notFoundTypes
;
6445 for (auto displayItemType
: LayerAnimationInfo::sDisplayItemTypes
) {
6446 notFoundTypes
.set(static_cast<uint32_t>(displayItemType
));
6449 for (auto displayItemType
: LayerAnimationInfo::sDisplayItemTypes
) {
6450 // For transform animations, the animation is on the primary frame but
6451 // |aFrame| is the style frame.
6452 const nsIFrame
* frameToQuery
=
6453 displayItemType
== DisplayItemType::TYPE_TRANSFORM
6454 ? nsLayoutUtils::GetPrimaryFrameFromStyleFrame(aFrame
)
6457 for (auto* did
: GetDisplayItemDataArray(frameToQuery
)) {
6458 DisplayItemData
* data
= DisplayItemData::AssertDisplayItemData(did
);
6459 if (!data
->mParent
->mLayerManager
->IsWidgetLayerManager()) {
6463 if (GetDisplayItemTypeFromKey(data
->mDisplayItemKey
) != displayItemType
) {
6467 notFoundTypes
.reset(static_cast<uint32_t>(displayItemType
));
6469 Maybe
<uint64_t> generation
;
6470 if (data
->mOptLayer
) {
6471 generation
= data
->mOptLayer
->GetAnimationGeneration();
6472 } else if (!data
->mLayer
->HasUserData(&gColorLayerUserData
) &&
6473 !data
->mLayer
->HasUserData(&gImageLayerUserData
) &&
6474 !data
->mLayer
->HasUserData(
6475 &gPaintedDisplayItemLayerUserData
)) {
6476 generation
= data
->mLayer
->GetAnimationGeneration();
6479 if (!aCallback(generation
, displayItemType
)) {
6487 // Bail out if we have already enumerated all possible layers for the given
6488 // display item types.
6489 if (notFoundTypes
.none()) {
6493 // If there are any display item types that the nsIFrame doesn't have, we need
6494 // to call the callback function for them respectively.
6495 for (auto displayItemType
: LayerAnimationInfo::sDisplayItemTypes
) {
6496 if (notFoundTypes
[static_cast<uint32_t>(displayItemType
)] &&
6497 !aCallback(Nothing(), displayItemType
)) {
6503 gfxSize
FrameLayerBuilder::GetPaintedLayerScaleForFrame(nsIFrame
* aFrame
) {
6504 MOZ_ASSERT(aFrame
, "need a frame");
6506 nsPresContext
* presCtx
= aFrame
->PresContext()->GetRootPresContext();
6509 presCtx
= aFrame
->PresContext();
6510 MOZ_ASSERT(presCtx
);
6513 nsIFrame
* root
= presCtx
->PresShell()->GetRootFrame();
6517 float resolution
= presCtx
->PresShell()->GetResolution();
6519 Matrix4x4Flagged transform
= Matrix4x4::Scaling(resolution
, resolution
, 1.0);
6520 if (aFrame
!= root
) {
6521 // aTransform is applied first, then the scale is applied to the result
6522 transform
= nsLayoutUtils::GetTransformToAncestor(RelativeTo
{aFrame
},
6528 if (transform
.CanDraw2D(&transform2d
)) {
6529 return ThebesMatrix(transform2d
).ScaleFactors();
6532 return gfxSize(1.0, 1.0);
6535 #ifdef MOZ_DUMP_PAINTING
6536 static void DebugPaintItem(DrawTarget
& aDrawTarget
, nsPresContext
* aPresContext
,
6537 nsPaintedDisplayItem
* aItem
,
6538 nsDisplayListBuilder
* aBuilder
) {
6540 Rect bounds
= NSRectToRect(aItem
->GetBounds(aBuilder
, &snap
),
6541 aPresContext
->AppUnitsPerDevPixel());
6543 const IntSize size
= IntSize::Truncate(bounds
.width
, bounds
.height
);
6544 if (size
.IsEmpty()) {
6548 RefPtr
<DrawTarget
> tempDT
=
6549 aDrawTarget
.CreateSimilarDrawTarget(size
, SurfaceFormat::B8G8R8A8
);
6550 RefPtr
<gfxContext
> context
= gfxContext::CreateOrNull(tempDT
);
6552 // Leave this as crash, it's in the debugging code, we want to know
6553 gfxDevCrash(LogReason::InvalidContext
)
6554 << "DebugPaintItem context problem " << gfx::hexa(tempDT
);
6557 context
->SetMatrix(Matrix::Translation(-bounds
.x
, -bounds
.y
));
6559 aItem
->Paint(aBuilder
, context
);
6560 RefPtr
<SourceSurface
> surface
= tempDT
->Snapshot();
6561 DumpPaintedImage(aItem
, surface
);
6563 aDrawTarget
.DrawSurface(surface
, bounds
, Rect(Point(0, 0), bounds
.Size()));
6565 aItem
->SetPainted();
6570 void FrameLayerBuilder::RecomputeVisibilityForItems(
6571 std::vector
<AssignedDisplayItem
>& aItems
, nsDisplayListBuilder
* aBuilder
,
6572 const nsIntRegion
& aRegionToDraw
, nsRect
& aPreviousRectToDraw
,
6573 const nsIntPoint
& aOffset
, int32_t aAppUnitsPerDevPixel
, float aXScale
,
6575 // Update visible regions. We perform visibility analysis to take account
6576 // of occlusion culling.
6577 nsRegion visible
= aRegionToDraw
.ToAppUnits(aAppUnitsPerDevPixel
);
6578 visible
.MoveBy(NSIntPixelsToAppUnits(aOffset
.x
, aAppUnitsPerDevPixel
),
6579 NSIntPixelsToAppUnits(aOffset
.y
, aAppUnitsPerDevPixel
));
6580 visible
.ScaleInverseRoundOut(aXScale
, aYScale
);
6582 // We're going to read from previousRectToDraw for every iteration, let's do
6583 // that on the stack, and just update the heap allocated one now. By the end
6584 // of this function {visible} will have been modified by occlusion culling.
6585 nsRect previousRectToDraw
= aPreviousRectToDraw
;
6586 aPreviousRectToDraw
= visible
.GetBounds();
6588 for (uint32_t i
= aItems
.size(); i
> 0; --i
) {
6589 AssignedDisplayItem
* cdi
= &aItems
[i
- 1];
6594 if (cdi
->mHasPaintRect
&&
6595 !cdi
->mContentRect
.Intersects(visible
.GetBounds()) &&
6596 !cdi
->mContentRect
.Intersects(previousRectToDraw
)) {
6600 if (IsEffectEndMarker(cdi
->mType
) || cdi
->HasOpacity() ||
6601 cdi
->HasTransform()) {
6602 // The visibility calculations are skipped when the item is an effect end
6603 // marker, or when the display item is within a flattened effect group.
6604 // This is because RecomputeVisibility has already been called for the
6605 // group item, and all the children.
6609 const DisplayItemClip
& clip
= cdi
->mItem
->GetClip();
6611 NS_ASSERTION(AppUnitsPerDevPixel(cdi
->mItem
) == aAppUnitsPerDevPixel
,
6612 "a painted layer should contain items only at the same zoom");
6614 MOZ_ASSERT(clip
.HasClip() || clip
.GetRoundedRectCount() == 0,
6615 "If we have rounded rects, we must have a clip rect");
6617 if (!clip
.IsRectAffectedByClip(visible
.GetBounds())) {
6618 cdi
->mItem
->RecomputeVisibility(aBuilder
, &visible
);
6622 // Do a little dance to account for the fact that we're clipping
6623 // to cdi->mClipRect
6625 clipped
.And(visible
, clip
.NonRoundedIntersection());
6626 nsRegion finalClipped
= clipped
;
6627 cdi
->mItem
->RecomputeVisibility(aBuilder
, &finalClipped
);
6628 // If we have rounded clip rects, don't subtract from the visible
6629 // region since we aren't displaying everything inside the rect.
6630 if (clip
.GetRoundedRectCount() == 0) {
6632 removed
.Sub(clipped
, finalClipped
);
6633 nsRegion newVisible
;
6634 newVisible
.Sub(visible
, removed
);
6635 // Don't let the visible region get too complex.
6636 if (newVisible
.GetNumRects() <= 15) {
6637 visible
= std::move(newVisible
);
6644 * Tracks and caches the item clip.
6646 struct ItemClipTracker
{
6647 explicit ItemClipTracker(gfxContext
* aContext
,
6648 const int32_t aAppUnitsPerDevPixel
)
6649 : mContext(aContext
),
6651 mAppUnitsPerDevPixel(aAppUnitsPerDevPixel
) {}
6654 * Returns true if a clip is set.
6656 bool HasClip() const { return mHasClip
; }
6659 * Returns true if the given |aClip| is set.
6661 bool HasClip(const DisplayItemClip
* aClip
) const {
6662 MOZ_ASSERT(aClip
&& aClip
->HasClip());
6663 return mHasClip
&& mCurrentClip
== *aClip
;
6667 * Removes the clip, if there is one.
6670 if (mCurrentClip
.HasClip()) {
6671 mCurrentClip
= DisplayItemClip::NoClip();
6678 mContext
->Restore();
6683 * Sets the clip to |aClip|, if it is not set already.
6685 void ChangeClipIfNeeded(const DisplayItemClip
* aClip
) {
6686 MOZ_ASSERT(aClip
&& aClip
->HasClip());
6688 if (HasClip(aClip
)) {
6689 // Reuse the old clip.
6693 // Remove the previous clip and save the current state.
6697 // Apply the new clip.
6699 mCurrentClip
= *aClip
;
6700 mCurrentClip
.ApplyTo(mContext
, mAppUnitsPerDevPixel
);
6701 mContext
->NewPath();
6705 gfxContext
* mContext
;
6707 const int32_t mAppUnitsPerDevPixel
;
6709 DisplayItemClip mCurrentClip
;
6713 * Tracks clips managed by |PushClip()| and |PopClip()|.
6714 * If allowed by the caller, the top clip may be reused when a new clip that
6715 * matches the previous one is pushed to the stack.
6718 explicit ClipStack(gfxContext
* aContext
, const int32_t aAppUnitsPerDevPixel
)
6719 : mContext(aContext
),
6720 mAppUnitsPerDevPixel(aAppUnitsPerDevPixel
),
6721 mDeferredPopClip(false) {}
6724 MOZ_ASSERT(!mDeferredPopClip
);
6725 MOZ_ASSERT(!HasClips());
6729 * Returns true if there are clips set.
6731 bool HasClips() const { return mClips
.Length() > 0; }
6734 * Returns the clip at the top of the stack.
6736 const DisplayItemClip
& TopClip() const {
6737 MOZ_ASSERT(HasClips());
6738 return mClips
.LastElement();
6742 * Returns true if the top clip matches the given |aClip|.
6744 bool TopClipMatches(const DisplayItemClip
& aClip
) {
6745 return HasClips() && TopClip() == aClip
;
6749 * Pops the current top clip. If |aDeferPopClip| is true, the top clip will
6750 * not be popped before the next call to |PopClip(false)|.
6751 * This allows the previously set clip to be reused during the next
6752 * |PushClip()| call, if the new clip is identical with the top clip.
6754 void PopClip(bool aDeferPopClip
) {
6755 MOZ_ASSERT(HasClips());
6757 if (aDeferPopClip
) {
6758 // Do not allow reusing clip with nested effects.
6759 MOZ_ASSERT(!mDeferredPopClip
);
6760 mDeferredPopClip
= true;
6764 if (TopClip().HasClip()) {
6765 mContext
->Restore();
6768 mClips
.RemoveLastElement();
6769 mDeferredPopClip
= false;
6773 * Pops the clip, if a call to |PopClip()| has been deferred.
6775 void PopDeferredClip() {
6776 if (mDeferredPopClip
) {
6782 * Pushes the given |aClip| to the stack.
6784 void PushClip(const DisplayItemClip
& aClip
) {
6785 if (mDeferredPopClip
&& TopClipMatches(aClip
)) {
6786 // Reuse this clip. Defer the decision to reuse it again until the next
6787 // call to PopClip().
6788 mDeferredPopClip
= false;
6794 mClips
.AppendElement(aClip
);
6796 // Save the current state and apply new clip, if needed.
6797 if (aClip
.HasClip()) {
6799 aClip
.ApplyTo(mContext
, mAppUnitsPerDevPixel
);
6800 mContext
->NewPath();
6805 gfxContext
* mContext
;
6806 const int32_t mAppUnitsPerDevPixel
;
6807 AutoTArray
<DisplayItemClip
, 2> mClips
;
6808 bool mDeferredPopClip
;
6812 * Returns a clip for the given |aItem|. If the clip can be simplified to not
6813 * include rounded rects, |aOutClip| is used to store the simplified clip.
6815 static const DisplayItemClip
* GetItemClip(const nsDisplayItem
* aItem
,
6816 DisplayItemClip
& aOutClip
) {
6817 const DisplayItemClip
& clip
= aItem
->GetClip();
6819 if (!clip
.HasClip()) {
6823 if (clip
.GetRoundedRectCount() > 0 &&
6824 !clip
.IsRectClippedByRoundedCorner(aItem
->GetPaintRect())) {
6825 aOutClip
.SetTo(clip
.GetClipRect());
6833 * Pushes a new opacity group for |aContext| based on |aItem|.
6835 static void PushOpacity(gfxContext
* aContext
, AssignedDisplayItem
& aItem
) {
6836 MOZ_ASSERT(aItem
.mType
== DisplayItemEntryType::PushOpacity
||
6837 aItem
.mType
== DisplayItemEntryType::PushOpacityWithBg
);
6838 MOZ_ASSERT(aItem
.mItem
->GetType() == DisplayItemType::TYPE_OPACITY
);
6839 nsDisplayOpacity
* item
= static_cast<nsDisplayOpacity
*>(aItem
.mItem
);
6841 const float opacity
= item
->GetOpacity();
6842 if (aItem
.mType
== DisplayItemEntryType::PushOpacityWithBg
) {
6843 aContext
->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA
, opacity
);
6845 aContext
->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA
, opacity
);
6850 * Pushes the transformation matrix of |aItem| into |aMatrixStack| and sets the
6851 * accumulated transform as the current transformation matrix for |aContext|.
6853 static void PushTransform(gfxContext
* aContext
, AssignedDisplayItem
& aItem
,
6854 nsDisplayListBuilder
* aBuilder
,
6855 MatrixStack4x4
& aMatrixStack
,
6856 const Matrix4x4Flagged
& aBaseMatrix
) {
6857 MOZ_ASSERT(aItem
.mType
== DisplayItemEntryType::PushTransform
);
6858 MOZ_ASSERT(aItem
.mItem
->GetType() == DisplayItemType::TYPE_TRANSFORM
);
6860 nsDisplayTransform
* item
= static_cast<nsDisplayTransform
*>(aItem
.mItem
);
6861 if (item
->ShouldSkipTransform(aBuilder
)) {
6862 aMatrixStack
.Push(Matrix4x4Flagged());
6864 aMatrixStack
.Push(item
->GetTransformForRendering());
6867 gfx::Matrix4x4Flagged matrix
= aMatrixStack
.CurrentMatrix() * aBaseMatrix
;
6868 gfx::Matrix matrix2d
;
6869 DebugOnly
<bool> ok
= matrix
.CanDraw2D(&matrix2d
);
6872 aContext
->SetMatrix(matrix2d
);
6875 static void UpdateEffectTracking(int& aOpacityLevel
, int& aTransformLevel
,
6876 const DisplayItemEntryType aType
) {
6878 case DisplayItemEntryType::PushOpacity
:
6879 case DisplayItemEntryType::PushOpacityWithBg
:
6882 case DisplayItemEntryType::PopOpacity
:
6885 case DisplayItemEntryType::PushTransform
:
6888 case DisplayItemEntryType::PopTransform
:
6895 MOZ_ASSERT(aOpacityLevel
>= 0 && aTransformLevel
>= 0);
6898 void FrameLayerBuilder::PaintItems(std::vector
<AssignedDisplayItem
>& aItems
,
6899 const nsIntRect
& aRect
, gfxContext
* aContext
,
6900 nsDisplayListBuilder
* aBuilder
,
6901 nsPresContext
* aPresContext
,
6902 const nsIntPoint
& aOffset
, float aXScale
,
6904 DrawTarget
& aDrawTarget
= *aContext
->GetDrawTarget();
6906 int32_t appUnitsPerDevPixel
= aPresContext
->AppUnitsPerDevPixel();
6907 nsRect boundRect
= ToAppUnits(aRect
, appUnitsPerDevPixel
);
6908 boundRect
.MoveBy(NSIntPixelsToAppUnits(aOffset
.x
, appUnitsPerDevPixel
),
6909 NSIntPixelsToAppUnits(aOffset
.y
, appUnitsPerDevPixel
));
6910 boundRect
.ScaleInverseRoundOut(aXScale
, aYScale
);
6912 if (boundRect
.IsEmpty()) {
6913 // Hack! This can happen if the conversion of |aRect| to scaled and offset
6914 // app units overflowed. Ideally the conversion would detect this and handle
6915 // such situations gracefully. For now, do nothing.
6920 // Tracks effect nesting level. These are used to track that every effect
6921 // start marker has a corresponding end marker.
6922 int opacityLevel
= 0;
6923 int transformLevel
= 0;
6926 // Tracks effect nesting level for skipping items between effect markers,
6927 // when the effect display item does not intersect with the invalidated area.
6928 int emptyEffectLevel
= 0;
6930 // Stores a simplified version of the item clip, if needed.
6931 DisplayItemClip temporaryClip
;
6933 // Two types of clips are used during PaintItems(): clips for items and clips
6934 // for effects. Item clips are always the most recent clip set, and they are
6935 // never nested. The previous item clip is reused, if the next item has the
6936 // same clip. Item clips are removed when an effect starts or ends.
6937 ItemClipTracker
itemClipTracker(aContext
, appUnitsPerDevPixel
);
6939 // Since effects can be nested, the effect clips need to be nested as well.
6940 // They are pushed for effect start marker, and popped for effect end marker.
6941 // Effect clips are tracked by |effectClipStack|. If there are consecutive
6942 // effects with the same clip, |effectClipStack| defers popping the clip for
6943 // the first end marker, and tries to reuse the previously set clip, when
6944 // processing the start marker for the next effect.
6945 ClipStack
effectClipStack(aContext
, appUnitsPerDevPixel
);
6947 MatrixStack4x4 matrixStack
;
6948 const Matrix4x4Flagged base
= Matrix4x4::From2D(aContext
->CurrentMatrix());
6950 for (uint32_t i
= 0; i
< aItems
.size(); ++i
) {
6951 AssignedDisplayItem
& cdi
= aItems
[i
];
6952 nsDisplayItem
* item
= cdi
.mItem
;
6954 const auto NextItemStartsEffect
= [&]() {
6955 const uint32_t next
= i
+ 1;
6956 return next
< aItems
.size() && IsEffectStartMarker(aItems
[next
].mType
);
6960 MOZ_ASSERT(cdi
.mType
== DisplayItemEntryType::Item
);
6964 nsRect visibleRect
= item
->GetPaintRect();
6966 if (matrixStack
.HasTransform()) {
6967 MOZ_ASSERT(transformLevel
> 0);
6969 if (IsEffectEndMarker(cdi
.mType
)) {
6970 // Always process the effect end markers.
6971 visibleRect
= boundRect
;
6973 const Matrix4x4Flagged
& matrix
= matrixStack
.CurrentMatrix();
6974 visibleRect
= nsLayoutUtils::MatrixTransformRect(visibleRect
, matrix
,
6975 appUnitsPerDevPixel
);
6979 const nsRect paintRect
= visibleRect
.Intersect(boundRect
);
6981 if (paintRect
.IsEmpty() || emptyEffectLevel
> 0) {
6982 // In order for this branch to be hit, either this item has an empty paint
6983 // rect and nothing would be drawn, or an effect marker before this
6984 // item had an empty paint rect. In the latter case, the items are skipped
6985 // until effect POP markers bring |emptyEffectLevel| back to 0.
6986 UpdateEffectTracking(emptyEffectLevel
, emptyEffectLevel
, cdi
.mType
);
6988 // Sometimes the item that was going to reuse the previous clip is culled.
6989 // Since |PushClip()| is never called for culled items, pop the clip now.
6990 effectClipStack
.PopDeferredClip();
6994 #ifdef MOZ_DUMP_PAINTING
6995 AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
6996 "FrameLayerBuilder::PaintItems", GRAPHICS_Rasterization
, item
->Name());
6998 AUTO_PROFILER_LABEL("FrameLayerBuilder::PaintItems",
6999 GRAPHICS_Rasterization
);
7002 MOZ_ASSERT((opacityLevel
== 0 && !cdi
.HasOpacity()) ||
7003 (opacityLevel
> 0 && cdi
.HasOpacity()) ||
7004 (transformLevel
== 0 && !cdi
.HasTransform()) ||
7005 (transformLevel
> 0 && cdi
.HasTransform()));
7007 if (cdi
.mType
!= DisplayItemEntryType::Item
) {
7008 // If we are processing an effect marker, remove the current item clip, if
7010 itemClipTracker
.Restore();
7013 if (cdi
.mType
== DisplayItemEntryType::PushOpacity
||
7014 cdi
.mType
== DisplayItemEntryType::PushOpacityWithBg
) {
7015 // To avoid pushing large temporary surfaces, it is important to clip
7016 // opacity group with both the paint rect and the actual opacity clip.
7017 DisplayItemClip effectClip
;
7018 effectClip
.SetTo(item
->GetPaintRect());
7019 effectClip
.IntersectWith(item
->GetClip());
7020 effectClipStack
.PushClip(effectClip
);
7021 PushOpacity(aContext
, cdi
);
7024 if (cdi
.mType
== DisplayItemEntryType::PopOpacity
) {
7025 MOZ_ASSERT(opacityLevel
> 0);
7026 aContext
->PopGroupAndBlend();
7029 if (cdi
.mType
== DisplayItemEntryType::PushTransform
) {
7030 effectClipStack
.PushClip(item
->GetClip());
7032 PushTransform(aContext
, cdi
, aBuilder
, matrixStack
, base
);
7035 if (cdi
.mType
== DisplayItemEntryType::PopTransform
) {
7036 MOZ_ASSERT(transformLevel
> 0);
7038 aContext
->Restore();
7041 if (IsEffectEndMarker(cdi
.mType
)) {
7042 // Pop the clip for the effect.
7043 MOZ_ASSERT(effectClipStack
.HasClips());
7045 // If the next item starts an effect, defer popping the current clip, and
7046 // try to reuse it during the next call to |PushClip()|. Trying to reuse
7047 // clips between nested effects would be difficult, for example due to
7048 // possibly different coordinate system, so this optimization is limited
7049 // to consecutive effects.
7050 effectClipStack
.PopClip(NextItemStartsEffect());
7053 if (cdi
.mType
!= DisplayItemEntryType::Item
) {
7055 UpdateEffectTracking(opacityLevel
, transformLevel
, cdi
.mType
);
7057 // Nothing more to do with effect markers.
7061 const bool paintAsLayer
= cdi
.mInactiveLayerData
.get();
7062 nsPaintedDisplayItem
* paintedItem
= item
->AsPaintedDisplayItem();
7063 MOZ_ASSERT(paintAsLayer
|| paintedItem
,
7064 "The display item does not support painting");
7066 const DisplayItemClip
* itemClip
= GetItemClip(item
, temporaryClip
);
7067 bool itemPaintsOwnClip
= false;
7069 if (itemClip
&& !itemClipTracker
.HasClip(itemClip
)) {
7070 // The clip has changed. Remove the previous clip.
7071 itemClipTracker
.Restore();
7073 // Check if the item supports painting with clip.
7075 paintAsLayer
? false : paintedItem
->CanPaintWithClip(*itemClip
);
7077 if (!itemPaintsOwnClip
) {
7078 // Item does not support painting with clip, set the clip.
7079 itemClipTracker
.ChangeClipIfNeeded(itemClip
);
7084 // Item does not need clipping, remove the clip if there is one.
7085 itemClipTracker
.Restore();
7089 bool saved
= aDrawTarget
.GetPermitSubpixelAA();
7090 PaintInactiveLayer(aBuilder
, cdi
.mInactiveLayerData
->mLayerManager
, item
,
7091 aContext
, aContext
);
7092 aDrawTarget
.SetPermitSubpixelAA(saved
);
7096 nsIFrame
* frame
= item
->Frame();
7097 if (aBuilder
->IsPaintingToWindow()) {
7098 frame
->AddStateBits(NS_FRAME_PAINTED_THEBES
);
7101 #ifdef MOZ_DUMP_PAINTING
7102 if (gfxEnv::DumpPaintItems()) {
7103 DebugPaintItem(aDrawTarget
, aPresContext
, paintedItem
, aBuilder
);
7108 if (itemPaintsOwnClip
) {
7109 MOZ_ASSERT(itemClip
);
7110 paintedItem
->PaintWithClip(aBuilder
, aContext
, *itemClip
);
7112 paintedItem
->Paint(aBuilder
, aContext
);
7116 itemClipTracker
.Restore();
7118 MOZ_ASSERT(opacityLevel
== 0);
7119 MOZ_ASSERT(transformLevel
== 0);
7120 MOZ_ASSERT(emptyEffectLevel
== 0);
7124 * Returns true if it is preferred to draw the list of display
7125 * items separately for each rect in the visible region rather
7126 * than clipping to a complex region.
7128 static bool ShouldDrawRectsSeparately(DrawTarget
* aDrawTarget
,
7129 DrawRegionClip aClip
) {
7130 if (!StaticPrefs::layout_paint_rects_separately_AtStartup() ||
7131 aClip
== DrawRegionClip::NONE
) {
7135 return !aDrawTarget
->SupportsRegionClipping();
7138 static void DrawForcedBackgroundColor(DrawTarget
& aDrawTarget
,
7139 const IntRect
& aBounds
,
7140 nscolor aBackgroundColor
) {
7141 if (NS_GET_A(aBackgroundColor
) > 0) {
7142 ColorPattern
color(ToDeviceColor(aBackgroundColor
));
7143 aDrawTarget
.FillRect(Rect(aBounds
), color
);
7148 * A note on residual transforms:
7150 * In a transformed subtree we sometimes apply the PaintedLayer's
7151 * "residual transform" when drawing content into the PaintedLayer.
7152 * This is a translation by components in the range [-0.5,0.5) provided
7153 * by the layer system; applying the residual transform followed by the
7154 * transforms used by layer compositing ensures that the subpixel alignment
7155 * of the content of the PaintedLayer exactly matches what it would be if
7156 * we used cairo/Thebes to draw directly to the screen without going through
7157 * retained layer buffers.
7159 * The visible and valid regions of the PaintedLayer are computed without
7160 * knowing the residual transform (because we don't know what the residual
7161 * transform is going to be until we've built the layer tree!). So we have to
7162 * consider whether content painted in the range [x, xmost) might be painted
7163 * outside the visible region we computed for that content. The visible region
7164 * would be [floor(x), ceil(xmost)). The content would be rendered at
7165 * [x + r, xmost + r), where -0.5 <= r < 0.5. So some half-rendered pixels could
7166 * indeed fall outside the computed visible region, which is not a big deal;
7167 * similar issues already arise when we snap cliprects to nearest pixels.
7168 * Note that if the rendering of the content is snapped to nearest pixels ---
7169 * which it often is --- then the content is actually rendered at
7170 * [snap(x + r), snap(xmost + r)). It turns out that floor(x) <= snap(x + r)
7171 * and ceil(xmost) >= snap(xmost + r), so the rendering of snapped content
7172 * always falls within the visible region we computed.
7176 void FrameLayerBuilder::DrawPaintedLayer(PaintedLayer
* aLayer
,
7177 gfxContext
* aContext
,
7178 const nsIntRegion
& aRegionToDraw
,
7179 const nsIntRegion
& aDirtyRegion
,
7180 DrawRegionClip aClip
,
7181 const nsIntRegion
& aRegionToInvalidate
,
7182 void* aCallbackData
) {
7183 DrawTarget
& aDrawTarget
= *aContext
->GetDrawTarget();
7185 AUTO_PROFILER_LABEL("FrameLayerBuilder::DrawPaintedLayer",
7186 GRAPHICS_Rasterization
);
7188 nsDisplayListBuilder
* builder
=
7189 static_cast<nsDisplayListBuilder
*>(aCallbackData
);
7191 FrameLayerBuilder
* layerBuilder
= aLayer
->Manager()->GetLayerBuilder();
7192 NS_ASSERTION(layerBuilder
, "Unexpectedly null layer builder!");
7194 auto* userData
= GetPaintedDisplayItemLayerUserData(aLayer
);
7195 NS_ASSERTION(userData
, "where did our user data go?");
7196 if (!userData
->mContainerLayerFrame
) {
7200 bool shouldDrawRectsSeparately
=
7201 ShouldDrawRectsSeparately(&aDrawTarget
, aClip
);
7203 if (!shouldDrawRectsSeparately
) {
7204 if (aClip
== DrawRegionClip::DRAW
) {
7205 gfxUtils::ClipToRegion(aContext
, aRegionToDraw
);
7208 DrawForcedBackgroundColor(aDrawTarget
, aRegionToDraw
.GetBounds(),
7209 userData
->mForcedBackgroundColor
);
7212 // make the origin of the context coincide with the origin of the
7214 gfxContextMatrixAutoSaveRestore
saveMatrix(aContext
);
7215 nsIntPoint offset
= GetTranslationForPaintedLayer(aLayer
);
7216 nsPresContext
* presContext
= userData
->mContainerLayerFrame
->PresContext();
7218 if (!userData
->mVisibilityComputedRegion
.Contains(aDirtyRegion
) &&
7219 !layerBuilder
->GetContainingPaintedLayerData()) {
7220 // Recompute visibility of items in our PaintedLayer, if required. Note
7221 // that this recomputes visibility for all descendants of our display
7222 // items too, so there's no need to do this for the items in inactive
7223 // PaintedLayers. If aDirtyRegion has not changed since the previous call
7224 // then we can skip this.
7225 int32_t appUnitsPerDevPixel
= presContext
->AppUnitsPerDevPixel();
7226 RecomputeVisibilityForItems(userData
->mItems
, builder
, aDirtyRegion
,
7227 userData
->mPreviousRecomputeVisibilityRect
,
7228 offset
, appUnitsPerDevPixel
, userData
->mXScale
,
7230 userData
->mVisibilityComputedRegion
= aDirtyRegion
;
7233 if (shouldDrawRectsSeparately
) {
7234 for (auto iter
= aRegionToDraw
.RectIter(); !iter
.Done(); iter
.Next()) {
7235 const nsIntRect
& iterRect
= iter
.Get();
7236 gfxContextAutoSaveRestore
save(aContext
);
7237 aContext
->NewPath();
7238 aContext
->Rectangle(ThebesRect(iterRect
));
7241 DrawForcedBackgroundColor(aDrawTarget
, iterRect
,
7242 userData
->mForcedBackgroundColor
);
7244 // Apply the residual transform if it has been enabled, to ensure that
7245 // snapping when we draw into aContext exactly matches the ideal
7246 // transform. See above for why this is OK.
7247 aContext
->SetMatrixDouble(
7248 aContext
->CurrentMatrixDouble()
7249 .PreTranslate(aLayer
->GetResidualTranslation() -
7250 gfxPoint(offset
.x
, offset
.y
))
7251 .PreScale(userData
->mXScale
, userData
->mYScale
));
7253 layerBuilder
->PaintItems(userData
->mItems
, iterRect
, aContext
, builder
,
7254 presContext
, offset
, userData
->mXScale
,
7256 if (StaticPrefs::gfx_logging_painted_pixel_count_enabled()) {
7257 aLayer
->Manager()->AddPaintedPixelCount(iterRect
.Area());
7261 // Apply the residual transform if it has been enabled, to ensure that
7262 // snapping when we draw into aContext exactly matches the ideal transform.
7263 // See above for why this is OK.
7264 aContext
->SetMatrixDouble(
7265 aContext
->CurrentMatrixDouble()
7266 .PreTranslate(aLayer
->GetResidualTranslation() -
7267 gfxPoint(offset
.x
, offset
.y
))
7268 .PreScale(userData
->mXScale
, userData
->mYScale
));
7270 layerBuilder
->PaintItems(userData
->mItems
, aRegionToDraw
.GetBounds(),
7271 aContext
, builder
, presContext
, offset
,
7272 userData
->mXScale
, userData
->mYScale
);
7273 if (StaticPrefs::gfx_logging_painted_pixel_count_enabled()) {
7274 aLayer
->Manager()->AddPaintedPixelCount(aRegionToDraw
.GetBounds().Area());
7278 bool isActiveLayerManager
= !aLayer
->Manager()->IsInactiveLayerManager();
7280 if (presContext
->GetPaintFlashing() && isActiveLayerManager
) {
7281 gfxContextAutoSaveRestore
save(aContext
);
7282 if (shouldDrawRectsSeparately
) {
7283 if (aClip
== DrawRegionClip::DRAW
) {
7284 gfxUtils::ClipToRegion(aContext
, aRegionToDraw
);
7287 FlashPaint(aContext
);
7290 if (presContext
->GetDocShell() && isActiveLayerManager
) {
7291 nsDocShell
* docShell
= static_cast<nsDocShell
*>(presContext
->GetDocShell());
7292 RefPtr
<TimelineConsumers
> timelines
= TimelineConsumers::Get();
7294 if (timelines
&& timelines
->HasConsumer(docShell
)) {
7295 timelines
->AddMarkerForDocShell(
7296 docShell
, MakeUnique
<LayerTimelineMarker
>(aRegionToDraw
));
7300 if (!aRegionToInvalidate
.IsEmpty()) {
7301 aLayer
->AddInvalidRect(aRegionToInvalidate
.GetBounds());
7306 void FrameLayerBuilder::DumpRetainedLayerTree(LayerManager
* aManager
,
7307 std::stringstream
& aStream
,
7309 aManager
->Dump(aStream
, "", aDumpHtml
);
7312 nsDisplayItemGeometry
* FrameLayerBuilder::GetMostRecentGeometry(
7313 nsDisplayItem
* aItem
) {
7314 // Retrieve the array of DisplayItemData associated with our frame.
7316 // Find our display item data, if it exists, and return its geometry.
7317 // We first check for ones with an inactive manager, since items that
7318 // create inactive layers will create two DisplayItemData entries,
7319 // and we want the outer one.
7320 DisplayItemData
* firstMatching
= nullptr;
7321 uint32_t itemPerFrameKey
= aItem
->GetPerFrameKey();
7323 for (auto* did
: GetDisplayItemDataArray(aItem
->Frame())) {
7324 auto* data
= DisplayItemData::AssertDisplayItemData(did
);
7325 if (data
->GetDisplayItemKey() == itemPerFrameKey
) {
7326 if (data
->InactiveManager()) {
7327 return data
->GetGeometry();
7329 if (!firstMatching
) {
7330 firstMatching
= data
;
7335 if (firstMatching
) {
7336 return firstMatching
->GetGeometry();
7339 if (RefPtr
<WebRenderFallbackData
> data
=
7340 GetWebRenderUserData
<WebRenderFallbackData
>(aItem
->Frame(),
7342 return data
->GetGeometry();
7348 static gfx::Rect
CalculateBounds(
7349 const nsTArray
<DisplayItemClip::RoundedRect
>& aRects
,
7350 int32_t aAppUnitsPerDevPixel
) {
7351 nsRect bounds
= aRects
[0].mRect
;
7352 for (uint32_t i
= 1; i
< aRects
.Length(); ++i
) {
7353 bounds
.UnionRect(bounds
, aRects
[i
].mRect
);
7356 return gfx::Rect(bounds
.ToNearestPixels(aAppUnitsPerDevPixel
));
7359 void ContainerState::SetupMaskLayer(Layer
* aLayer
,
7360 const DisplayItemClip
& aClip
) {
7361 // don't build an unnecessary mask
7362 if (aClip
.GetRoundedRectCount() == 0) {
7366 RefPtr
<Layer
> maskLayer
= CreateMaskLayer(aLayer
, aClip
, Nothing());
7372 aLayer
->SetMaskLayer(maskLayer
);
7375 static MaskLayerUserData
* GetMaskLayerUserData(Layer
* aMaskLayer
) {
7380 return static_cast<MaskLayerUserData
*>(
7381 aMaskLayer
->GetUserData(&gMaskLayerUserData
));
7384 static void SetMaskLayerUserData(Layer
* aMaskLayer
) {
7385 MOZ_ASSERT(aMaskLayer
);
7387 aMaskLayer
->SetUserData(&gMaskLayerUserData
, new MaskLayerUserData());
7390 already_AddRefed
<Layer
> ContainerState::CreateMaskLayer(
7391 Layer
* aLayer
, const DisplayItemClip
& aClip
,
7392 const Maybe
<size_t>& aForAncestorMaskLayer
) {
7393 // aLayer will never be the container layer created by an
7394 // nsDisplayMasksAndClipPaths because nsDisplayMasksAndClipPaths propagates
7395 // the DisplayItemClip to its contents and is not clipped itself.
7396 // This assertion will fail if that ever stops being the case.
7397 MOZ_ASSERT(!aLayer
->GetUserData(&gCSSMaskLayerUserData
),
7398 "A layer contains round clips should not have css-mask on it.");
7400 // check if we can re-use the mask layer
7401 RefPtr
<ImageLayer
> maskLayer
= CreateOrRecycleMaskImageLayerFor(
7402 MaskLayerKey(aLayer
, aForAncestorMaskLayer
), GetMaskLayerUserData
,
7403 SetMaskLayerUserData
);
7404 MaskLayerUserData
* userData
= GetMaskLayerUserData(maskLayer
.get());
7406 int32_t A2D
= mContainerFrame
->PresContext()->AppUnitsPerDevPixel();
7407 MaskLayerUserData
newData(aClip
, A2D
, mParameters
);
7408 if (*userData
== newData
) {
7409 return maskLayer
.forget();
7412 gfx::Rect boundingRect
=
7413 CalculateBounds(newData
.mRoundedClipRects
, newData
.mAppUnitsPerDevPixel
);
7414 boundingRect
.Scale(mParameters
.mXScale
, mParameters
.mYScale
);
7415 if (boundingRect
.IsEmpty()) {
7416 // Return early if we know that there is effectively no visible data.
7420 uint32_t maxSize
= mManager
->GetMaxTextureSize();
7421 NS_ASSERTION(maxSize
> 0, "Invalid max texture size");
7422 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
7423 // Make mask image width aligned to 4. See Bug 1245552.
7424 gfx::Size
surfaceSize(
7425 std::min
<gfx::Float
>(
7426 GetAlignedStride
<4>(NSToIntCeil(boundingRect
.Width()), 1), maxSize
),
7427 std::min
<gfx::Float
>(boundingRect
.Height(), maxSize
));
7429 gfx::Size
surfaceSize(std::min
<gfx::Float
>(boundingRect
.Width(), maxSize
),
7430 std::min
<gfx::Float
>(boundingRect
.Height(), maxSize
));
7433 // maskTransform is applied to the clip when it is painted into the mask (as a
7434 // component of imageTransform), and its inverse used when the mask is used
7435 // for masking. It is the transform from the masked layer's space to mask
7437 gfx::Matrix maskTransform
=
7438 Matrix::Scaling(surfaceSize
.width
/ boundingRect
.Width(),
7439 surfaceSize
.height
/ boundingRect
.Height());
7440 if (surfaceSize
.IsEmpty()) {
7441 // Return early if we know that the size of this mask surface is empty.
7445 gfx::Point p
= boundingRect
.TopLeft();
7446 maskTransform
.PreTranslate(-p
.x
, -p
.y
);
7447 // imageTransform is only used when the clip is painted to the mask
7448 gfx::Matrix imageTransform
= maskTransform
;
7449 imageTransform
.PreScale(mParameters
.mXScale
, mParameters
.mYScale
);
7451 UniquePtr
<MaskLayerImageCache::MaskLayerImageKey
> newKey(
7452 new MaskLayerImageCache::MaskLayerImageKey());
7454 // copy and transform the rounded rects
7455 for (uint32_t i
= 0; i
< newData
.mRoundedClipRects
.Length(); ++i
) {
7456 newKey
->mRoundedClipRects
.AppendElement(
7457 MaskLayerImageCache::PixelRoundedRect(newData
.mRoundedClipRects
[i
],
7458 mContainerFrame
->PresContext()));
7459 newKey
->mRoundedClipRects
[i
].ScaleAndTranslate(imageTransform
);
7461 newKey
->mKnowsCompositor
= mManager
->AsKnowsCompositor();
7463 const MaskLayerImageCache::MaskLayerImageKey
* lookupKey
= newKey
.get();
7465 // check to see if we can reuse a mask image
7466 RefPtr
<ImageContainer
> container
=
7467 GetMaskLayerImageCache()->FindImageFor(&lookupKey
);
7470 IntSize
surfaceSizeInt(NSToIntCeil(surfaceSize
.width
),
7471 NSToIntCeil(surfaceSize
.height
));
7472 // no existing mask image, so build a new one
7473 MaskImageData
imageData(surfaceSizeInt
, mManager
);
7474 RefPtr
<DrawTarget
> dt
= imageData
.CreateDrawTarget();
7476 // fail if we can't get the right surface
7477 if (!dt
|| !dt
->IsValid()) {
7478 NS_WARNING("Could not create DrawTarget for mask layer.");
7482 RefPtr
<gfxContext
> context
= gfxContext::CreateOrNull(dt
);
7483 MOZ_ASSERT(context
); // already checked the draw target above
7484 context
->Multiply(ThebesMatrix(imageTransform
));
7486 // paint the clipping rects with alpha to create the mask
7487 aClip
.FillIntersectionOfRoundedRectClips(
7488 context
, DeviceColor::MaskOpaqueWhite(), newData
.mAppUnitsPerDevPixel
);
7490 // build the image and container
7491 MOZ_ASSERT(aLayer
->Manager() == mManager
);
7492 container
= imageData
.CreateImageAndImageContainer();
7493 NS_ASSERTION(container
, "Could not create image container for mask layer.");
7499 GetMaskLayerImageCache()->PutImage(newKey
.release(), container
);
7502 maskLayer
->SetContainer(container
);
7504 maskTransform
.Invert();
7505 Matrix4x4 matrix
= Matrix4x4::From2D(maskTransform
);
7506 matrix
.PreTranslate(mParameters
.mOffset
.x
, mParameters
.mOffset
.y
, 0);
7507 maskLayer
->SetBaseTransform(matrix
);
7509 // save the details of the clip in user data
7510 *userData
= std::move(newData
);
7511 userData
->mImageKey
.Reset(lookupKey
);
7513 return maskLayer
.forget();
7516 } // namespace mozilla