Bug 1687263: part 4) Defer and in some cases avoid removing spellchecking-ranges...
[gecko.git] / layout / painting / FrameLayerBuilder.cpp
blobc07c49717b68386f45bf62bd8b8e92902efd5eaa
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"
9 #include <algorithm>
10 #include <deque>
11 #include <functional>
12 #include <utility>
14 #include "ActiveLayerTracker.h"
15 #include "BasicLayers.h"
16 #include "ImageContainer.h"
17 #include "ImageLayers.h"
18 #include "LayerTreeInvalidation.h"
19 #include "LayerUserData.h"
20 #include "Layers.h"
21 #include "MaskLayerImageCache.h"
22 #include "MatrixStack.h"
23 #include "TransformClipNode.h"
24 #include "UnitTransforms.h"
25 #include "Units.h"
26 #include "gfx2DGlue.h"
27 #include "gfxContext.h"
28 #include "gfxEnv.h"
29 #include "gfxUtils.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
73 // non-memmovable
74 MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(mozilla::PaintedLayerData);
76 namespace mozilla {
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;
92 /**
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;
175 nsRect mContentRect;
176 LayerState mLayerState;
177 DisplayItemEntryType mType;
179 bool mReused;
180 bool mMerged;
181 bool mHasOpacity;
182 bool mHasPaintRect;
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)) {
238 return false;
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) \
246 std::conditional< \
247 markerType == MarkerType::StartMarker, \
248 std::integral_constant<DisplayItemEntryType, start_marker>, \
249 std::integral_constant<DisplayItemEntryType, end_marker>>::type::value;
251 switch (type) {
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.
258 return false;
261 marker = GET_MARKER(DisplayItemEntryType::PushOpacity,
262 DisplayItemEntryType::PopOpacity);
263 break;
264 case DisplayItemType::TYPE_TRANSFORM:
265 marker = GET_MARKER(DisplayItemEntryType::PushTransform,
266 DisplayItemEntryType::PopTransform);
267 break;
268 default:
269 MOZ_ASSERT_UNREACHABLE("Invalid display item type!");
270 break;
273 aMarkers.emplace_back(aItem, marker);
274 return true;
277 DisplayItemData::DisplayItemData(LayerManagerData* aParent, uint32_t aKey,
278 Layer* aLayer, nsIFrame* aFrame)
280 : mRefCnt(0),
281 mParent(aParent),
282 mLayer(aLayer),
283 mDisplayItemKey(aKey),
284 mItem(nullptr),
285 mUsed(true),
286 mIsInvalid(false),
287 mReusedItem(false) {
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);
297 if (aFrame) {
298 AddFrame(aFrame);
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,
312 this);
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();
322 if (!array) {
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();
336 if (array) {
337 array->RemoveElement(this);
341 void DisplayItemData::EndUpdate() {
342 MOZ_RELEASE_ASSERT(mLayer);
343 mIsInvalid = false;
344 mUsed = false;
345 mReusedItem = false;
346 mOldTransform = nullptr;
349 void DisplayItemData::EndUpdate(UniquePtr<nsDisplayItemGeometry>&& aGeometry) {
350 MOZ_RELEASE_ASSERT(mLayer);
351 MOZ_ASSERT(mItem);
352 MOZ_ASSERT(mGeometry || aGeometry);
354 if (aGeometry) {
355 mGeometry = std::move(aGeometry);
357 mClip = mItem->GetClip();
358 mChangedFrameInvalidations.SetEmpty();
360 EndUpdate();
363 void DisplayItemData::BeginUpdate(Layer* aLayer, LayerState aState,
364 bool aFirstUpdate,
365 nsPaintedDisplayItem* aItem /* = nullptr */) {
366 bool isReused = false;
367 bool isMerged = false;
369 if (aItem) {
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,
381 bool aIsMerged) {
382 MOZ_RELEASE_ASSERT(mLayer);
383 MOZ_RELEASE_ASSERT(aLayer);
384 mLayer = aLayer;
385 mOptLayer = nullptr;
386 mInactiveManager = nullptr;
387 mLayerState = aState;
388 mUsed = true;
390 mItem = aItem;
391 mReusedItem = aIsReused;
393 if (!aItem) {
394 return;
397 if (!aIsMerged && mFrameList.Length() == 1) {
398 MOZ_ASSERT(mFrameList[0] == aItem->Frame());
399 return;
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());
411 if (aIsMerged) {
412 MOZ_ASSERT(aItem->AsDisplayWrapList());
414 for (nsIFrame* frame : aItem->AsDisplayWrapList()->GetMergedFrames()) {
415 if (!copy.RemoveElement(frame)) {
416 AddFrame(frame);
417 mChangedFrameInvalidations.Or(mChangedFrameInvalidations,
418 frame->InkOverflowRect());
423 for (nsIFrame* frame : copy) {
424 RemoveFrame(frame);
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) {
436 continue;
439 SmallPointerArray<DisplayItemData>* array = frame->DisplayItemData();
440 if (array) {
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.
460 return;
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) {
469 return;
472 // This is a remote browser that is going away, notify it that it is now
473 // hidden
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) {
486 return;
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);
504 return aData;
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 {
517 public:
518 explicit LayerManagerData(LayerManager* aManager)
519 : mLayerManager(aManager),
520 #ifdef DEBUG_DISPLAY_ITEM_DATA
521 mParent(nullptr),
522 #endif
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;
534 prefix += aPrefix;
535 prefix += " ";
537 const char* layerState;
538 switch (data->mLayerState) {
539 case LayerState::LAYER_NONE:
540 layerState = "LAYER_NONE";
541 break;
542 case LayerState::LAYER_INACTIVE:
543 layerState = "LAYER_INACTIVE";
544 break;
545 case LayerState::LAYER_ACTIVE:
546 layerState = "LAYER_ACTIVE";
547 break;
548 case LayerState::LAYER_ACTIVE_FORCE:
549 layerState = "LAYER_ACTIVE_FORCE";
550 break;
551 case LayerState::LAYER_ACTIVE_EMPTY:
552 layerState = "LAYER_ACTIVE_EMPTY";
553 break;
554 case LayerState::LAYER_SVG_EFFECTS:
555 layerState = "LAYER_SVG_EFFECTS";
556 break;
558 uint32_t mask = (1 << TYPE_BITS) - 1;
560 nsAutoCString str;
561 str += prefix;
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());
576 str += "\n";
578 printf_stderr("%s", str.get());
580 if (data->mInactiveManager) {
581 prefix += " ";
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());
589 #endif
592 * Tracks which frames have layers associated with them.
594 LayerManager* mLayerManager;
595 #ifdef DEBUG_DISPLAY_ITEM_DATA
596 LayerManagerData* mParent;
597 #endif
598 std::vector<RefPtr<DisplayItemData>> mDisplayItems;
599 bool mInvalidateAllLayers;
602 /* static */
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());
615 if (userDataTable) {
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 {
633 public:
634 PaintedLayerData()
635 : mAnimatedGeometryRoot(nullptr),
636 mASR(nullptr),
637 mClipChain(nullptr),
638 mReferenceFrame(nullptr),
639 mLayer(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),
648 mImage(nullptr),
649 mItemClip(nullptr),
650 mNewChildLayersIndex(-1)
651 #ifdef DEBUG
653 mTransformLevel(0)
654 #endif
658 PaintedLayerData(PaintedLayerData&& aRhs) = default;
660 ~PaintedLayerData() { MOZ_ASSERT(mTransformLevel == 0); }
662 #ifdef MOZ_DUMP_PAINTING
664 * Keep track of important decisions for debugging.
666 nsCString mLog;
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__); \
673 #else
674 # define FLB_LOG_PAINTED_LAYER_DECISION(...)
675 #endif
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.
766 nsRegion mHitRegion;
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
830 * region.
832 nscolor mSolidColor;
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
884 * be no part at all.
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;
903 #ifdef DEBUG
905 * Tracks the level of transform to ensure balanced PUSH/POP markers.
907 int mTransformLevel;
908 #endif
911 struct NewLayerEntry {
912 NewLayerEntry()
913 : mAnimatedGeometryRoot(nullptr),
914 mASR(nullptr),
915 mClipChain(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
931 // on the layer.
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
954 // transform.
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
967 * tree.
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
970 * can move anywhere.
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
975 * look correct.
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 {
983 public:
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
1053 * our clip.
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); }
1067 protected:
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
1078 * one.
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;
1117 bool mHasClip;
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
1139 * in layer pixels.
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
1143 * finished.
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
1149 * the tree.
1151 class PaintedLayerDataTree {
1152 public:
1153 PaintedLayerDataTree(ContainerState& aContainerState,
1154 nscolor& aBackgroundColor)
1155 : mContainerState(aContainerState),
1156 mContainerUniformBackgroundColor(aBackgroundColor),
1157 mForInactiveLayer(false) {}
1159 ~PaintedLayerDataTree() {
1160 MOZ_ASSERT(!mRoot);
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.
1194 void Finish();
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;
1229 protected:
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
1268 * geometry root.
1270 nsTHashMap<nsPtrHashKey<AnimatedGeometryRoot>, PaintedLayerDataNode*> mNodes;
1272 bool mForInactiveLayer;
1276 * This is a helper object used to build up the layer children for
1277 * a ContainerLayer.
1279 class ContainerState {
1280 public:
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),
1291 mManager(aManager),
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();
1315 MOZ_ASSERT(
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
1321 // will work.
1322 mSnappingEnabled = aManager->IsSnappingEffectiveTransforms() &&
1323 !mParameters.AllowResidualTranslation();
1324 CollectOldLayers();
1328 * This is the method that actually walks a display list and builds
1329 * the child layers.
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()) {
1353 return nsIntRect();
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
1421 * NS_RGBA(0,0,0,0).
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);
1439 protected:
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
1572 * PaintedLayer.
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
1604 * be created.
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;
1648 #ifdef DEBUG
1649 nsRect mAccumulatedChildBounds;
1650 #endif
1651 ContainerLayerParameters mParameters;
1653 * The region of PaintedLayers that should be invalidated every time
1654 * we recycle one.
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,
1661 * or no 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;
1687 Layer* mLayer;
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
1694 // thing repeatly.
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 {
1713 public:
1714 FLBDisplayListIterator(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
1715 ContainerState* aState)
1716 : FlattenedDisplayListIterator(aBuilder, aList, false), mState(aState) {
1717 MOZ_ASSERT(mState);
1718 ResolveFlattening();
1721 DisplayItemEntry GetNextEntry() {
1722 if (!mMarkers.empty()) {
1723 DisplayItemEntry entry = mMarkers.front();
1724 mMarkers.pop_front();
1725 return entry;
1728 return DisplayItemEntry{GetNextItem(), DisplayItemEntryType::Item};
1731 bool HasNext() const override {
1732 return FlattenedDisplayListIterator::HasNext() || !mMarkers.empty();
1735 private:
1736 bool ShouldFlattenNextItem() override {
1737 if (!FlattenedDisplayListIterator::ShouldFlattenNextItem()) {
1738 return false;
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)) {
1751 return true;
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.
1758 return true;
1761 if (mState->IsInInactiveLayer() || !ItemWantsInactiveLayer(next)) {
1762 // Do not flatten nested inactive display items, or display items that
1763 // want an active layer.
1764 return false;
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));
1771 return true;
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 {
1798 public:
1799 PaintedDisplayItemLayerUserData()
1800 : mForcedBackgroundColor(NS_RGBA(0, 0, 0, 0)),
1801 mXScale(1.f),
1802 mYScale(1.f),
1803 mAppUnitsPerDevPixel(0),
1804 mTranslation(0, 0),
1805 mAnimatedGeometryRootPosition(0, 0),
1806 mLastItemCount(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
1867 // previous paint.
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;
1889 protected:
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 {
1923 MaskLayerUserData()
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
1957 // transform.
1958 nsIntPoint mOffset;
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)) {
1982 return false;
1985 // Make sure we draw the same portion of the mask onto mask layer.
1986 if (mMaskLayerOffset != aOther.mMaskLayerOffset) {
1987 return false;
1990 return mMaskStyle == aOther.mMaskStyle;
1993 private:
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 {
2006 public:
2007 MaskImageData(const gfx::IntSize& aSize, LayerManager* aLayerManager)
2008 : mTextureClientLocked(false),
2009 mSize(aSize),
2010 mLayerManager(aLayerManager) {
2011 MOZ_ASSERT(!mSize.IsEmpty());
2012 MOZ_ASSERT(mLayerManager);
2015 ~MaskImageData() {
2016 if (mTextureClientLocked) {
2017 MOZ_ASSERT(mTextureClient);
2018 // Clear DrawTarget before Unlock.
2019 mDrawTarget = nullptr;
2020 mTextureClient->Unlock();
2024 gfx::DrawTarget* CreateDrawTarget() {
2025 if (mDrawTarget) {
2026 return mDrawTarget;
2029 if (mLayerManager->GetBackendType() == LayersBackend::LAYERS_BASIC) {
2030 mDrawTarget = mLayerManager->CreateOptimalMaskDrawTarget(mSize);
2031 return mDrawTarget;
2034 MOZ_ASSERT(mLayerManager->GetBackendType() ==
2035 LayersBackend::LAYERS_CLIENT ||
2036 mLayerManager->GetBackendType() == LayersBackend::LAYERS_WR);
2038 KnowsCompositor* knowsCompositor = mLayerManager->AsKnowsCompositor();
2039 if (!knowsCompositor) {
2040 return nullptr;
2042 mTextureClient = TextureClient::CreateForDrawing(
2043 knowsCompositor, SurfaceFormat::A8, mSize, BackendSelector::Content,
2044 TextureFlags::DISALLOW_BIGIMAGE,
2045 TextureAllocationFlags::ALLOC_CLEAR_BUFFER);
2046 if (!mTextureClient) {
2047 return nullptr;
2050 mTextureClientLocked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
2051 if (!mTextureClientLocked) {
2052 return nullptr;
2055 mDrawTarget = mTextureClient->BorrowDrawTarget();
2056 return mDrawTarget;
2059 already_AddRefed<ImageContainer> CreateImageAndImageContainer() {
2060 RefPtr<ImageContainer> container = LayerManager::CreateImageContainer();
2061 RefPtr<Image> image = CreateImage();
2063 if (!image) {
2064 return nullptr;
2066 container->SetCurrentImageInTransaction(image);
2068 return container.forget();
2071 private:
2072 already_AddRefed<Image> CreateImage() {
2073 if (mLayerManager->GetBackendType() == LayersBackend::LAYERS_BASIC &&
2074 mDrawTarget) {
2075 RefPtr<SourceSurface> surface = mDrawTarget->Snapshot();
2076 RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(mSize, surface);
2077 // Disallow BIGIMAGE (splitting into multiple textures) for mask
2078 // layer images
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();
2091 return nullptr;
2094 bool mTextureClientLocked;
2095 gfx::IntSize mSize;
2096 LayerManager* mLayerManager;
2097 RefPtr<gfx::DrawTarget> mDrawTarget;
2098 RefPtr<TextureClient> mTextureClient;
2101 /* static */
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;
2115 mRootPresContext =
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));
2128 aContext->Paint();
2131 DisplayItemData* FrameLayerBuilder::GetDisplayItemData(nsIFrame* aFrame,
2132 uint32_t aKey) {
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) {
2137 return data;
2141 return nullptr;
2144 #ifdef MOZ_DUMP_PAINTING
2145 static nsACString& AppendToString(nsACString& s, const nsIntRect& r,
2146 const char* pfx = "", const char* sfx = "") {
2147 s += pfx;
2148 s += nsPrintfCString("(x=%d, y=%d, w=%d, h=%d)", r.x, r.y, r.width, r.height);
2149 return s += sfx;
2152 static nsACString& AppendToString(nsACString& s, const nsIntRegion& r,
2153 const char* pfx = "", const char* sfx = "") {
2154 s += pfx;
2156 s += "< ";
2157 for (auto iter = r.RectIter(); !iter.Done(); iter.Next()) {
2158 AppendToString(s, iter.Get()) += "; ";
2160 s += ">";
2162 return s += sfx;
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()) {
2183 nsAutoCString str;
2184 AppendToString(str, rgn);
2185 printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
2187 #endif
2190 static PaintedDisplayItemLayerUserData* GetPaintedDisplayItemLayerUserData(
2191 Layer* aLayer) {
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);
2224 if (aTransform) {
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.
2250 /* static */
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;
2262 if (array) {
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);
2275 rootData->Dump();
2277 #endif
2279 for (DisplayItemData* data : arrayCopy) {
2280 PaintedLayer* t = data->mLayer ? data->mLayer->AsPaintedLayer() : nullptr;
2281 if (t) {
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);
2292 pixelRgn =
2293 pixelRgn.ScaleRoundOut(paintedData->mXScale, paintedData->mYScale);
2295 pixelRgn.MoveBy(-GetTranslationForPaintedLayer(t));
2297 paintedData->mRegionToInvalidate.Or(paintedData->mRegionToInvalidate,
2298 pixelRgn);
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
2316 // hidden
2317 frameLoader->GetRemoteBrowser()->UpdateEffects(
2318 mozilla::dom::EffectsInfo::FullyHidden());
2322 arrayCopy.Clear();
2323 delete array;
2324 sDestroyedFrame = nullptr;
2327 void FrameLayerBuilder::DidBeginRetainedLayerTransaction(
2328 LayerManager* aManager) {
2329 mRetainingManager = aManager;
2330 LayerManagerData* data = static_cast<LayerManagerData*>(
2331 aManager->GetUserData(&gLayerManagerUserData));
2332 if (data) {
2333 mInvalidateAllLayers = data->mInvalidateAllLayers;
2334 } else {
2335 data = new LayerManagerData(aManager);
2336 aManager->SetUserData(&gLayerManagerUserData, data);
2340 void FrameLayerBuilder::DidEndTransaction() {
2341 GetMaskLayerImageCache()->Sweep();
2344 void FrameLayerBuilder::WillEndTransaction() {
2345 if (!mRetainingManager) {
2346 return;
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();
2358 if (!did->mUsed) {
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()) {
2364 printf_stderr(
2365 "Invalidating unused display item (%i) belonging to "
2366 "frame %p from layer %p\n",
2367 did->mDisplayItemKey, did->mFrameList[0], t);
2369 #endif
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();
2382 } else {
2383 data->mDisplayItems.pop_back();
2384 break;
2387 // Don't increment iter because we still need to process the item which
2388 // was moved.
2390 } else {
2391 ComputeGeometryChangeForItem(did);
2392 iter++;
2396 data->mInvalidateAllLayers = false;
2399 /* static */
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) {
2406 return data;
2410 return nullptr;
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) {
2418 return true;
2422 if (RefPtr<WebRenderUserData> data =
2423 GetWebRenderUserData<WebRenderFallbackData>(aFrame,
2424 aDisplayItemKey)) {
2425 return true;
2428 return false;
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
2436 // anything.
2437 if (!mRetainingManager || mInvalidateAllLayers) {
2438 return nullptr;
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));
2452 return data;
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);
2462 if (oldData) {
2463 if (aOldGeometry) {
2464 *aOldGeometry = oldData->mGeometry.get();
2466 if (aOldClip) {
2467 *aOldClip = &oldData->mClip;
2469 return oldData->mLayer;
2472 return nullptr;
2475 /* static */
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()) {
2480 return data;
2484 return nullptr;
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;
2499 if (layer) {
2500 ResetLayerStateForRecycling(layer);
2501 layer->ClearExtraDumpInfo();
2502 } else {
2503 // Create a new layer
2504 layer = mManager->CreateColorLayer();
2505 if (!layer) {
2506 return nullptr;
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;
2522 if (layer) {
2523 ResetLayerStateForRecycling(layer);
2524 layer->ClearExtraDumpInfo();
2525 } else {
2526 // Create a new layer
2527 layer = mManager->CreateImageLayer();
2528 if (!layer) {
2529 return nullptr;
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
2551 } else {
2552 // Create a new layer
2553 result = mManager->CreateImageLayer();
2554 if (!result) {
2555 return nullptr;
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) {
2576 if (residual > 0 &&
2577 fabs(residual - 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) {
2578 // Round up instead
2579 return int32_t(ceil(aValue));
2581 } else if (aOldResidual > 0) {
2582 if (residual < 0 &&
2583 fabs(residual + 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) {
2584 // Round down instead
2585 return int32_t(floor(aValue));
2588 return v;
2591 static void ResetScrollPositionForLayerPixelAlignment(
2592 AnimatedGeometryRoot* aAnimatedGeometryRoot) {
2593 nsIScrollableFrame* sf =
2594 nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot);
2595 if (sf) {
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);
2607 #endif
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
2624 // root chain.
2625 for (AnimatedGeometryRoot* agr = aAnimatedGeometryRoot;
2626 agr && agr != mContainerAnimatedGeometryRoot; agr = agr->mParentAGR) {
2627 nsIFrame* fParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(*agr);
2628 if (!fParent) {
2629 break;
2631 nsIScrollableFrame* scrollable = do_QueryFrame(fParent);
2632 if (scrollable) {
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()) {
2644 return nullptr;
2647 if (!mPaintedLayersAvailableForRecycling.EnsureRemoved(
2648 oldLayer->AsPaintedLayer())) {
2649 // Not found.
2650 return nullptr;
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))) {
2659 return nullptr;
2662 bool didResetScrollPositionForLayerPixelAlignment = false;
2663 PaintedDisplayItemLayerUserData* data =
2664 RecyclePaintedLayer(layer, aAnimatedGeometryRoot,
2665 didResetScrollPositionForLayerPixelAlignment);
2666 PreparePaintedLayerForUse(layer, data, aAnimatedGeometryRoot, aReferenceFrame,
2667 aTopLeft,
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);
2687 if (!layer) {
2688 return nullptr;
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);
2734 #endif
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",
2743 aLayer);
2745 #endif
2746 aLayer->InvalidateRegion(data->mRegionToInvalidate);
2747 #ifdef MOZ_DUMP_PAINTING
2748 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2749 nsAutoCString str;
2750 AppendToString(str, data->mRegionToInvalidate);
2751 printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
2753 #endif
2754 data->mRegionToInvalidate.SetEmpty();
2756 return data;
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.
2773 nsPoint offset =
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;
2807 return;
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,
2819 "subpixel offset");
2820 } else if (didResetScrollPositionForLayerPixelAlignment) {
2821 aData->mAnimatedGeometryRootPosition = animatedGeometryRootTopLeft;
2823 #else
2824 Unused << didResetScrollPositionForLayerPixelAlignment;
2825 #endif
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
2836 // APDs.
2837 if (aItem->GetType() == DisplayItemType::TYPE_ZOOM) {
2838 return static_cast<nsDisplayZoom*>(aItem)->GetParentAppUnitsPerDevPixel();
2840 return aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
2842 #endif
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();
2856 Matrix transform2D;
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);
2869 } else {
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,
2874 outerRect.height);
2875 transform.Invert();
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);
2891 Rect layerVisible =
2892 transform.ProjectRectBounds(outerVisible, layerContentsVisible);
2894 layerVisible.RoundOut();
2896 IntRect intRect;
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.
2934 continue;
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.
2940 continue;
2943 nsDisplayItem* item = assignedItem.mItem;
2944 bool snap;
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);
2952 } else {
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)) {
2961 continue;
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) {
2974 return *color;
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
2993 // hopeless
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
2999 continue;
3002 bool intersectsLayer = true;
3003 nsIntRect rect = aTargetVisibleRegion.GetBounds();
3004 nscolor color = mTree.ContState().FindOpaqueBackgroundColorInLayer(
3005 candidate, rect, &intersectsLayer);
3006 if (!intersectsLayer) {
3007 continue;
3009 return color;
3011 if (mAllDrawingAboveBackground ||
3012 !mVisibleAboveBackgroundRegion.Intersect(aTargetVisibleRegion)
3013 .IsEmpty()) {
3014 // Some non-PaintedLayer content is between this node's background and
3015 // target.
3016 return NS_RGBA(0, 0, 0, 0);
3018 return FindOpaqueBackgroundColorInParentNode();
3021 nscolor PaintedLayerDataNode::FindOpaqueBackgroundColorCoveringEverything()
3022 const {
3023 if (!mPaintedLayerDataStack.IsEmpty() || mAllDrawingAboveBackground ||
3024 !mVisibleAboveBackgroundRegion.IsEmpty()) {
3025 return NS_RGBA(0, 0, 0, 0);
3027 return FindOpaqueBackgroundColorInParentNode();
3030 nscolor PaintedLayerDataNode::FindOpaqueBackgroundColorInParentNode() const {
3031 if (mParent) {
3032 if (mHasClip) {
3033 // Check whether our parent node has uniform content behind our whole
3034 // clip.
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();
3046 // We are the root.
3047 return mTree.UniformBackgroundColor();
3050 bool PaintedLayerData::CanOptimizeToImageLayer(nsDisplayListBuilder* aBuilder) {
3051 if (!mImage) {
3052 return false;
3055 return mImage->CanOptimizeToImageLayer(mLayer->Manager(), aBuilder);
3058 already_AddRefed<ImageContainer> PaintedLayerData::GetContainerForImageLayer(
3059 nsDisplayListBuilder* aBuilder) {
3060 if (!mImage) {
3061 return nullptr;
3064 return mImage->GetContainer(mLayer->Manager(), aBuilder);
3067 PaintedLayerDataNode::PaintedLayerDataNode(
3068 PaintedLayerDataTree& aTree, PaintedLayerDataNode* aParent,
3069 AnimatedGeometryRoot* aAnimatedGeometryRoot)
3070 : mTree(aTree),
3071 mParent(aParent),
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)) {
3103 break;
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))) {
3117 break;
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
3121 // now.
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)) {
3127 break;
3130 if (StaticPrefs::layout_smaller_painted_layers()) {
3131 lowestUsableLayer = nullptr;
3134 if (lowestUsableLayer) {
3135 return lowestUsableLayer;
3138 PaintedLayerData* data = mPaintedLayerDataStack.AppendElement();
3139 aNewPaintedLayerCallback(data);
3141 return 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);
3158 mChildren.Clear();
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) {
3168 if (mHasClip) {
3169 mParent->AddToVisibleAboveRegion(mClipRect);
3170 } else {
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;
3194 index--) {
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() {
3214 if (mRoot) {
3215 mRoot->Finish(false);
3217 MOZ_ASSERT(mNodes.Count() == 0);
3218 mRoot.reset();
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) {
3231 node = mRoot.ptr();
3232 } else {
3233 FinishPotentiallyIntersectingNodes(aAnimatedGeometryRoot, aRect);
3234 node = EnsureNodeFor(aAnimatedGeometryRoot);
3236 if (aRect) {
3237 if (aOutUniformBackgroundColor) {
3238 *aOutUniformBackgroundColor = node->FindOpaqueBackgroundColor(*aRect);
3240 node->AddToVisibleAboveRegion(*aRect);
3241 } else {
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) {
3259 node = mRoot.ptr();
3260 } else {
3261 FinishPotentiallyIntersectingNodes(aAnimatedGeometryRoot, bounds);
3262 node = EnsureNodeFor(aAnimatedGeometryRoot);
3265 PaintedLayerData* data =
3266 node->FindPaintedLayerFor(aVisibleRect, aBackfaceHidden, aASR, aClipChain,
3267 aNewPaintedLayerCallback);
3268 return data;
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.
3279 MOZ_ASSERT(!mRoot);
3280 return;
3283 if (ancestorNode->GetAnimatedGeometryRoot() == aAnimatedGeometryRoot) {
3284 // aAnimatedGeometryRoot already has a node in the tree.
3285 // This is the common case.
3286 MOZ_ASSERT(!ancestorThatIsChildOfCommonAncestor);
3287 if (aRect) {
3288 ancestorNode->FinishChildrenIntersecting(*aRect);
3289 } else {
3290 ancestorNode->FinishAllChildren();
3292 return;
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.
3310 nsIntRect clip;
3311 if (IsClippedWithRespectToParentAnimatedGeometryRoot(
3312 ancestorThatIsChildOfCommonAncestor, &clip)) {
3313 ancestorNode->FinishChildrenIntersecting(clip);
3314 } else {
3315 ancestorNode->FinishAllChildren();
3319 PaintedLayerDataNode* PaintedLayerDataTree::EnsureNodeFor(
3320 AnimatedGeometryRoot* aAnimatedGeometryRoot) {
3321 MOZ_ASSERT(aAnimatedGeometryRoot);
3322 PaintedLayerDataNode* node = mNodes.Get(aAnimatedGeometryRoot);
3323 if (node) {
3324 return node;
3327 AnimatedGeometryRoot* parentAnimatedGeometryRoot =
3328 aAnimatedGeometryRoot->mParentAGR;
3329 if (!parentAnimatedGeometryRoot) {
3330 MOZ_ASSERT(!mRoot);
3331 MOZ_ASSERT(*aAnimatedGeometryRoot == Builder()->RootReferenceFrame());
3332 mRoot.emplace(*this, nullptr, aAnimatedGeometryRoot);
3333 node = mRoot.ptr();
3334 } else {
3335 PaintedLayerDataNode* parentNode =
3336 EnsureNodeFor(parentAnimatedGeometryRoot);
3337 MOZ_ASSERT(parentNode);
3338 node = parentNode->AddChildNodeFor(aAnimatedGeometryRoot);
3340 MOZ_ASSERT(node);
3341 mNodes.InsertOrUpdate(aAnimatedGeometryRoot, node);
3342 return node;
3345 bool PaintedLayerDataTree::IsClippedWithRespectToParentAnimatedGeometryRoot(
3346 AnimatedGeometryRoot* aAnimatedGeometryRoot, nsIntRect* aOutClip) {
3347 if (mForInactiveLayer) {
3348 return false;
3350 nsIScrollableFrame* scrollableFrame =
3351 nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot);
3352 if (!scrollableFrame) {
3353 return false;
3355 nsIFrame* scrollFrame = do_QueryFrame(scrollableFrame);
3356 nsRect scrollPort = scrollableFrame->GetScrollPortRect() +
3357 Builder()->ToReferenceFrame(scrollFrame);
3358 *aOutClip = mContainerState.ScaleToNearestPixels(scrollPort);
3359 return true;
3362 PaintedLayerDataNode*
3363 PaintedLayerDataTree::FindNodeForAncestorAnimatedGeometryRoot(
3364 AnimatedGeometryRoot* aAnimatedGeometryRoot,
3365 AnimatedGeometryRoot** aOutAncestorChild) {
3366 if (!aAnimatedGeometryRoot) {
3367 return nullptr;
3369 PaintedLayerDataNode* node = mNodes.Get(aAnimatedGeometryRoot);
3370 if (node) {
3371 return node;
3373 *aOutAncestorChild = aAnimatedGeometryRoot;
3374 return FindNodeForAncestorAnimatedGeometryRoot(
3375 aAnimatedGeometryRoot->mParentAGR, aOutAncestorChild);
3378 static bool CanOptimizeAwayPaintedLayer(PaintedLayerData* aData,
3379 FrameLayerBuilder* aLayerBuilder) {
3380 if (!aLayerBuilder->IsBuildingRetainedLayers()) {
3381 return false;
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()) {
3388 return true;
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();
3398 #ifdef DEBUG
3399 static int32_t FindIndexOfLayerIn(nsTArray<NewLayerEntry>& aArray,
3400 Layer* aLayer) {
3401 for (uint32_t i = 0; i < aArray.Length(); ++i) {
3402 if (aArray[i].mLayer == aLayer) {
3403 return i;
3406 return -1;
3408 #endif
3410 already_AddRefed<Layer> ContainerState::PrepareImageLayer(
3411 PaintedLayerData* aData) {
3412 RefPtr<ImageContainer> imageContainer =
3413 aData->GetContainerForImageLayer(mBuilder);
3414 if (!imageContainer) {
3415 return nullptr;
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));
3428 } else {
3429 imageLayer->SetClipRect(Nothing());
3432 FLB_LOG_PAINTED_LAYER_DECISION(aData, " Selected image layer=%p\n",
3433 imageLayer.get());
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));
3443 // Copy transform
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",
3454 colorLayer.get());
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);
3463 } else {
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)) {
3503 NS_ASSERTION(
3504 !(data->mIsSolidColorInVisibleRegion && canOptimizeToImageLayer),
3505 "Can't be a solid color as well as an image!");
3507 layer = canOptimizeToImageLayer ? PrepareImageLayer(data)
3508 : PrepareColorLayer(data);
3510 if (layer) {
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());
3535 if (!layer) {
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",
3540 layer.get());
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.
3549 continue;
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;
3563 } else {
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));
3573 } else {
3574 layer->AddExtraDumpInfo(nsCString(data->mLog));
3577 #endif
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);
3589 if (!isOpaque) {
3590 backgroundColor = aFindOpaqueBackgroundColor();
3591 if (NS_GET_A(backgroundColor) == 255) {
3592 isOpaque = true;
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()) {
3602 printf_stderr(
3603 "Forced background color has changed from #%08X to #%08X "
3604 "on layer %p\n",
3605 userData->mForcedBackgroundColor, backgroundColor, data->mLayer);
3606 nsAutoCString str;
3607 AppendToString(str, data->mLayer->GetValidRegion());
3608 printf_stderr("Invalidating layer %p: %s\n", data->mLayer, str.get());
3610 #endif
3611 data->mLayer->InvalidateWholeLayer();
3613 userData->mForcedBackgroundColor = backgroundColor;
3614 } else {
3615 // mask layer for image and color layers
3616 SetupMaskLayer(layer, *data->mItemClip);
3619 uint32_t flags = 0;
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;
3623 if (hidpi) {
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,
3682 inactiveLayerClip);
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,
3694 inactiveLayerClip);
3695 nsLayoutUtils::TransformToAncestorAndCombineRegions(
3696 data->mHorizontalPanRegion, mContainerReferenceFrame,
3697 containingPaintedLayerData->mReferenceFrame,
3698 &containingPaintedLayerData->mHorizontalPanRegion,
3699 &containingPaintedLayerData->mDispatchToContentHitRegion, &matrixCache,
3700 inactiveLayerClip);
3701 nsLayoutUtils::TransformToAncestorAndCombineRegions(
3702 data->mVerticalPanRegion, mContainerReferenceFrame,
3703 containingPaintedLayerData->mReferenceFrame,
3704 &containingPaintedLayerData->mVerticalPanRegion,
3705 &containingPaintedLayerData->mDispatchToContentHitRegion, &matrixCache,
3706 inactiveLayerClip);
3707 if (alreadyHadRegions) {
3708 containingPaintedLayerData->mDispatchToContentHitRegion.OrWith(
3709 containingPaintedLayerData->CombinedTouchActionRegion());
3711 containingPaintedLayerData->HitRegionsUpdated();
3712 } else {
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();
3723 mat.Invert();
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.
3740 return true;
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
3746 // the window.
3747 return false;
3749 return aBuilder->GetWindowOpaqueRegion().Contains(aComponentAlphaBounds);
3752 void PaintedLayerData::UpdateEffectStatus(DisplayItemEntryType aType,
3753 nsTArray<size_t>& aOpacityIndices) {
3754 switch (aType) {
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());
3759 break;
3760 case DisplayItemEntryType::PopOpacity:
3761 MOZ_ASSERT(!aOpacityIndices.IsEmpty());
3762 aOpacityIndices.RemoveLastElement();
3763 break;
3764 #ifdef DEBUG
3765 case DisplayItemEntryType::PopTransform:
3766 MOZ_ASSERT(mTransformLevel >= 0);
3767 mTransformLevel--;
3768 break;
3769 case DisplayItemEntryType::PushTransform:
3770 mTransformLevel++;
3771 break;
3772 #endif
3773 default:
3774 break;
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.
3786 return false;
3789 if (aTransform) {
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();
3802 if (aTransform) {
3803 buildingRect =
3804 aTransform->TransformRect(buildingRect, aState->mAppUnitsPerDevPixel);
3807 const nsRect tightBounds = componentAlphaBounds.Intersect(buildingRect);
3809 if (IsItemAreaInWindowOpaqueRegion(aState->mBuilder, aItem, tightBounds)) {
3810 mNeedComponentAlpha = true;
3811 } else {
3812 // There is no opaque background below the item, disable component alpha.
3813 aItem->DisableComponentAlpha();
3814 return false;
3818 return true;
3821 UniquePtr<InactiveLayerData> PaintedLayerData::CreateInactiveLayerData(
3822 ContainerState* aState, nsPaintedDisplayItem* aItem,
3823 DisplayItemData* aData) {
3824 RefPtr<BasicLayerManager> tempManager;
3825 if (aData) {
3826 tempManager = aData->InactiveManager();
3828 if (!tempManager) {
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,
3837 &aItem->GetClip());
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());
3847 return data;
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.
3864 return;
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;
3879 mItemClip = &aClip;
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);
3887 return;
3890 bool clipMatches =
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",
3901 item);
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();
3915 } else {
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.
3933 return;
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.
3946 return;
3949 nsIntRegion opaquePixels;
3951 // Active opacity means no opaque pixels.
3952 if (!hasOpacity) {
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");
3970 mImage = nullptr;
3973 bool isFirstVisibleItem = mVisibleRegion.IsEmpty();
3975 Maybe<nscolor> uniformColor;
3976 if (!hasOpacity) {
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).
3987 if (uniformColor) {
3988 bool snap;
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");
3996 if (uniformColor) {
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)) &&
4003 clipMatches) {
4004 // we can just blend the colors together
4005 mSolidColor = NS_ComposeColors(mSolidColor, *uniformColor);
4006 } else {
4007 FLB_LOG_PAINTED_LAYER_DECISION(
4008 this, " Layer not a solid color: Can't blend colors togethers\n");
4009 mIsSolidColorInVisibleRegion = false;
4011 } else {
4012 FLB_LOG_PAINTED_LAYER_DECISION(this,
4013 " Layer is not a solid color: Display "
4014 "item is not uniform over the visible "
4015 "bound\n");
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.
4030 nsIntRegion tmp;
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() {
4043 nsRegion result;
4044 result.Or(mHorizontalPanRegion, mVerticalPanRegion);
4045 result.OrWith(mNoActionRegion);
4046 return result;
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(
4058 this,
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);
4065 if (aTransform) {
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);
4072 return;
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;
4085 } else {
4086 frame->AddStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
4090 if (hasRoundedCorners || frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
4091 mMaybeHitRegion.OrWith(area);
4092 } else {
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);
4118 } else {
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
4133 // properly.
4134 if (touchFlags !=
4135 CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled) {
4136 if (!flags.contains(CompositorHitTestFlags::eTouchActionPanXDisabled)) {
4137 // pan-x is allowed
4138 mHorizontalPanRegion.OrWith(area);
4140 if (!flags.contains(CompositorHitTestFlags::eTouchActionPanYDisabled)) {
4141 // pan-y is allowed
4142 mVerticalPanRegion.OrWith(area);
4144 } else {
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;
4195 aData->mASR = aASR;
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());
4218 string.Append('-');
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");
4225 #endif
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);
4243 if (tempDT) {
4244 context = gfxContext::CreateOrNull(tempDT);
4245 if (!context) {
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);
4249 return;
4251 context->SetMatrix(
4252 Matrix::Translation(-itemVisibleRect.x, -itemVisibleRect.y));
4255 #endif
4256 basic->BeginTransaction();
4257 basic->SetTarget(context);
4259 if (aItem->GetType() == DisplayItemType::TYPE_MASK) {
4260 static_cast<nsDisplayMasksAndClipPaths*>(aItem)->PaintAsLayer(aBuilder,
4261 aCtx, basic);
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();
4270 } else {
4271 basic->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aBuilder);
4273 FrameLayerBuilder* builder = static_cast<FrameLayerBuilder*>(
4274 basic->GetUserData(&gLayerManagerLayerBuilder));
4275 if (builder) {
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();
4293 #endif
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) {
4331 bool snapOpaque;
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
4352 // not needed).
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()|.
4382 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)) {
4394 return asr;
4397 return nullptr;
4400 static CSSMaskLayerUserData* GetCSSMaskLayerUserData(Layer* aMaskLayer) {
4401 if (!aMaskLayer) {
4402 return nullptr;
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);
4423 bool snap;
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.
4430 Matrix4x4 matrix;
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,
4438 maskLayerOffset);
4439 nsRect dirtyRect;
4440 if (!aMaskItem->IsInvalid(dirtyRect) && *oldUserData == newUserData) {
4441 aLayer->SetMaskLayer(maskLayer);
4442 return;
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.
4451 return;
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.");
4458 return;
4461 RefPtr<gfxContext> maskCtx = gfxContext::CreateOrNull(dt);
4462 maskCtx->SetMatrix(Matrix::Translation(-itemRect.TopLeft()));
4463 maskCtx->Multiply(
4464 gfxMatrix::Scaling(mParameters.mXScale, mParameters.mYScale));
4466 bool isPaintFinished = aMaskItem->PaintMask(mBuilder, maskCtx);
4468 RefPtr<ImageContainer> imgContainer =
4469 imageData.CreateImageAndImageContainer();
4470 if (!imgContainer) {
4471 return;
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) {
4490 switch (aMarker) {
4491 case DisplayItemEntryType::PushTransform:
4492 case DisplayItemEntryType::PushOpacity:
4493 SelectLayerIfNeeded();
4494 break;
4495 case DisplayItemEntryType::PopTransform:
4496 case DisplayItemEntryType::PopOpacity:
4497 ClearLayerSelectionIfNeeded();
4498 break;
4499 default:
4500 break;
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>
4521 autoRecording;
4523 nsPoint topLeft(0, 0);
4525 int32_t maxLayers = StaticPrefs::layers_max_active();
4526 int layerCount = 0;
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
4537 // non-null.
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;
4558 MOZ_ASSERT(item);
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;
4592 } else {
4593 layerClipChain = itemClipChain;
4595 } else {
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,
4620 transformNode);
4621 continue;
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;
4630 } else {
4631 lastTopLeft = topLeft =
4632 (*itemAGR)->GetOffsetToCrossDoc(mContainerReferenceFrame);
4633 lastAnimatedGeometryRoot = itemAGR;
4636 const ActiveScrolledRoot* scrollMetadataASR =
4637 layerClipChain
4638 ? ActiveScrolledRoot::PickDescendant(itemASR, layerClipChain->mASR)
4639 : itemASR;
4641 const bool prerenderedTransform =
4642 itemType == DisplayItemType::TYPE_TRANSFORM &&
4643 static_cast<nsDisplayTransform*>(item)->MayBeAnimated(mBuilder);
4645 bool snap = false;
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);
4673 itemContent =
4674 transformNode->TransformRect(itemContent, mAppUnitsPerDevPixel);
4676 itemDrawRect = transformNode->TransformRect(itemDrawRect);
4679 #ifdef DEBUG
4680 nsRect bounds = itemContent;
4682 if (inEffect || isHitTestItem) {
4683 bounds.SetEmpty();
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);
4695 #endif
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))) {
4723 layerCount++;
4725 // Currently we do not support flattening effects within nested inactive
4726 // layer trees.
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!
4732 NS_ASSERTION(
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,
4790 uniformColorPtr);
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,
4804 uniformColorPtr);
4805 } else if (IsScrollThumbLayer(item) && mManager->IsWidgetLayerManager()) {
4806 // For scrollbar thumbs, the clip we care about is the clip added by the
4807 // slider frame.
4808 mPaintedLayerDataTree.AddingOwnLayer(itemAGR->mParentAGR, clipPtr,
4809 uniformColorPtr);
4810 } else if (prerenderedTransform && mManager->IsWidgetLayerManager()) {
4811 if (itemAGR->mParentAGR) {
4812 mPaintedLayerDataTree.AddingOwnLayer(itemAGR->mParentAGR, clipPtr,
4813 uniformColorPtr);
4814 } else {
4815 mPaintedLayerDataTree.AddingOwnLayer(itemAGR, nullptr,
4816 uniformColorPtr);
4818 } else {
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,
4828 uniformColorPtr);
4831 ContainerLayerParameters params = mParameters;
4832 params.mBackgroundColor = uniformColor;
4833 params.mLayerCreationHint = GetLayerCreationHint(itemAGR);
4834 if (!transformNode) {
4835 params.mItemVisibleRect = &itemVisibleRectAu;
4836 } else {
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(
4872 scrollMetadataASR,
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
4891 // clip.
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
4905 // them below.
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);
4925 if (!ownLayer) {
4926 continue;
4929 NS_ASSERTION(!ownLayer->AsPaintedLayer(),
4930 "Should never have created a dedicated Painted layer!");
4932 SetBackfaceHiddenForLayer(item->BackfaceIsHidden(), ownLayer);
4934 nsRect invalid;
4935 if (item->IsInvalid(invalid)) {
4936 ownLayer->SetInvalidRectToVisibleRegion();
4939 // If it's not a ContainerLayer, we need to apply the scale transform
4940 // ourselves.
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.
4990 iter.GetNextItem();
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
5016 // chain instead.
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);
5062 } else {
5063 newLayerEntry->mVisibleRegion = itemVisibleRegion;
5065 newLayerEntry->mOpaqueRegion = ComputeOpaqueRect(
5066 item, itemAGR, itemASR, itemClip, aList,
5067 &newLayerEntry->mHideAllLayersBelow,
5068 &newLayerEntry->mOpaqueForAnimatedGeometryRootParent);
5069 } else {
5070 bool useChildrenVisible = itemType == DisplayItemType::TYPE_TRANSFORM &&
5071 (item->Frame()->IsPreserve3DLeaf() ||
5072 item->Frame()->HasPerspective());
5073 const nsIntRegion& visible =
5074 useChildrenVisible
5075 ? item->GetBuildingRectForChildren().ScaleToOutsidePixels(
5076 contentXScale, contentYScale, mAppUnitsPerDevPixel)
5077 : itemVisibleRegion;
5079 SetOuterVisibleRegionForLayer(ownLayer, visible,
5080 layerContentsVisibleRect.width >= 0
5081 ? &layerContentsVisibleRect
5082 : nullptr,
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);
5104 } else {
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,
5121 backfaceHidden);
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);
5135 if (layer) {
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 =
5145 std::move(layer);
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
5203 // layer.
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);
5214 #endif
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);
5232 if (aTransform) {
5233 return aTransform->TransformRect(clipped, aA2D);
5236 return clipped;
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) {
5245 aData->EndUpdate();
5246 return;
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
5252 // yet.
5253 UniquePtr<nsDisplayItemGeometry> geometry;
5254 if (aData->mReusedItem && aData->mGeometry) {
5255 aData->EndUpdate();
5256 return;
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.
5270 nsRect invalid;
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());
5287 #endif
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));
5294 nsRect oldArea =
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);
5302 nsRegion combined;
5303 combined.Or(oldArea, newArea);
5304 invalidPixels = combined.ScaleToOutsidePixels(
5305 layerData->mXScale, layerData->mYScale, appUnitsPerDevPixel);
5306 #ifdef MOZ_DUMP_PAINTING
5307 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
5308 printf_stderr(
5309 "Display item type %s(%p) (in layer %p) belongs to an "
5310 "invalidated frame!\n",
5311 item->Name(), item->Frame(), aData->mLayer.get());
5313 #endif
5314 } else {
5315 // Let the display item check for geometry changes and decide what needs to
5316 // be repainted.
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.
5323 shift = nsPoint();
5326 aData->mGeometry->MoveBy(shift);
5328 nsRegion combined;
5329 item->ComputeInvalidationRegion(mDisplayListBuilder, aData->mGeometry.get(),
5330 &combined);
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(),
5347 &combined);
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()) {
5370 printf_stderr(
5371 "Display item type %s(%p) (in layer %p) changed geometry!\n",
5372 item->Name(), item->Frame(), aData->mLayer.get());
5375 #endif
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,
5388 Layer* aLayer) {
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,
5396 aItem.mMerged);
5397 } else {
5398 data = StoreDataForFrame(aItem.mItem, layer, aItem.mLayerState, nullptr);
5400 data->mInactiveManager = aItem.mInactiveLayerData
5401 ? aItem.mInactiveLayerData->mLayerManager
5402 : nullptr;
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
5422 if (!tmpLayer) {
5423 tempManager->EndTransaction(nullptr, nullptr);
5424 tempManager->SetUserData(&gLayerManagerLayerBuilder, nullptr);
5425 aItem.mItem = nullptr;
5426 return;
5429 bool snap;
5430 nsRect visibleRect = aItem.mItem->GetBuildingRect().Intersect(
5431 aItem.mItem->GetBounds(mDisplayListBuilder, &snap));
5432 nsIntRegion rgn =
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()) {
5440 rgn.AndWith(
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;
5455 #endif
5456 data =
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()) {
5469 fprintf_stderr(
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
5479 nsIntPoint offset =
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,
5486 nullptr)) {
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()) {
5497 printf_stderr(
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(),
5501 layer);
5503 #endif
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) {
5520 MOZ_ASSERT(aItem);
5522 if (aData) {
5523 if (!aData->mUsed) {
5524 aData->BeginUpdate(aLayer, aState, false, aItem);
5526 return aData;
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);
5538 return 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);
5547 return;
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)
5566 : mItem(aItem),
5567 mDisplayItemData(aData),
5568 mTransform(aTransform),
5569 mContentRect(aContentRect),
5570 mLayerState(aLayerState),
5571 mType(aType),
5572 mReused(aItem->IsReused()),
5573 mMerged(aIsMerged),
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) {
5585 return true;
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);
5593 return 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) {
5639 return d;
5642 return nullptr;
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");
5654 return directChild;
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()) {
5664 return;
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
5702 // aTargetASR.
5703 aLayer->SetFixedPositionData(
5704 FindDirectChildASR(aTargetASR, compositorASR)->GetViewId(),
5705 aLayer->GetFixedPositionAnchor(), aLayer->GetFixedPositionSides());
5706 } else {
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
5721 // for this paint.
5722 return;
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.
5732 startASR = stopASR;
5733 } else {
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
5741 // fixed layer.
5742 do {
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
5764 // a layer).
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
5773 // frame.
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
5776 // itself.
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) {
5785 if (!asr) {
5786 MOZ_ASSERT_UNREACHABLE("Should have encountered stopASR on the way up.");
5787 break;
5789 if (clipChain && clipChain->mASR == asr) {
5790 clipChain = clipChain->mParent;
5793 nsIScrollableFrame* scrollFrame = asr->mScrollableFrame;
5794 const DisplayItemClip* clip = (clipChain && clipChain->mASR == asr->mParent)
5795 ? &clipChain->mClip
5796 : nullptr;
5798 scrollFrame->ClipLayerToDisplayPort(aEntry->mLayer, clip, mParameters);
5800 Maybe<ScrollMetadata> metadata;
5801 if (mCachedScrollMetadata.mASR == asr &&
5802 mCachedScrollMetadata.mClip == clip) {
5803 metadata = mCachedScrollMetadata.mMetadata;
5804 } else {
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;
5814 if (!metadata) {
5815 continue;
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);
5826 if (maskLayer) {
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(
5842 Layer* aLayer) {
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);
5857 if (!e->mLayer) {
5858 continue;
5861 OpaqueRegionEntry* data =
5862 FindOpaqueRegionEntry(opaqueRegions, e->mAnimatedGeometryRoot, e->mASR);
5864 SetupScrollingMetadata(e);
5866 if (hideAll) {
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();
5875 } else if (data) {
5876 e->mVisibleRegion.Sub(e->mVisibleRegion, data->mOpaqueRegion);
5880 SetOuterVisibleRegionForLayer(e->mLayer, e->mVisibleRegion,
5881 e->mLayerContentsVisibleRect.width >= 0
5882 ? &e->mLayerContentsVisibleRect
5883 : nullptr,
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,
5896 asrToCover);
5899 if (!data) {
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();
5912 if (clipRect) {
5913 clippedOpaque.AndWith(clipRect->ToUnknownRect());
5915 if (e->mLayer->GetScrolledClip()) {
5916 // The clip can move asynchronously, so we can't rely on opaque parts
5917 // staying visible.
5918 clippedOpaque.SetEmpty();
5919 } else if (e->mHideAllLayersBelow) {
5920 hideAll = true;
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) {
5966 continue;
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
5980 // now.
5981 mContainerLayer->InsertAfter(layer, prevChild);
5982 } else {
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.
5992 if (!layer) {
5993 layer = mContainerLayer->GetFirstChild();
5994 } else {
5995 layer = layer->GetNextSibling();
5997 while (layer) {
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()) {
6011 return;
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();
6038 if (widget) {
6039 return LayoutDevicePixel::ToAppUnits(widget->GetClientSize(),
6040 presContext->AppUnitsPerDevPixel());
6043 return presContext->GetVisibleArea().Size();
6046 /* static */
6047 Size FrameLayerBuilder::ChooseScale(nsIFrame* aContainerFrame,
6048 nsDisplayItem* aContainerItem,
6049 const nsRect& aVisibleRect, float aXScale,
6050 float aYScale, const Matrix& aTransform2d,
6051 bool aCanDraw2D) {
6052 Size scale;
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
6068 // to account
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;
6078 } else {
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
6094 // display size.
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);
6107 } else {
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);
6116 } else {
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.
6125 scale =
6126 Size(std::min(scale.width, 32768.0f), std::min(scale.height, 32768.0f));
6128 return scale;
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) {
6137 nsIntPoint offset;
6139 Matrix4x4 transform =
6140 Matrix4x4::Scaling(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0);
6141 if (aTransform) {
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();
6151 Matrix transform2d;
6152 if (aContainerFrame && aLayerBuilder->GetContainingPaintedLayerData() &&
6153 (!aTransform ||
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
6162 // matter.
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()) {
6178 return false;
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
6190 // prevent this.
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,
6201 aIncomingScale);
6202 if (aTransform) {
6203 aOutgoingScale.mInTransformedSubtree = true;
6204 if (ActiveLayerTracker::IsTransformAnimated(aDisplayListBuilder,
6205 aContainerFrame)) {
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;
6218 return 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) {
6234 return nullptr;
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);
6246 } else {
6247 DisplayItemData* data =
6248 GetOldLayerForFrame(aContainerFrame, containerDisplayItemKey);
6249 if (data) {
6250 oldLayer = data->mLayer;
6254 if (oldLayer) {
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.
6260 } else {
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;
6291 nsRect bounds =
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)) {
6300 return nullptr;
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);
6311 } else {
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;
6325 uint32_t flags;
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
6350 // we won't paint
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);
6382 if (!layer) {
6383 return nullptr;
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
6388 // layer rendering.
6389 return nullptr;
6391 ResetLayerStateForRecycling(layer);
6392 return layer;
6395 /* static */
6396 void FrameLayerBuilder::InvalidateAllLayers(LayerManager* aManager) {
6397 LayerManagerData* data = static_cast<LayerManagerData*>(
6398 aManager->GetUserData(&gLayerManagerUserData));
6399 if (data) {
6400 data->mInvalidateAllLayers = true;
6404 /* static */
6405 void FrameLayerBuilder::InvalidateAllLayersForFrame(nsIFrame* aFrame) {
6406 for (auto* did : GetDisplayItemDataArray(aFrame)) {
6407 DisplayItemData* data = DisplayItemData::AssertDisplayItemData(did);
6408 data->mParent->mInvalidateAllLayers = true;
6412 /* static */
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()) {
6421 continue;
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)) {
6433 return layer;
6438 return nullptr;
6441 /* static */
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)
6455 : aFrame;
6457 for (auto* did : GetDisplayItemDataArray(frameToQuery)) {
6458 DisplayItemData* data = DisplayItemData::AssertDisplayItemData(did);
6459 if (!data->mParent->mLayerManager->IsWidgetLayerManager()) {
6460 continue;
6463 if (GetDisplayItemTypeFromKey(data->mDisplayItemKey) != displayItemType) {
6464 continue;
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)) {
6480 return;
6483 break;
6487 // Bail out if we have already enumerated all possible layers for the given
6488 // display item types.
6489 if (notFoundTypes.none()) {
6490 return;
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)) {
6498 return;
6503 gfxSize FrameLayerBuilder::GetPaintedLayerScaleForFrame(nsIFrame* aFrame) {
6504 MOZ_ASSERT(aFrame, "need a frame");
6506 nsPresContext* presCtx = aFrame->PresContext()->GetRootPresContext();
6508 if (!presCtx) {
6509 presCtx = aFrame->PresContext();
6510 MOZ_ASSERT(presCtx);
6513 nsIFrame* root = presCtx->PresShell()->GetRootFrame();
6515 MOZ_ASSERT(root);
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},
6523 RelativeTo{root}) *
6524 transform;
6527 Matrix transform2d;
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) {
6539 bool snap;
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()) {
6545 return;
6548 RefPtr<DrawTarget> tempDT =
6549 aDrawTarget.CreateSimilarDrawTarget(size, SurfaceFormat::B8G8R8A8);
6550 RefPtr<gfxContext> context = gfxContext::CreateOrNull(tempDT);
6551 if (!context) {
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);
6555 return;
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();
6567 #endif
6569 /* static */
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,
6574 float aYScale) {
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];
6590 if (!cdi->mItem) {
6591 continue;
6594 if (cdi->mHasPaintRect &&
6595 !cdi->mContentRect.Intersects(visible.GetBounds()) &&
6596 !cdi->mContentRect.Intersects(previousRectToDraw)) {
6597 continue;
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.
6606 continue;
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);
6619 continue;
6622 // Do a little dance to account for the fact that we're clipping
6623 // to cdi->mClipRect
6624 nsRegion clipped;
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) {
6631 nsRegion removed;
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),
6650 mHasClip(false),
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.
6669 void Restore() {
6670 if (mCurrentClip.HasClip()) {
6671 mCurrentClip = DisplayItemClip::NoClip();
6674 if (!HasClip()) {
6675 return;
6678 mContext->Restore();
6679 mHasClip = false;
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.
6690 return;
6693 // Remove the previous clip and save the current state.
6694 Restore();
6695 mContext->Save();
6697 // Apply the new clip.
6698 mHasClip = true;
6699 mCurrentClip = *aClip;
6700 mCurrentClip.ApplyTo(mContext, mAppUnitsPerDevPixel);
6701 mContext->NewPath();
6704 private:
6705 gfxContext* mContext;
6706 bool mHasClip;
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.
6717 struct ClipStack {
6718 explicit ClipStack(gfxContext* aContext, const int32_t aAppUnitsPerDevPixel)
6719 : mContext(aContext),
6720 mAppUnitsPerDevPixel(aAppUnitsPerDevPixel),
6721 mDeferredPopClip(false) {}
6723 ~ClipStack() {
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;
6761 return;
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) {
6777 PopClip(false);
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;
6789 return;
6792 PopDeferredClip();
6794 mClips.AppendElement(aClip);
6796 // Save the current state and apply new clip, if needed.
6797 if (aClip.HasClip()) {
6798 mContext->Save();
6799 aClip.ApplyTo(mContext, mAppUnitsPerDevPixel);
6800 mContext->NewPath();
6804 private:
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()) {
6820 return nullptr;
6823 if (clip.GetRoundedRectCount() > 0 &&
6824 !clip.IsRectClippedByRoundedCorner(aItem->GetPaintRect())) {
6825 aOutClip.SetTo(clip.GetClipRect());
6826 return &aOutClip;
6829 return &clip;
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);
6844 } else {
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());
6863 } else {
6864 aMatrixStack.Push(item->GetTransformForRendering());
6867 gfx::Matrix4x4Flagged matrix = aMatrixStack.CurrentMatrix() * aBaseMatrix;
6868 gfx::Matrix matrix2d;
6869 DebugOnly<bool> ok = matrix.CanDraw2D(&matrix2d);
6870 MOZ_ASSERT(ok);
6872 aContext->SetMatrix(matrix2d);
6875 static void UpdateEffectTracking(int& aOpacityLevel, int& aTransformLevel,
6876 const DisplayItemEntryType aType) {
6877 switch (aType) {
6878 case DisplayItemEntryType::PushOpacity:
6879 case DisplayItemEntryType::PushOpacityWithBg:
6880 aOpacityLevel++;
6881 break;
6882 case DisplayItemEntryType::PopOpacity:
6883 aOpacityLevel--;
6884 break;
6885 case DisplayItemEntryType::PushTransform:
6886 aTransformLevel++;
6887 break;
6888 case DisplayItemEntryType::PopTransform:
6889 aTransformLevel--;
6890 break;
6891 default:
6892 break;
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,
6903 float aYScale) {
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.
6916 return;
6919 #ifdef DEBUG
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;
6924 #endif
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);
6959 if (!item) {
6960 MOZ_ASSERT(cdi.mType == DisplayItemEntryType::Item);
6961 continue;
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;
6972 } else {
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();
6991 continue;
6994 #ifdef MOZ_DUMP_PAINTING
6995 AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
6996 "FrameLayerBuilder::PaintItems", GRAPHICS_Rasterization, item->Name());
6997 #else
6998 AUTO_PROFILER_LABEL("FrameLayerBuilder::PaintItems",
6999 GRAPHICS_Rasterization);
7000 #endif
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
7009 // there is one.
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());
7031 aContext->Save();
7032 PushTransform(aContext, cdi, aBuilder, matrixStack, base);
7035 if (cdi.mType == DisplayItemEntryType::PopTransform) {
7036 MOZ_ASSERT(transformLevel > 0);
7037 matrixStack.Pop();
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) {
7054 #ifdef DEBUG
7055 UpdateEffectTracking(opacityLevel, transformLevel, cdi.mType);
7056 #endif
7057 // Nothing more to do with effect markers.
7058 continue;
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.
7074 itemPaintsOwnClip =
7075 paintAsLayer ? false : paintedItem->CanPaintWithClip(*itemClip);
7077 if (!itemPaintsOwnClip) {
7078 // Item does not support painting with clip, set the clip.
7079 itemClipTracker.ChangeClipIfNeeded(itemClip);
7083 if (!itemClip) {
7084 // Item does not need clipping, remove the clip if there is one.
7085 itemClipTracker.Restore();
7088 if (paintAsLayer) {
7089 bool saved = aDrawTarget.GetPermitSubpixelAA();
7090 PaintInactiveLayer(aBuilder, cdi.mInactiveLayerData->mLayerManager, item,
7091 aContext, aContext);
7092 aDrawTarget.SetPermitSubpixelAA(saved);
7093 continue;
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);
7104 continue;
7106 #endif
7108 if (itemPaintsOwnClip) {
7109 MOZ_ASSERT(itemClip);
7110 paintedItem->PaintWithClip(aBuilder, aContext, *itemClip);
7111 } else {
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) {
7132 return false;
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.
7175 /* static */
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) {
7197 return;
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
7213 // PaintedLayer
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,
7229 userData->mYScale);
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));
7239 aContext->Clip();
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,
7255 userData->mYScale);
7256 if (StaticPrefs::gfx_logging_painted_pixel_count_enabled()) {
7257 aLayer->Manager()->AddPaintedPixelCount(iterRect.Area());
7260 } else {
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());
7305 /* static */
7306 void FrameLayerBuilder::DumpRetainedLayerTree(LayerManager* aManager,
7307 std::stringstream& aStream,
7308 bool aDumpHtml) {
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(),
7341 itemPerFrameKey)) {
7342 return data->GetGeometry();
7345 return nullptr;
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) {
7363 return;
7366 RefPtr<Layer> maskLayer = CreateMaskLayer(aLayer, aClip, Nothing());
7368 if (!maskLayer) {
7369 return;
7372 aLayer->SetMaskLayer(maskLayer);
7375 static MaskLayerUserData* GetMaskLayerUserData(Layer* aMaskLayer) {
7376 if (!aMaskLayer) {
7377 return nullptr;
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.
7417 return nullptr;
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));
7428 #else
7429 gfx::Size surfaceSize(std::min<gfx::Float>(boundingRect.Width(), maxSize),
7430 std::min<gfx::Float>(boundingRect.Height(), maxSize));
7431 #endif
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
7436 // space
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.
7442 return nullptr;
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);
7469 if (!container) {
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.");
7479 return nullptr;
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.");
7495 if (!container) {
7496 return nullptr;
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