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/.
9 * structures that represent things to be painted (ordered in z-order),
10 * used during painting and hit testing
13 #include "nsDisplayList.h"
19 #include "gfxContext.h"
21 #include "mozilla/DisplayPortUtils.h"
22 #include "mozilla/dom/BrowserChild.h"
23 #include "mozilla/dom/HTMLCanvasElement.h"
24 #include "mozilla/dom/Selection.h"
25 #include "mozilla/dom/ServiceWorkerRegistrar.h"
26 #include "mozilla/dom/ServiceWorkerRegistration.h"
27 #include "mozilla/dom/SVGElement.h"
28 #include "mozilla/dom/TouchEvent.h"
29 #include "mozilla/gfx/2D.h"
30 #include "mozilla/layers/PLayerTransaction.h"
31 #include "mozilla/PresShell.h"
32 #include "mozilla/ShapeUtils.h"
33 #include "mozilla/StaticPrefs_apz.h"
34 #include "mozilla/StaticPrefs_gfx.h"
35 #include "mozilla/StaticPrefs_layers.h"
36 #include "mozilla/StaticPrefs_layout.h"
37 #include "mozilla/SVGIntegrationUtils.h"
38 #include "mozilla/SVGUtils.h"
39 #include "mozilla/ViewportUtils.h"
40 #include "nsCSSRendering.h"
41 #include "nsCSSRenderingGradients.h"
42 #include "nsRefreshDriver.h"
44 #include "nsStyleStructInlines.h"
45 #include "nsStyleTransformMatrix.h"
46 #include "nsTransitionManager.h"
47 #include "gfxMatrix.h"
48 #include "nsLayoutUtils.h"
49 #include "nsIScrollableFrame.h"
50 #include "nsIFrameInlines.h"
51 #include "nsStyleConsts.h"
52 #include "BorderConsts.h"
53 #include "LayerTreeInvalidation.h"
54 #include "mozilla/MathAlgorithms.h"
56 #include "imgIContainer.h"
57 #include "BasicLayers.h"
58 #include "nsBoxFrame.h"
59 #include "nsImageFrame.h"
60 #include "nsSubDocumentFrame.h"
61 #include "nsViewManager.h"
62 #include "ImageLayers.h"
63 #include "ImageContainer.h"
64 #include "nsCanvasFrame.h"
65 #include "nsSubDocumentFrame.h"
66 #include "StickyScrollContainer.h"
67 #include "mozilla/AnimationPerformanceWarning.h"
68 #include "mozilla/AnimationUtils.h"
69 #include "mozilla/AutoRestore.h"
70 #include "mozilla/EffectCompositor.h"
71 #include "mozilla/EffectSet.h"
72 #include "mozilla/EventStates.h"
73 #include "mozilla/HashTable.h"
74 #include "mozilla/LookAndFeel.h"
75 #include "mozilla/OperatorNewExtensions.h"
76 #include "mozilla/PendingAnimationTracker.h"
77 #include "mozilla/Preferences.h"
78 #include "mozilla/ProfilerLabels.h"
79 #include "mozilla/ProfilerMarkers.h"
80 #include "mozilla/StyleAnimationValue.h"
81 #include "mozilla/ServoBindings.h"
82 #include "mozilla/SVGClipPathFrame.h"
83 #include "mozilla/SVGMaskFrame.h"
84 #include "mozilla/SVGObserverUtils.h"
85 #include "mozilla/Telemetry.h"
86 #include "mozilla/UniquePtr.h"
87 #include "mozilla/Unused.h"
88 #include "mozilla/ViewportFrame.h"
89 #include "mozilla/gfx/gfxVars.h"
90 #include "ActiveLayerTracker.h"
91 #include "nsPrintfCString.h"
92 #include "UnitTransforms.h"
93 #include "LayerAnimationInfo.h"
94 #include "FrameLayerBuilder.h"
95 #include "mozilla/EventStateManager.h"
97 #include "nsDOMTokenList.h"
98 #include "nsCSSProps.h"
99 #include "nsTableCellFrame.h"
100 #include "nsTableColFrame.h"
101 #include "nsTextFrame.h"
102 #include "nsSliderFrame.h"
103 #include "nsFocusManager.h"
104 #include "ClientLayerManager.h"
105 #include "TextDrawTarget.h"
106 #include "mozilla/layers/AnimationHelper.h"
107 #include "mozilla/layers/CompositorThread.h"
108 #include "mozilla/layers/InputAPZContext.h"
109 #include "mozilla/layers/RenderRootStateManager.h"
110 #include "mozilla/layers/StackingContextHelper.h"
111 #include "mozilla/layers/TreeTraversal.h"
112 #include "mozilla/layers/WebRenderBridgeChild.h"
113 #include "mozilla/layers/WebRenderLayerManager.h"
114 #include "mozilla/layers/WebRenderMessages.h"
115 #include "mozilla/layers/WebRenderScrollData.h"
117 using namespace mozilla
;
118 using namespace mozilla::layers
;
119 using namespace mozilla::dom
;
120 using namespace mozilla::layout
;
121 using namespace mozilla::gfx
;
123 typedef ScrollableLayerGuid::ViewID ViewID
;
124 typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox
;
127 static bool SpammyLayoutWarningsEnabled() {
128 static bool sValue
= false;
129 static bool sValueInitialized
= false;
131 if (!sValueInitialized
) {
132 Preferences::GetBool("layout.spammy_warnings.enabled", &sValue
);
133 sValueInitialized
= true;
140 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
141 void AssertUniqueItem(nsDisplayItem
* aItem
) {
142 for (nsDisplayItemBase
* i
: aItem
->Frame()->DisplayItems()) {
143 if (i
!= aItem
&& !i
->HasDeletedFrame() && i
->Frame() == aItem
->Frame() &&
144 i
->GetPerFrameKey() == aItem
->GetPerFrameKey()) {
145 if (i
->IsPreProcessedItem()) {
148 MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
154 bool ShouldBuildItemForEvents(const DisplayItemType aType
) {
155 return aType
== DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
||
156 (GetDisplayItemFlagsForType(aType
) & TYPE_IS_CONTAINER
);
159 static bool ItemTypeSupportsHitTesting(const DisplayItemType aType
) {
161 case DisplayItemType::TYPE_BACKGROUND
:
162 case DisplayItemType::TYPE_BACKGROUND_COLOR
:
163 case DisplayItemType::TYPE_THEMED_BACKGROUND
:
170 void InitializeHitTestInfo(nsDisplayListBuilder
* aBuilder
,
171 nsPaintedDisplayItem
* aItem
,
172 const DisplayItemType aType
) {
173 if (ItemTypeSupportsHitTesting(aType
)) {
174 aItem
->InitializeHitTestInfo(aBuilder
);
179 already_AddRefed
<ActiveScrolledRoot
> ActiveScrolledRoot::CreateASRForFrame(
180 const ActiveScrolledRoot
* aParent
, nsIScrollableFrame
* aScrollableFrame
,
182 nsIFrame
* f
= do_QueryFrame(aScrollableFrame
);
184 RefPtr
<ActiveScrolledRoot
> asr
;
186 asr
= f
->GetProperty(ActiveScrolledRootCache());
190 asr
= new ActiveScrolledRoot();
193 RefPtr
<ActiveScrolledRoot
> ref
= asr
;
194 f
->SetProperty(ActiveScrolledRootCache(), ref
.forget().take());
197 asr
->mParent
= aParent
;
198 asr
->mScrollableFrame
= aScrollableFrame
;
199 asr
->mViewId
= Nothing();
200 asr
->mDepth
= aParent
? aParent
->mDepth
+ 1 : 1;
201 asr
->mRetained
= aIsRetained
;
207 bool ActiveScrolledRoot::IsAncestor(const ActiveScrolledRoot
* aAncestor
,
208 const ActiveScrolledRoot
* aDescendant
) {
210 // nullptr is the root
213 if (Depth(aAncestor
) > Depth(aDescendant
)) {
216 const ActiveScrolledRoot
* asr
= aDescendant
;
218 if (asr
== aAncestor
) {
227 nsCString
ActiveScrolledRoot::ToString(
228 const ActiveScrolledRoot
* aActiveScrolledRoot
) {
230 for (auto* asr
= aActiveScrolledRoot
; asr
; asr
= asr
->mParent
) {
231 str
.AppendPrintf("<0x%p>", asr
->mScrollableFrame
);
233 str
.AppendLiteral(", ");
236 return std::move(str
);
239 mozilla::layers::ScrollableLayerGuid::ViewID
ActiveScrolledRoot::ComputeViewId()
241 nsIContent
* content
= mScrollableFrame
->GetScrolledFrame()->GetContent();
242 return nsLayoutUtils::FindOrCreateIDFor(content
);
245 ActiveScrolledRoot::~ActiveScrolledRoot() {
246 if (mScrollableFrame
&& mRetained
) {
247 nsIFrame
* f
= do_QueryFrame(mScrollableFrame
);
248 f
->RemoveProperty(ActiveScrolledRootCache());
252 static uint64_t AddAnimationsForWebRender(
253 nsDisplayItem
* aItem
, mozilla::layers::RenderRootStateManager
* aManager
,
254 nsDisplayListBuilder
* aDisplayListBuilder
,
255 const Maybe
<LayoutDevicePoint
>& aPosition
= Nothing()) {
257 EffectSet::GetEffectSetForFrame(aItem
->Frame(), aItem
->GetType());
258 if (!effects
|| effects
->IsEmpty()) {
259 // If there is no animation on the nsIFrame, that means
260 // 1) we've never created any animations on this frame or
261 // 2) the frame was reconstruced or
262 // 3) all animations on the frame have finished
263 // in such cases we don't need do anything here.
265 // Even if there is a WebRenderAnimationData for the display item type on
266 // this frame, it's going to be discarded since it's not marked as being
271 RefPtr
<WebRenderAnimationData
> animationData
=
272 aManager
->CommandBuilder()
273 .CreateOrRecycleWebRenderUserData
<WebRenderAnimationData
>(aItem
);
274 AnimationInfo
& animationInfo
= animationData
->GetAnimationInfo();
275 animationInfo
.AddAnimationsForDisplayItem(
276 aItem
->Frame(), aDisplayListBuilder
, aItem
, aItem
->GetType(),
277 aManager
->LayerManager(), aPosition
);
278 animationInfo
.StartPendingAnimations(
279 aManager
->LayerManager()->GetAnimationReadyTime());
281 // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
282 // are no active animations.
283 uint64_t animationsId
= animationInfo
.GetCompositorAnimationsId();
284 if (!animationInfo
.GetAnimations().IsEmpty()) {
285 OpAddCompositorAnimations
anim(
286 CompositorAnimations(animationInfo
.GetAnimations(), animationsId
));
287 aManager
->WrBridge()->AddWebRenderParentCommand(anim
);
288 aManager
->AddActiveCompositorAnimationId(animationsId
);
289 } else if (animationsId
) {
290 aManager
->AddCompositorAnimationsIdForDiscard(animationsId
);
297 static bool GenerateAndPushTextMask(nsIFrame
* aFrame
, gfxContext
* aContext
,
298 const nsRect
& aFillRect
,
299 nsDisplayListBuilder
* aBuilder
) {
300 if (aBuilder
->IsForGenerateGlyphMask()) {
304 SVGObserverUtils::GetAndObserveBackgroundClip(aFrame
);
306 // The main function of enabling background-clip:text property value.
307 // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
309 // 1. Generate a mask by all descendant text frames
310 // 2. Push the generated mask into aContext.
312 gfxContext
* sourceCtx
= aContext
;
313 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
314 aFillRect
, aFrame
->PresContext()->AppUnitsPerDevPixel());
316 // Create a mask surface.
317 RefPtr
<DrawTarget
> sourceTarget
= sourceCtx
->GetDrawTarget();
318 RefPtr
<DrawTarget
> maskDT
= sourceTarget
->CreateClippedDrawTarget(
319 bounds
.ToUnknownRect(), SurfaceFormat::A8
);
320 if (!maskDT
|| !maskDT
->IsValid()) {
323 RefPtr
<gfxContext
> maskCtx
=
324 gfxContext::CreatePreservingTransformOrNull(maskDT
);
326 maskCtx
->Multiply(Matrix::Translation(bounds
.TopLeft().ToUnknownPoint()));
328 // Shade text shape into mask A8 surface.
329 nsLayoutUtils::PaintFrame(
330 maskCtx
, aFrame
, nsRect(nsPoint(0, 0), aFrame
->GetSize()),
331 NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph
);
333 // Push the generated mask into aContext, so that the caller can pop and
336 Matrix currentMatrix
= sourceCtx
->CurrentMatrix();
337 Matrix invCurrentMatrix
= currentMatrix
;
338 invCurrentMatrix
.Invert();
340 RefPtr
<SourceSurface
> maskSurface
= maskDT
->Snapshot();
341 sourceCtx
->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA
, 1.0,
342 maskSurface
, invCurrentMatrix
);
348 void nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
349 Layer
* aLayer
, nsDisplayListBuilder
* aBuilder
, nsDisplayItem
* aItem
,
350 nsIFrame
* aFrame
, DisplayItemType aType
) {
351 // This function can be called in two ways: from
352 // nsDisplay*::BuildLayer while constructing a layer (with all
353 // pointers non-null), or from RestyleManager's handling of
354 // UpdateOpacityLayer/UpdateTransformLayer hints.
355 MOZ_ASSERT(!aBuilder
== !aItem
,
356 "should only be called in two configurations, with both "
357 "aBuilder and aItem, or with neither");
358 MOZ_ASSERT(!aItem
|| aFrame
== aItem
->Frame(), "frame mismatch");
360 // Only send animations to a layer that is actually using
361 // off-main-thread compositing.
362 LayersBackend backend
= aLayer
->Manager()->GetBackendType();
363 if (!(backend
== layers::LayersBackend::LAYERS_CLIENT
||
364 backend
== layers::LayersBackend::LAYERS_WR
)) {
368 AnimationInfo
& animationInfo
= aLayer
->GetAnimationInfo();
369 animationInfo
.AddAnimationsForDisplayItem(aFrame
, aBuilder
, aItem
, aType
,
371 animationInfo
.TransferMutatedFlagToLayer(aLayer
);
374 nsDisplayWrapList
* nsDisplayListBuilder::MergeItems(
375 nsTArray
<nsDisplayWrapList
*>& aItems
) {
376 // For merging, we create a temporary item by cloning the last item of the
377 // mergeable items list. This ensures that the temporary item will have the
378 // correct frame and bounds.
379 nsDisplayWrapList
* merged
= nullptr;
381 for (nsDisplayWrapList
* item
: Reversed(aItems
)) {
385 // Create the temporary item.
386 merged
= item
->Clone(this);
389 AddTemporaryItem(merged
);
391 // Merge the item properties (frame/bounds/etc) with the previously
392 // created temporary item.
393 MOZ_ASSERT(merged
->CanMerge(item
));
397 // Create nsDisplayWrapList that points to the internal display list of the
398 // item we are merging. This nsDisplayWrapList is added to the display list
399 // of the temporary item.
400 merged
->MergeDisplayListFromItem(this, item
);
406 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
407 SetCurrentActiveScrolledRoot(
408 const ActiveScrolledRoot
* aActiveScrolledRoot
) {
411 // Set the builder's mCurrentActiveScrolledRoot.
412 mBuilder
->mCurrentActiveScrolledRoot
= aActiveScrolledRoot
;
414 // We also need to adjust the builder's mCurrentContainerASR.
415 // mCurrentContainerASR needs to be an ASR that all the container's
416 // contents have finite bounds with respect to. If aActiveScrolledRoot
417 // is an ancestor ASR of mCurrentContainerASR, that means we need to
418 // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
419 // the items that will be created with aActiveScrolledRoot wouldn't
420 // have finite bounds with respect to mCurrentContainerASR. There's one
421 // exception, in the case where there's a content clip on the builder
422 // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
423 // content clip will clip all items that are created while this
424 // AutoCurrentActiveScrolledRootSetter exists. This means that the items
425 // created during our lifetime will have finite bounds with respect to
426 // the content clip's ASR, even if the items' actual ASR is an ancestor
427 // of that. And it also means that mCurrentContainerASR only needs to be
428 // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
429 // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
430 // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
432 // finiteBoundsASR is the leafmost ASR that all items created during
433 // object's lifetime have finite bounds with respect to.
434 const ActiveScrolledRoot
* finiteBoundsASR
=
435 ActiveScrolledRoot::PickDescendant(mContentClipASR
, aActiveScrolledRoot
);
437 // mCurrentContainerASR is adjusted so that it's still an ancestor of
439 mBuilder
->mCurrentContainerASR
= ActiveScrolledRoot::PickAncestor(
440 mBuilder
->mCurrentContainerASR
, finiteBoundsASR
);
442 // If we are entering out-of-flow content inside a CSS filter, mark
443 // scroll frames wrt. which the content is fixed as containing such content.
444 if (mBuilder
->mFilterASR
&& ActiveScrolledRoot::IsAncestor(
445 aActiveScrolledRoot
, mBuilder
->mFilterASR
)) {
446 for (const ActiveScrolledRoot
* asr
= mBuilder
->mFilterASR
;
447 asr
&& asr
!= aActiveScrolledRoot
; asr
= asr
->mParent
) {
448 asr
->mScrollableFrame
->SetHasOutOfFlowContentInsideFilter();
455 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
456 InsertScrollFrame(nsIScrollableFrame
* aScrollableFrame
) {
458 size_t descendantsEndIndex
= mBuilder
->mActiveScrolledRoots
.Length();
459 const ActiveScrolledRoot
* parentASR
= mBuilder
->mCurrentActiveScrolledRoot
;
460 const ActiveScrolledRoot
* asr
=
461 mBuilder
->AllocateActiveScrolledRoot(parentASR
, aScrollableFrame
);
462 mBuilder
->mCurrentActiveScrolledRoot
= asr
;
464 // All child ASRs of parentASR that were created while this
465 // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
466 // now. Reparent them to asr.
467 for (size_t i
= mDescendantsStartIndex
; i
< descendantsEndIndex
; i
++) {
468 ActiveScrolledRoot
* descendantASR
= mBuilder
->mActiveScrolledRoots
[i
];
469 if (ActiveScrolledRoot::IsAncestor(parentASR
, descendantASR
)) {
470 descendantASR
->IncrementDepth();
471 if (descendantASR
->mParent
== parentASR
) {
472 descendantASR
->mParent
= asr
;
480 nsPresContext
* nsDisplayListBuilder::CurrentPresContext() {
481 return CurrentPresShellState()->mPresShell
->GetPresContext();
485 nsRect
nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
486 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
487 const nsRect
& aVisibleRect
, const nsRect
& aDirtyRect
,
488 nsRect
* aOutDirtyRect
) {
489 nsRect visible
= aVisibleRect
;
490 nsRect dirtyRectRelativeToDirtyFrame
= aDirtyRect
;
492 bool inPartialUpdate
=
493 aBuilder
->IsRetainingDisplayList() && aBuilder
->IsPartialUpdate();
494 if (StaticPrefs::apz_allow_zooming() &&
495 DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame
) &&
496 aBuilder
->IsPaintingToWindow() && !inPartialUpdate
) {
497 dirtyRectRelativeToDirtyFrame
=
498 nsRect(nsPoint(0, 0), aFrame
->GetParent()->GetSize());
500 // If there's a visual viewport size set, restrict the amount of the
501 // fixed-position element we paint to the visual viewport. (In general
502 // the fixed-position element can be as large as the layout viewport,
503 // which at a high zoom level can cause us to paint too large of an
505 PresShell
* presShell
= aFrame
->PresShell();
506 if (presShell
->IsVisualViewportSizeSet()) {
507 dirtyRectRelativeToDirtyFrame
=
508 nsRect(presShell
->GetVisualViewportOffsetRelativeToLayoutViewport(),
509 presShell
->GetVisualViewportSize());
510 // But if we have a displayport, expand it to the displayport, so
511 // that async-scrolling the visual viewport within the layout viewport
512 // will not checkerboard.
513 if (nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame()) {
515 // Note that the displayport here is already in the right coordinate
516 // space: it's relative to the scroll port (= layout viewport), but
517 // covers the visual viewport with some margins around it, which is
518 // exactly what we want.
519 if (DisplayPortUtils::GetHighResolutionDisplayPort(
520 rootScrollFrame
->GetContent(), &displayport
,
521 DisplayPortOptions().With(ContentGeometryType::Fixed
))) {
522 dirtyRectRelativeToDirtyFrame
= displayport
;
526 visible
= dirtyRectRelativeToDirtyFrame
;
527 if (StaticPrefs::apz_test_logging_enabled() &&
528 presShell
->GetDocument()->IsContentDocument()) {
529 nsLayoutUtils::LogAdditionalTestData(
530 aBuilder
, "fixedPosDisplayport",
531 ToString(CSSSize::FromAppUnits(visible
)));
535 *aOutDirtyRect
= dirtyRectRelativeToDirtyFrame
- aFrame
->GetPosition();
536 visible
-= aFrame
->GetPosition();
538 nsRect overflowRect
= aFrame
->InkOverflowRect();
540 if (aFrame
->IsTransformed() &&
541 mozilla::EffectCompositor::HasAnimationsForCompositor(
542 aFrame
, DisplayItemType::TYPE_TRANSFORM
)) {
544 * Add a fuzz factor to the overflow rectangle so that elements only
545 * just out of view are pulled into the display list, so they can be
546 * prerendered if necessary.
548 overflowRect
.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
551 visible
.IntersectRect(visible
, overflowRect
);
552 aOutDirtyRect
->IntersectRect(*aOutDirtyRect
, overflowRect
);
557 nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder
* aBuilder
,
559 // Links don't nest, so if the builder already has a destination, no need to
560 // check for a link element here.
561 if (!aBuilder
->mLinkSpec
.IsEmpty()) {
565 // Find the element that we need to check for link-ness, bailing out if
566 // we can't find one.
567 Element
* elem
= Element::FromNodeOrNull(aFrame
->GetContent());
572 // Check if we have actually found a link and it has a usable spec.
573 nsCOMPtr
<nsIURI
> linkURI
;
574 if (!elem
->IsLink(getter_AddRefs(linkURI
))) {
577 if (NS_FAILED(linkURI
->GetSpec(aBuilder
->mLinkSpec
)) ||
578 aBuilder
->mLinkSpec
.IsEmpty()) {
582 // Record that we need to reset the builder's state on destruction.
583 mBuilderToReset
= aBuilder
;
586 void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
587 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
) {
588 // Note that we may generate a link here even if the constructor bailed out
589 // without updating aBuilder->LinkSpec(), because it may have been set by
590 // an ancestor that was associated with a link element.
591 if (!aBuilder
->mLinkSpec
.IsEmpty()) {
592 auto* link
= MakeDisplayItem
<nsDisplayLink
>(
593 aBuilder
, aFrame
, aBuilder
->mLinkSpec
.get(), aFrame
->GetRect());
594 aList
->AppendToTop(link
);
598 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame
* aReferenceFrame
,
599 nsDisplayListBuilderMode aMode
,
601 bool aRetainingDisplayList
)
602 : mReferenceFrame(aReferenceFrame
),
603 mIgnoreScrollFrame(nullptr),
604 mCurrentActiveScrolledRoot(nullptr),
605 mCurrentContainerASR(nullptr),
606 mCurrentFrame(aReferenceFrame
),
607 mCurrentReferenceFrame(aReferenceFrame
),
608 mRootAGR(AnimatedGeometryRoot::CreateAGRForFrame(
609 aReferenceFrame
, nullptr, true, aRetainingDisplayList
)),
610 mCurrentAGR(mRootAGR
),
611 mBuildingExtraPagesForPageNum(0),
613 mDirtyRect(-1, -1, -1, -1),
614 mGlassDisplayItem(nullptr),
615 mHasGlassItemDuringPartial(false),
616 mCaretFrame(nullptr),
617 mScrollInfoItemsForHoisting(nullptr),
618 mFirstClipChainToDestroy(nullptr),
620 mTableBackgroundSet(nullptr),
621 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID
),
622 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID
),
624 mContainsBlendMode(false),
625 mIsBuildingScrollbar(false),
626 mCurrentScrollbarWillHaveLayer(false),
627 mBuildCaret(aBuildCaret
),
628 mRetainingDisplayList(aRetainingDisplayList
),
629 mPartialUpdate(false),
630 mIgnoreSuppression(false),
631 mIncludeAllOutOfFlows(false),
632 mDescendIntoSubdocuments(true),
633 mSelectedFramesOnly(false),
634 mAllowMergingAndFlattening(true),
636 mInEventsOnly(false),
638 mInPageSequence(false),
639 mIsInChromePresContext(false),
640 mSyncDecodeImages(false),
641 mIsPaintingToWindow(false),
642 mUseHighQualityScaling(false),
643 mIsPaintingForWebRender(false),
644 mIsCompositingCheap(false),
645 mAncestorHasApzAwareEventHandler(false),
646 mHaveScrollableDisplayPort(false),
647 mWindowDraggingAllowed(false),
648 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame
)),
649 mForceLayerForScrollParent(false),
650 mContainsNonMinimalDisplayPort(false),
651 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame
)),
652 mBuildingInvisibleItems(false),
654 mInInvalidSubtree(false),
655 mDisablePartialUpdates(false),
656 mPartialBuildFailed(false),
657 mIsInActiveDocShell(false),
658 mBuildAsyncZoomContainer(false),
659 mContainsBackdropFilter(false),
660 mIsRelativeToLayoutViewport(false),
661 mUseOverlayScrollbars(false),
662 mAlwaysLayerizeScrollbars(false) {
663 MOZ_COUNT_CTOR(nsDisplayListBuilder
);
665 mBuildCompositorHitTestInfo
= mAsyncPanZoomEnabled
&& IsForPainting();
667 ShouldRebuildDisplayListDueToPrefChange();
669 mUseOverlayScrollbars
=
670 (LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars
) != 0);
672 mAlwaysLayerizeScrollbars
=
673 StaticPrefs::layout_scrollbars_always_layerize_track();
676 static_cast<uint32_t>(DisplayItemType::TYPE_MAX
) < (1 << TYPE_BITS
),
677 "Check TYPE_MAX should not overflow");
680 static PresShell
* GetFocusedPresShell() {
681 nsPIDOMWindowOuter
* focusedWnd
=
682 nsFocusManager::GetFocusManager()->GetFocusedWindow();
687 nsCOMPtr
<nsIDocShell
> focusedDocShell
= focusedWnd
->GetDocShell();
688 if (!focusedDocShell
) {
692 return focusedDocShell
->GetPresShell();
695 void nsDisplayListBuilder::BeginFrame() {
696 nsCSSRendering::BeginFrameTreesLocked();
697 mCurrentAGR
= mRootAGR
;
698 mFrameToAnimatedGeometryRootMap
.InsertOrUpdate(mReferenceFrame
,
701 mIsPaintingToWindow
= false;
702 mUseHighQualityScaling
= false;
703 mIgnoreSuppression
= false;
704 mInTransform
= false;
706 mSyncDecodeImages
= false;
712 RefPtr
<PresShell
> presShell
= GetFocusedPresShell();
714 RefPtr
<nsCaret
> caret
= presShell
->GetCaret();
715 mCaretFrame
= caret
->GetPaintGeometry(&mCaretRect
);
717 // The focused pres shell may not be in the document that we're
718 // painting, or be in a popup. Check if the display root for
719 // the caret matches the display root that we're painting, and
720 // only use it if it matches.
722 nsLayoutUtils::GetDisplayRootFrame(mCaretFrame
) !=
723 nsLayoutUtils::GetDisplayRootFrame(mReferenceFrame
)) {
724 mCaretFrame
= nullptr;
729 void nsDisplayListBuilder::EndFrame() {
730 NS_ASSERTION(!mInInvalidSubtree
,
731 "Someone forgot to cleanup mInInvalidSubtree!");
732 mFrameToAnimatedGeometryRootMap
.Clear();
733 mAGRBudgetSet
.Clear();
734 mActiveScrolledRoots
.Clear();
735 mEffectsUpdates
.Clear();
737 FreeTemporaryItems();
738 nsCSSRendering::EndFrameTreesLocked();
739 mCaretFrame
= nullptr;
742 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame
* aFrame
,
743 const nsIFrame
* aStopAtFrame
) {
744 mFramesMarkedForDisplay
.AppendElement(aFrame
);
745 for (nsIFrame
* f
= aFrame
; f
;
746 f
= nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f
)) {
747 if (f
->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
)) {
750 f
->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
);
751 if (f
== aStopAtFrame
) {
752 // we've reached a frame that we know will be painted, so we can stop.
758 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame
* aFrame
) {
759 mFramesMarkedForDisplayIfVisible
.AppendElement(aFrame
);
762 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
763 nsIFrame
* aFrame
, const nsIFrame
* aStopAtFrame
) {
764 AddFrameMarkedForDisplayIfVisible(aFrame
);
765 for (nsIFrame
* f
= aFrame
; f
; f
= nsLayoutUtils::GetDisplayListParent(f
)) {
766 if (f
->ForceDescendIntoIfVisible()) {
769 f
->SetForceDescendIntoIfVisible(true);
770 if (f
== aStopAtFrame
) {
771 // we've reached a frame that we know will be painted, so we can stop.
777 void nsDisplayListBuilder::SetGlassDisplayItem(nsDisplayItem
* aItem
) {
778 // Web pages or extensions could trigger the "Multiple glass backgrounds
779 // found?" warning by using -moz-appearance:win-borderless-glass etc on their
780 // own elements (as long as they are DocElementBoxFrames, which is rare as
781 // each xul doc only gets one near the root). We only care about first one,
782 // since that will be the background of the root window.
784 if (IsPartialUpdate()) {
785 if (aItem
->Frame()->IsDocElementBoxFrame()) {
787 if (mHasGlassItemDuringPartial
) {
788 NS_WARNING("Multiple glass backgrounds found?");
791 if (!mHasGlassItemDuringPartial
) {
792 mHasGlassItemDuringPartial
= true;
793 aItem
->SetIsGlassItem();
799 if (aItem
->Frame()->IsDocElementBoxFrame()) {
801 if (mGlassDisplayItem
) {
802 NS_WARNING("Multiple glass backgrounds found?");
805 if (!mGlassDisplayItem
) {
806 mGlassDisplayItem
= aItem
;
807 mGlassDisplayItem
->SetIsGlassItem();
812 bool nsDisplayListBuilder::NeedToForceTransparentSurfaceForItem(
813 nsDisplayItem
* aItem
) {
814 return aItem
== mGlassDisplayItem
;
817 AnimatedGeometryRoot
* nsDisplayListBuilder::WrapAGRForFrame(
818 nsIFrame
* aAnimatedGeometryRoot
, bool aIsAsync
,
819 AnimatedGeometryRoot
* aParent
/* = nullptr */) {
820 DebugOnly
<bool> dummy
;
821 MOZ_ASSERT(IsAnimatedGeometryRoot(aAnimatedGeometryRoot
, dummy
) == AGR_YES
);
823 RefPtr
<AnimatedGeometryRoot
> result
=
824 mFrameToAnimatedGeometryRootMap
.Get(aAnimatedGeometryRoot
);
826 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(RootReferenceFrame(),
827 aAnimatedGeometryRoot
));
828 RefPtr
<AnimatedGeometryRoot
> parent
= aParent
;
830 nsIFrame
* parentFrame
=
831 nsLayoutUtils::GetCrossDocParentFrame(aAnimatedGeometryRoot
);
834 nsIFrame
* parentAGRFrame
=
835 FindAnimatedGeometryRootFrameFor(parentFrame
, isAsync
);
836 parent
= WrapAGRForFrame(parentAGRFrame
, isAsync
);
839 result
= AnimatedGeometryRoot::CreateAGRForFrame(
840 aAnimatedGeometryRoot
, parent
, aIsAsync
, IsRetainingDisplayList());
841 mFrameToAnimatedGeometryRootMap
.InsertOrUpdate(aAnimatedGeometryRoot
,
844 MOZ_ASSERT(!aParent
|| result
->mParentAGR
== aParent
);
848 AnimatedGeometryRoot
* nsDisplayListBuilder::AnimatedGeometryRootForASR(
849 const ActiveScrolledRoot
* aASR
) {
851 return GetRootAnimatedGeometryRoot();
853 nsIFrame
* scrolledFrame
= aASR
->mScrollableFrame
->GetScrolledFrame();
854 return FindAnimatedGeometryRootFor(scrolledFrame
);
857 AnimatedGeometryRoot
* nsDisplayListBuilder::FindAnimatedGeometryRootFor(
859 if (!IsPaintingToWindow()) {
862 if (aFrame
== mCurrentFrame
) {
866 RefPtr
<AnimatedGeometryRoot
> result
=
867 mFrameToAnimatedGeometryRootMap
.Get(aFrame
);
873 nsIFrame
* agrFrame
= FindAnimatedGeometryRootFrameFor(aFrame
, isAsync
);
874 result
= WrapAGRForFrame(agrFrame
, isAsync
);
875 mFrameToAnimatedGeometryRootMap
.InsertOrUpdate(aFrame
, RefPtr
{result
});
879 AnimatedGeometryRoot
* nsDisplayListBuilder::FindAnimatedGeometryRootFor(
880 nsDisplayItem
* aItem
) {
881 if (aItem
->ShouldFixToViewport(this)) {
882 // Make its active scrolled root be the active scrolled root of
883 // the enclosing viewport, since it shouldn't be scrolled by scrolled
884 // frames in its document. InvalidateFixedBackgroundFramesFromList in
885 // nsGfxScrollFrame will not repaint this item when scrolling occurs.
886 nsIFrame
* viewportFrame
= nsLayoutUtils::GetClosestFrameOfType(
887 aItem
->Frame(), LayoutFrameType::Viewport
, RootReferenceFrame());
889 return FindAnimatedGeometryRootFor(viewportFrame
);
892 return FindAnimatedGeometryRootFor(aItem
->Frame());
895 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
896 mIsRelativeToLayoutViewport
= true;
897 UpdateShouldBuildAsyncZoomContainer();
900 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
901 const Document
* document
= mReferenceFrame
->PresContext()->Document();
902 mBuildAsyncZoomContainer
= !mIsRelativeToLayoutViewport
&&
903 !document
->Fullscreen() &&
904 nsLayoutUtils::AllowZoomingForDocument(document
);
907 // Certain prefs may cause display list items to be added or removed when they
908 // are toggled. In those cases, we need to fully rebuild the display list.
909 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
910 // If we transition between wrapping the RCD-RSF contents into an async
911 // zoom container vs. not, we need to rebuild the display list. This only
912 // happens when the zooming or container scrolling prefs are toggled
913 // (manually by the user, or during test setup).
914 bool didBuildAsyncZoomContainer
= mBuildAsyncZoomContainer
;
915 UpdateShouldBuildAsyncZoomContainer();
917 bool hadOverlayScrollbarsLastTime
= mUseOverlayScrollbars
;
918 mUseOverlayScrollbars
=
919 (LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars
) != 0);
921 bool alwaysLayerizedScrollbarsLastTime
= mAlwaysLayerizeScrollbars
;
922 mAlwaysLayerizeScrollbars
=
923 StaticPrefs::layout_scrollbars_always_layerize_track();
925 if (didBuildAsyncZoomContainer
!= mBuildAsyncZoomContainer
) {
929 if (hadOverlayScrollbarsLastTime
!= mUseOverlayScrollbars
) {
933 if (alwaysLayerizedScrollbarsLastTime
!= mAlwaysLayerizeScrollbars
) {
940 void nsDisplayListBuilder::AddScrollFrameToNotify(
941 nsIScrollableFrame
* aScrollFrame
) {
942 mScrollFramesToNotify
.insert(aScrollFrame
);
945 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
946 for (const auto& it
: mScrollFramesToNotify
) {
947 it
->NotifyApzTransaction();
949 mScrollFramesToNotify
.clear();
952 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
953 nsIFrame
* aDirtyFrame
, nsIFrame
* aFrame
, const nsRect
& aVisibleRect
,
954 const nsRect
& aDirtyRect
) {
955 MOZ_ASSERT(aFrame
->GetParent() == aDirtyFrame
);
957 nsRect visible
= OutOfFlowDisplayData::ComputeVisibleRectForFrame(
958 this, aFrame
, aVisibleRect
, aDirtyRect
, &dirty
);
959 if (!aFrame
->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) &&
964 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
965 // frame, then it will also mark any outer frames to ensure that building
966 // reaches the dirty feame.
967 if (!dirty
.IsEmpty() || aFrame
->ForceDescendIntoIfVisible()) {
968 MarkFrameForDisplay(aFrame
, aDirtyFrame
);
974 static void UnmarkFrameForDisplay(nsIFrame
* aFrame
,
975 const nsIFrame
* aStopAtFrame
) {
976 for (nsIFrame
* f
= aFrame
; f
;
977 f
= nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f
)) {
978 if (!f
->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
)) {
981 f
->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
);
982 if (f
== aStopAtFrame
) {
983 // we've reached a frame that we know will be painted, so we can stop.
989 static void UnmarkFrameForDisplayIfVisible(nsIFrame
* aFrame
) {
990 for (nsIFrame
* f
= aFrame
; f
; f
= nsLayoutUtils::GetDisplayListParent(f
)) {
991 if (!f
->ForceDescendIntoIfVisible()) {
994 f
->SetForceDescendIntoIfVisible(false);
998 nsDisplayListBuilder::~nsDisplayListBuilder() {
999 NS_ASSERTION(mFramesMarkedForDisplay
.Length() == 0,
1000 "All frames should have been unmarked");
1001 NS_ASSERTION(mFramesWithOOFData
.Length() == 0,
1002 "All OOF data should have been removed");
1003 NS_ASSERTION(mPresShellStates
.Length() == 0,
1004 "All presshells should have been exited");
1006 DisplayItemClipChain
* c
= mFirstClipChainToDestroy
;
1008 DisplayItemClipChain
* next
= c
->mNextClipChainToDestroy
;
1009 c
->DisplayItemClipChain::~DisplayItemClipChain();
1013 MOZ_COUNT_DTOR(nsDisplayListBuilder
);
1016 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
1018 if (mSyncDecodeImages
) {
1019 flags
|= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES
;
1021 if (mIsPaintingToWindow
) {
1022 flags
|= nsCSSRendering::PAINTBG_TO_WINDOW
;
1024 if (mUseHighQualityScaling
) {
1025 flags
|= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING
;
1030 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
1031 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
1033 if (mSyncDecodeImages
) {
1034 flags
|= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES
;
1036 if (mIsPaintingToWindow
) {
1037 flags
|= nsImageRenderer::FLAG_PAINTING_TO_WINDOW
;
1039 if (mUseHighQualityScaling
) {
1040 flags
|= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING
;
1045 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1046 uint32_t flags
= imgIContainer::FLAG_ASYNC_NOTIFY
;
1047 if (mSyncDecodeImages
) {
1048 flags
|= imgIContainer::FLAG_SYNC_DECODE
;
1050 flags
|= imgIContainer::FLAG_SYNC_DECODE_IF_FAST
;
1052 if (mIsPaintingToWindow
|| mUseHighQualityScaling
) {
1053 flags
|= imgIContainer::FLAG_HIGH_QUALITY_SCALING
;
1058 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion
* aVisibleRegion
,
1059 const nsRegion
& aRegion
) {
1060 if (aRegion
.IsEmpty()) {
1065 tmp
.Sub(*aVisibleRegion
, aRegion
);
1066 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1067 // to its bounds either, which can be very bad (see bug 516740).
1068 // Do let aVisibleRegion get more complex if by doing so we reduce its
1069 // area by at least half.
1070 if (tmp
.GetNumRects() <= 15 || tmp
.Area() <= aVisibleRegion
->Area() / 2) {
1071 *aVisibleRegion
= tmp
;
1075 nsCaret
* nsDisplayListBuilder::GetCaret() {
1076 RefPtr
<nsCaret
> caret
= CurrentPresShellState()->mPresShell
->GetCaret();
1080 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell
* aPresShell
) {
1081 if (mIsPaintingToWindow
) {
1082 mReferenceFrame
->AddPaintedPresShell(aPresShell
);
1083 aPresShell
->IncrementPaintCount();
1087 void nsDisplayListBuilder::EnterPresShell(const nsIFrame
* aReferenceFrame
,
1088 bool aPointerEventsNoneDoc
) {
1089 PresShellState
* state
= mPresShellStates
.AppendElement();
1090 state
->mPresShell
= aReferenceFrame
->PresShell();
1091 state
->mFirstFrameMarkedForDisplay
= mFramesMarkedForDisplay
.Length();
1092 state
->mFirstFrameWithOOFData
= mFramesWithOOFData
.Length();
1094 nsIScrollableFrame
* sf
= state
->mPresShell
->GetRootScrollFrameAsScrollable();
1095 if (sf
&& IsInSubdocument()) {
1096 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1097 // that the canvas background color will be set correctly, and that only one
1098 // unscrollable item will be created.
1099 // This is done to avoid, for example, a case where only scrollbar frames
1100 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1101 // and possibly end up with an extra nsDisplaySolidColor item.
1102 // We skip this for the root document, since we don't want to use
1103 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1104 // do it manually there.
1105 nsCanvasFrame
* canvasFrame
= do_QueryFrame(sf
->GetScrolledFrame());
1107 MarkFrameForDisplayIfVisible(canvasFrame
, aReferenceFrame
);
1112 state
->mAutoLayoutPhase
.emplace(aReferenceFrame
->PresContext(),
1113 nsLayoutPhase::DisplayListBuilding
);
1116 state
->mPresShell
->UpdateCanvasBackground();
1118 bool buildCaret
= mBuildCaret
;
1119 if (mIgnoreSuppression
|| !state
->mPresShell
->IsPaintingSuppressed()) {
1120 state
->mIsBackgroundOnly
= false;
1122 state
->mIsBackgroundOnly
= true;
1126 bool pointerEventsNone
= aPointerEventsNoneDoc
;
1127 if (IsInSubdocument()) {
1128 pointerEventsNone
|= mPresShellStates
[mPresShellStates
.Length() - 2]
1129 .mInsidePointerEventsNoneDoc
;
1131 state
->mInsidePointerEventsNoneDoc
= pointerEventsNone
;
1133 state
->mPresShellIgnoreScrollFrame
=
1134 state
->mPresShell
->IgnoringViewportScrolling()
1135 ? state
->mPresShell
->GetRootScrollFrame()
1138 nsPresContext
* pc
= aReferenceFrame
->PresContext();
1139 mIsInChromePresContext
= pc
->IsChrome();
1140 nsIDocShell
* docShell
= pc
->GetDocShell();
1143 docShell
->GetWindowDraggingAllowed(&mWindowDraggingAllowed
);
1146 state
->mTouchEventPrefEnabledDoc
= dom::TouchEvent::PrefEnabled(docShell
);
1152 // Caret frames add visual area to their frame, but we don't update the
1153 // overflow area. Use flags to make sure we build display items for that frame
1155 if (mCaretFrame
&& mCaretFrame
->PresShell() == state
->mPresShell
) {
1156 MarkFrameForDisplay(mCaretFrame
, aReferenceFrame
);
1160 // A non-blank paint is a paint that does not just contain the canvas
1162 static bool DisplayListIsNonBlank(nsDisplayList
* aList
) {
1163 for (nsDisplayItem
* i
: *aList
) {
1164 switch (i
->GetType()) {
1165 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
:
1166 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR
:
1167 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE
:
1169 case DisplayItemType::TYPE_SOLID_COLOR
:
1170 case DisplayItemType::TYPE_BACKGROUND
:
1171 case DisplayItemType::TYPE_BACKGROUND_COLOR
:
1172 if (i
->Frame()->IsCanvasFrame()) {
1183 // A contentful paint is a paint that does contains DOM content (text,
1184 // images, non-blank canvases, SVG): "First Contentful Paint entry
1185 // contains a DOMHighResTimeStamp reporting the time when the browser
1186 // first rendered any text, image (including background images),
1187 // non-white canvas or SVG. This excludes any content of iframes, but
1188 // includes text with pending webfonts. This is the first time users
1189 // could start consuming page content."
1190 static bool DisplayListIsContentful(nsDisplayListBuilder
* aBuilder
,
1191 nsDisplayList
* aList
) {
1192 for (nsDisplayItem
* i
: *aList
) {
1193 DisplayItemType type
= i
->GetType();
1194 nsDisplayList
* children
= i
->GetChildren();
1197 case DisplayItemType::TYPE_SUBDOCUMENT
: // iframes are ignored
1199 // CANVASes check if they may have been modified (as a stand-in
1200 // actually tracking all modifications)
1202 if (i
->IsContentful()) {
1204 nsRect bound
= i
->GetBounds(aBuilder
, &dummy
);
1205 if (!bound
.IsEmpty()) {
1210 if (DisplayListIsContentful(aBuilder
, children
)) {
1220 void nsDisplayListBuilder::LeavePresShell(const nsIFrame
* aReferenceFrame
,
1221 nsDisplayList
* aPaintedContents
) {
1223 CurrentPresShellState()->mPresShell
== aReferenceFrame
->PresShell(),
1224 "Presshell mismatch");
1226 if (mIsPaintingToWindow
&& aPaintedContents
) {
1227 nsPresContext
* pc
= aReferenceFrame
->PresContext();
1228 if (!pc
->HadNonBlankPaint()) {
1229 if (!CurrentPresShellState()->mIsBackgroundOnly
&&
1230 DisplayListIsNonBlank(aPaintedContents
)) {
1231 pc
->NotifyNonBlankPaint();
1234 nsRootPresContext
* rootPresContext
= pc
->GetRootPresContext();
1235 if (!pc
->HadContentfulPaint() && rootPresContext
) {
1236 if (!CurrentPresShellState()->mIsBackgroundOnly
) {
1237 if (pc
->HasEverBuiltInvisibleText() ||
1238 DisplayListIsContentful(this, aPaintedContents
)) {
1239 pc
->NotifyContentfulPaint();
1245 ResetMarkedFramesForDisplayList(aReferenceFrame
);
1246 mPresShellStates
.RemoveLastElement();
1248 if (!mPresShellStates
.IsEmpty()) {
1249 nsPresContext
* pc
= CurrentPresContext();
1250 nsIDocShell
* docShell
= pc
->GetDocShell();
1252 docShell
->GetWindowDraggingAllowed(&mWindowDraggingAllowed
);
1254 mIsInChromePresContext
= pc
->IsChrome();
1256 mCurrentAGR
= mRootAGR
;
1258 for (uint32_t i
= 0; i
< mFramesMarkedForDisplayIfVisible
.Length(); ++i
) {
1259 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible
[i
]);
1261 mFramesMarkedForDisplayIfVisible
.SetLength(0);
1265 void nsDisplayListBuilder::FreeClipChains() {
1266 // Iterate the clip chains from newest to oldest (forward
1267 // iteration), so that we destroy descendants first which
1268 // will drop the ref count on their ancestors.
1269 DisplayItemClipChain
** indirect
= &mFirstClipChainToDestroy
;
1272 if (!(*indirect
)->mRefCount
) {
1273 DisplayItemClipChain
* next
= (*indirect
)->mNextClipChainToDestroy
;
1275 mClipDeduplicator
.erase(*indirect
);
1276 (*indirect
)->DisplayItemClipChain::~DisplayItemClipChain();
1277 Destroy(DisplayListArenaObjectId::CLIPCHAIN
, *indirect
);
1281 indirect
= &(*indirect
)->mNextClipChainToDestroy
;
1286 void nsDisplayListBuilder::FreeTemporaryItems() {
1287 for (nsDisplayItem
* i
: mTemporaryItems
) {
1288 // Temporary display items are not added to the frames.
1289 MOZ_ASSERT(i
->Frame());
1290 i
->RemoveFrame(i
->Frame());
1294 mTemporaryItems
.Clear();
1297 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1298 const nsIFrame
* aReferenceFrame
) {
1299 // Unmark and pop off the frames marked for display in this pres shell.
1300 uint32_t firstFrameForShell
=
1301 CurrentPresShellState()->mFirstFrameMarkedForDisplay
;
1302 for (uint32_t i
= firstFrameForShell
; i
< mFramesMarkedForDisplay
.Length();
1304 UnmarkFrameForDisplay(mFramesMarkedForDisplay
[i
], aReferenceFrame
);
1306 mFramesMarkedForDisplay
.SetLength(firstFrameForShell
);
1308 firstFrameForShell
= CurrentPresShellState()->mFirstFrameWithOOFData
;
1309 for (uint32_t i
= firstFrameForShell
; i
< mFramesWithOOFData
.Length(); ++i
) {
1310 mFramesWithOOFData
[i
]->RemoveProperty(OutOfFlowDisplayDataProperty());
1312 mFramesWithOOFData
.SetLength(firstFrameForShell
);
1315 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1316 CurrentPresShellState()->mFixedBackgroundDisplayData
= Nothing();
1319 void nsDisplayListBuilder::MarkFramesForDisplayList(
1320 nsIFrame
* aDirtyFrame
, const nsFrameList
& aFrames
) {
1321 nsRect visibleRect
= GetVisibleRect();
1322 nsRect dirtyRect
= GetDirtyRect();
1324 // If we are entering content that is fixed to the RCD-RSF, we are
1325 // crossing the async zoom container boundary, and need to convert from
1326 // visual to layout coordinates.
1327 if (ViewportFrame
* viewportFrame
= do_QueryFrame(aDirtyFrame
)) {
1328 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1329 viewportFrame
->PresContext()->IsRootContentDocumentCrossProcess()) {
1330 if (viewportFrame
->PresShell()->GetRootScrollFrame()) {
1332 for (nsIFrame
* f
: aFrames
) {
1333 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f
));
1336 visibleRect
= ViewportUtils::VisualToLayout(visibleRect
,
1337 viewportFrame
->PresShell());
1338 dirtyRect
= ViewportUtils::VisualToLayout(dirtyRect
,
1339 viewportFrame
->PresShell());
1343 // This is an edge case that should only happen if we are in a
1344 // document with a XUL root element so that it does not have a root
1345 // scroll frame but it has fixed pos content and all of the frames in
1346 // aFrames are that fixed pos content.
1347 for (nsIFrame
* f
: aFrames
) {
1348 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f
) &&
1349 f
->GetParent() == aDirtyFrame
&&
1350 f
->StyleDisplay()->mPosition
==
1351 StylePositionProperty::Fixed
);
1353 // There's no root scroll frame so there can't be any zooming or async
1354 // panning so we don't need to adjust the visible and dirty rects.
1360 bool markedFrames
= false;
1361 for (nsIFrame
* e
: aFrames
) {
1362 // Skip the AccessibleCaret frame when building no caret.
1363 if (!IsBuildingCaret()) {
1364 nsIContent
* content
= e
->GetContent();
1365 if (content
&& content
->IsInNativeAnonymousSubtree() &&
1366 content
->IsElement()) {
1367 auto classList
= content
->AsElement()->ClassList();
1368 if (classList
->Contains(u
"moz-accessiblecaret"_ns
)) {
1373 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame
, e
, visibleRect
, dirtyRect
)) {
1374 markedFrames
= true;
1379 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1380 // to objects on the stack, so we need to clone the chain.
1381 const DisplayItemClipChain
* clipChain
=
1382 CopyWholeChain(mClipState
.GetClipChainForContainingBlockDescendants());
1383 const DisplayItemClipChain
* combinedClipChain
=
1384 mClipState
.GetCurrentCombinedClipChain(this);
1385 const ActiveScrolledRoot
* asr
= mCurrentActiveScrolledRoot
;
1387 OutOfFlowDisplayData
* data
= new OutOfFlowDisplayData(
1388 clipChain
, combinedClipChain
, asr
, visibleRect
, dirtyRect
);
1389 aDirtyFrame
->SetProperty(
1390 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data
);
1391 mFramesWithOOFData
.AppendElement(aDirtyFrame
);
1394 if (!aDirtyFrame
->GetParent()) {
1395 // This is the viewport frame of aDirtyFrame's presshell.
1396 // Store the current display data so that it can be used for fixed
1397 // background images.
1399 CurrentPresShellState()->mPresShell
== aDirtyFrame
->PresShell(),
1400 "Presshell mismatch");
1401 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData
,
1402 "already traversed this presshell's root frame?");
1404 const DisplayItemClipChain
* clipChain
=
1405 CopyWholeChain(mClipState
.GetClipChainForContainingBlockDescendants());
1406 const DisplayItemClipChain
* combinedClipChain
=
1407 mClipState
.GetCurrentCombinedClipChain(this);
1408 const ActiveScrolledRoot
* asr
= mCurrentActiveScrolledRoot
;
1409 CurrentPresShellState()->mFixedBackgroundDisplayData
.emplace(
1410 clipChain
, combinedClipChain
, asr
, GetVisibleRect(), GetDirtyRect());
1415 * Mark all preserve-3d children with
1416 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1417 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1418 * dirty rect for preserve-3d children.
1420 * @param aDirtyFrame is the frame to mark children extending context.
1422 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1423 nsIFrame
* aDirtyFrame
) {
1424 for (const auto& childList
: aDirtyFrame
->ChildLists()) {
1425 for (nsIFrame
* child
: childList
.mList
) {
1426 if (child
->Combines3DTransformWithAncestors()) {
1427 MarkFrameForDisplay(child
, aDirtyFrame
);
1430 if (child
->IsBlockWrapper()) {
1431 // Mark preserve-3d frames inside the block wrapper.
1432 MarkPreserve3DFramesForDisplayList(child
);
1438 ActiveScrolledRoot
* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1439 const ActiveScrolledRoot
* aParent
, nsIScrollableFrame
* aScrollableFrame
) {
1440 RefPtr
<ActiveScrolledRoot
> asr
= ActiveScrolledRoot::CreateASRForFrame(
1441 aParent
, aScrollableFrame
, IsRetainingDisplayList());
1442 mActiveScrolledRoots
.AppendElement(asr
);
1446 const DisplayItemClipChain
* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1447 const DisplayItemClip
& aClip
, const ActiveScrolledRoot
* aASR
,
1448 const DisplayItemClipChain
* aParent
) {
1449 MOZ_ASSERT(!(aParent
&& aParent
->mOnStack
));
1450 void* p
= Allocate(sizeof(DisplayItemClipChain
),
1451 DisplayListArenaObjectId::CLIPCHAIN
);
1452 DisplayItemClipChain
* c
= new (KnownNotNull
, p
)
1453 DisplayItemClipChain(aClip
, aASR
, aParent
, mFirstClipChainToDestroy
);
1455 c
->mOnStack
= false;
1457 auto result
= mClipDeduplicator
.insert(c
);
1458 if (!result
.second
) {
1459 // An equivalent clip chain item was already created, so let's return that
1460 // instead. Destroy the one we just created.
1461 // Note that this can cause clip chains from different coordinate systems to
1462 // collapse into the same clip chain object, because clip chains do not keep
1463 // track of the reference frame that they were created in.
1464 c
->DisplayItemClipChain::~DisplayItemClipChain();
1465 Destroy(DisplayListArenaObjectId::CLIPCHAIN
, c
);
1466 return *(result
.first
);
1468 mFirstClipChainToDestroy
= c
;
1472 struct ClipChainItem
{
1473 DisplayItemClip clip
;
1474 const ActiveScrolledRoot
* asr
;
1477 const DisplayItemClipChain
* nsDisplayListBuilder::CreateClipChainIntersection(
1478 const DisplayItemClipChain
* aAncestor
,
1479 const DisplayItemClipChain
* aLeafClip1
,
1480 const DisplayItemClipChain
* aLeafClip2
) {
1481 AutoTArray
<ClipChainItem
, 8> intersectedClips
;
1483 const DisplayItemClipChain
* clip1
= aLeafClip1
;
1484 const DisplayItemClipChain
* clip2
= aLeafClip2
;
1486 const ActiveScrolledRoot
* asr
= ActiveScrolledRoot::PickDescendant(
1487 clip1
? clip1
->mASR
: nullptr, clip2
? clip2
->mASR
: nullptr);
1489 // Build up the intersection from the leaf to the root and put it into
1490 // intersectedClips. The loop below will convert intersectedClips into an
1491 // actual DisplayItemClipChain.
1492 // (We need to do this in two passes because we need the parent clip in order
1493 // to create the DisplayItemClipChain object, but the parent clip has not
1494 // been created at that point.)
1495 while (!aAncestor
|| asr
!= aAncestor
->mASR
) {
1496 if (clip1
&& clip1
->mASR
== asr
) {
1497 if (clip2
&& clip2
->mASR
== asr
) {
1498 DisplayItemClip intersection
= clip1
->mClip
;
1499 intersection
.IntersectWith(clip2
->mClip
);
1500 intersectedClips
.AppendElement(ClipChainItem
{intersection
, asr
});
1501 clip2
= clip2
->mParent
;
1503 intersectedClips
.AppendElement(ClipChainItem
{clip1
->mClip
, asr
});
1505 clip1
= clip1
->mParent
;
1506 } else if (clip2
&& clip2
->mASR
== asr
) {
1507 intersectedClips
.AppendElement(ClipChainItem
{clip2
->mClip
, asr
});
1508 clip2
= clip2
->mParent
;
1511 MOZ_ASSERT(!aAncestor
, "We should have exited this loop earlier");
1517 // Convert intersectedClips into a DisplayItemClipChain.
1518 const DisplayItemClipChain
* parentSC
= aAncestor
;
1519 for (auto& sc
: Reversed(intersectedClips
)) {
1520 parentSC
= AllocateDisplayItemClipChain(sc
.clip
, sc
.asr
, parentSC
);
1525 const DisplayItemClipChain
* nsDisplayListBuilder::CopyWholeChain(
1526 const DisplayItemClipChain
* aClipChain
) {
1527 return CreateClipChainIntersection(nullptr, aClipChain
, nullptr);
1530 const DisplayItemClipChain
* nsDisplayListBuilder::FuseClipChainUpTo(
1531 const DisplayItemClipChain
* aClipChain
, const ActiveScrolledRoot
* aASR
) {
1536 const DisplayItemClipChain
* sc
= aClipChain
;
1537 DisplayItemClip mergedClip
;
1538 while (sc
&& ActiveScrolledRoot::PickDescendant(aASR
, sc
->mASR
) == sc
->mASR
) {
1539 mergedClip
.IntersectWith(sc
->mClip
);
1543 if (!mergedClip
.HasClip()) {
1547 return AllocateDisplayItemClipChain(mergedClip
, aASR
, sc
);
1550 const nsIFrame
* nsDisplayListBuilder::FindReferenceFrameFor(
1551 const nsIFrame
* aFrame
, nsPoint
* aOffset
) const {
1552 auto MaybeApplyAdditionalOffset
= [&]() {
1553 if (AdditionalOffset()) {
1554 // The additional reference frame offset should only affect descendants
1555 // of |mAdditionalOffsetFrame|.
1556 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(mAdditionalOffsetFrame
,
1558 *aOffset
+= *AdditionalOffset();
1562 if (aFrame
== mCurrentFrame
) {
1564 *aOffset
= mCurrentOffsetToReferenceFrame
;
1566 return mCurrentReferenceFrame
;
1569 for (const nsIFrame
* f
= aFrame
; f
;
1570 f
= nsLayoutUtils::GetCrossDocParentFrame(f
)) {
1571 if (f
== mReferenceFrame
|| f
->IsTransformed()) {
1573 *aOffset
= aFrame
->GetOffsetToCrossDoc(f
);
1574 MaybeApplyAdditionalOffset();
1581 *aOffset
= aFrame
->GetOffsetToCrossDoc(mReferenceFrame
);
1584 return mReferenceFrame
;
1587 // Sticky frames are active if their nearest scrollable frame is also active.
1588 static bool IsStickyFrameActive(nsDisplayListBuilder
* aBuilder
,
1589 nsIFrame
* aFrame
, nsIFrame
* aParent
) {
1590 MOZ_ASSERT(aFrame
->StyleDisplay()->mPosition
==
1591 StylePositionProperty::Sticky
);
1593 // Find the nearest scrollframe.
1594 nsIScrollableFrame
* sf
= nsLayoutUtils::GetNearestScrollableFrame(
1595 aFrame
->GetParent(), nsLayoutUtils::SCROLLABLE_SAME_DOC
|
1596 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN
);
1601 return sf
->IsScrollingActive(aBuilder
);
1604 nsDisplayListBuilder::AGRState
nsDisplayListBuilder::IsAnimatedGeometryRoot(
1605 nsIFrame
* aFrame
, bool& aIsAsync
, nsIFrame
** aParent
) {
1606 // We can return once we know that this frame is an AGR, and we're either
1607 // async, or sure that none of the later conditions might make us async.
1608 // The exception to this is when IsPaintingToWindow() == false.
1610 if (aFrame
== mReferenceFrame
) {
1615 if (!IsPaintingToWindow()) {
1617 *aParent
= nsLayoutUtils::GetCrossDocParentFrame(aFrame
);
1622 nsIFrame
* parent
= nsLayoutUtils::GetCrossDocParentFrame(aFrame
);
1628 if (aFrame
->StyleDisplay()->mPosition
== StylePositionProperty::Sticky
&&
1629 IsStickyFrameActive(this, aFrame
, parent
)) {
1634 if (aFrame
->IsTransformed()) {
1635 aIsAsync
= EffectCompositor::HasAnimationsForCompositor(
1636 aFrame
, DisplayItemType::TYPE_TRANSFORM
);
1640 LayoutFrameType parentType
= parent
->Type();
1641 if (parentType
== LayoutFrameType::Scroll
||
1642 parentType
== LayoutFrameType::ListControl
) {
1643 nsIScrollableFrame
* sf
= do_QueryFrame(parent
);
1644 if (sf
->GetScrolledFrame() == aFrame
&& sf
->IsScrollingActive(this)) {
1645 MOZ_ASSERT(!aFrame
->IsTransformed());
1646 aIsAsync
= sf
->IsMaybeAsynchronouslyScrolled();
1651 // Treat the slider thumb as being as an active scrolled root when it wants
1652 // its own layer so that it can move without repainting.
1653 if (parentType
== LayoutFrameType::Slider
) {
1654 auto* sf
= static_cast<nsSliderFrame
*>(parent
)->GetScrollFrame();
1655 // The word "Maybe" in IsMaybeScrollingActive might be confusing but we do
1656 // indeed need to always consider scroll thumbs as AGRs if
1657 // IsMaybeScrollingActive is true because that is the same condition we use
1658 // in ScrollFrameHelper::AppendScrollPartsTo to layerize scroll thumbs.
1659 if (sf
&& sf
->IsMaybeScrollingActive()) {
1664 if (nsLayoutUtils::IsPopup(aFrame
)) {
1668 if (ActiveLayerTracker::IsOffsetStyleAnimated(aFrame
)) {
1669 const bool inBudget
= AddToAGRBudget(aFrame
);
1675 if (!aFrame
->GetParent() &&
1676 DisplayPortUtils::ViewportHasDisplayPort(aFrame
->PresContext())) {
1677 // Viewport frames in a display port need to be animated geometry roots
1678 // for background-attachment:fixed elements.
1682 // Fixed-pos frames are parented by the viewport frame, which has no parent.
1683 if (DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame
)) {
1694 nsIFrame
* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1695 nsIFrame
* aFrame
, bool& aIsAsync
) {
1697 nsLayoutUtils::IsAncestorFrameCrossDoc(RootReferenceFrame(), aFrame
));
1698 nsIFrame
* cursor
= aFrame
;
1699 while (cursor
!= RootReferenceFrame()) {
1701 if (IsAnimatedGeometryRoot(cursor
, aIsAsync
, &next
) == AGR_YES
) {
1706 // Root frame is always an async agr.
1711 void nsDisplayListBuilder::RecomputeCurrentAnimatedGeometryRoot() {
1713 if (*mCurrentAGR
!= mCurrentFrame
&&
1714 IsAnimatedGeometryRoot(const_cast<nsIFrame
*>(mCurrentFrame
), isAsync
) ==
1716 AnimatedGeometryRoot
* oldAGR
= mCurrentAGR
;
1717 mCurrentAGR
= WrapAGRForFrame(const_cast<nsIFrame
*>(mCurrentFrame
), isAsync
,
1720 // Iterate the AGR cache and look for any objects that reference the old AGR
1721 // and check to see if they need to be updated. AGRs can be in the cache
1722 // multiple times, so we may end up doing the work multiple times for AGRs
1723 // that don't change.
1724 for (const RefPtr
<AnimatedGeometryRoot
>& cached
:
1725 mFrameToAnimatedGeometryRootMap
.Values()) {
1726 if (cached
->mParentAGR
== oldAGR
&& cached
!= mCurrentAGR
) {
1727 // It's possible that this cached AGR struct that has the old AGR as a
1728 // parent should instead have mCurrentFrame has a parent.
1729 nsIFrame
* parent
= FindAnimatedGeometryRootFrameFor(*cached
, isAsync
);
1730 MOZ_ASSERT(parent
== mCurrentFrame
|| parent
== *oldAGR
);
1731 if (parent
== mCurrentFrame
) {
1732 cached
->mParentAGR
= mCurrentAGR
;
1739 static nsRect
ApplyAllClipNonRoundedIntersection(
1740 const DisplayItemClipChain
* aClipChain
, const nsRect
& aRect
) {
1741 nsRect result
= aRect
;
1742 while (aClipChain
) {
1743 result
= aClipChain
->mClip
.ApplyNonRoundedIntersection(result
);
1744 aClipChain
= aClipChain
->mParent
;
1749 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame
* aFrame
) {
1750 if (!mWindowDraggingAllowed
|| !IsForPainting()) {
1754 const nsStyleUIReset
* styleUI
= aFrame
->StyleUIReset();
1755 if (styleUI
->mWindowDragging
== StyleWindowDragging::Default
) {
1756 // This frame has the default value and doesn't influence the window
1761 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame
;
1763 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1764 nsIFrame
* referenceFrame
=
1765 const_cast<nsIFrame
*>(FindReferenceFrameFor(aFrame
));
1767 if (IsInTransform()) {
1768 // Only support 2d rectilinear transforms. Transform support is needed for
1769 // the horizontal flip transform that's applied to the urlbar textbox in
1770 // RTL mode - it should be able to exclude itself from the draggable region.
1771 referenceFrameToRootReferenceFrame
=
1772 ViewAs
<LayoutDeviceToLayoutDeviceMatrix4x4
>(
1773 nsLayoutUtils::GetTransformToAncestor(RelativeTo
{referenceFrame
},
1774 RelativeTo
{mReferenceFrame
})
1776 Matrix referenceFrameToRootReferenceFrame2d
;
1777 if (!referenceFrameToRootReferenceFrame
.Is2D(
1778 &referenceFrameToRootReferenceFrame2d
) ||
1779 !referenceFrameToRootReferenceFrame2d
.IsRectilinear()) {
1783 MOZ_ASSERT(referenceFrame
== mReferenceFrame
,
1784 "referenceFrameToRootReferenceFrame needs to be adjusted");
1787 // We do some basic visibility checking on the frame's border box here.
1788 // We intersect it both with the current dirty rect and with the current
1789 // clip. Either one is just a conservative approximation on its own, but
1790 // their intersection luckily works well enough for our purposes, so that
1791 // we don't have to do full-blown visibility computations.
1792 // The most important case we need to handle is the scrolled-off tab:
1793 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1794 // should not be allowed to interfere with the window dragging region. Using
1795 // just the current DisplayItemClip is not enough to cover this case
1796 // completely because clips are reset while building stacking context
1797 // contents, so for example we'd fail to clip frames that have a clip path
1798 // applied to them. But the current dirty rect doesn't get reset in that
1799 // case, so we use it to make this case work.
1800 nsRect borderBox
= aFrame
->GetRectRelativeToSelf().Intersect(mVisibleRect
);
1801 borderBox
+= ToReferenceFrame(aFrame
);
1802 const DisplayItemClipChain
* clip
=
1803 ClipState().GetCurrentCombinedClipChain(this);
1804 borderBox
= ApplyAllClipNonRoundedIntersection(clip
, borderBox
);
1805 if (borderBox
.IsEmpty()) {
1809 LayoutDeviceRect devPixelBorderBox
= LayoutDevicePixel::FromAppUnits(
1810 borderBox
, aFrame
->PresContext()->AppUnitsPerDevPixel());
1812 LayoutDeviceRect transformedDevPixelBorderBox
=
1813 TransformBy(referenceFrameToRootReferenceFrame
, devPixelBorderBox
);
1814 transformedDevPixelBorderBox
.Round();
1815 LayoutDeviceIntRect transformedDevPixelBorderBoxInt
;
1817 if (!transformedDevPixelBorderBox
.ToIntRect(
1818 &transformedDevPixelBorderBoxInt
)) {
1822 LayoutDeviceIntRegion
& region
=
1823 styleUI
->mWindowDragging
== StyleWindowDragging::Drag
1824 ? mWindowDraggingRegion
1825 : mWindowNoDraggingRegion
;
1827 if (!IsRetainingDisplayList()) {
1828 region
.OrWith(transformedDevPixelBorderBoxInt
);
1832 mozilla::gfx::IntRect
rect(transformedDevPixelBorderBoxInt
.ToUnknownRect());
1833 if (styleUI
->mWindowDragging
== StyleWindowDragging::Drag
) {
1834 mRetainedWindowDraggingRegion
.Add(aFrame
, rect
);
1836 mRetainedWindowNoDraggingRegion
.Add(aFrame
, rect
);
1840 LayoutDeviceIntRegion
nsDisplayListBuilder::GetWindowDraggingRegion() const {
1841 LayoutDeviceIntRegion result
;
1842 if (!IsRetainingDisplayList()) {
1843 result
.Sub(mWindowDraggingRegion
, mWindowNoDraggingRegion
);
1847 LayoutDeviceIntRegion dragRegion
=
1848 mRetainedWindowDraggingRegion
.ToLayoutDeviceIntRegion();
1850 LayoutDeviceIntRegion noDragRegion
=
1851 mRetainedWindowNoDraggingRegion
.ToLayoutDeviceIntRegion();
1853 result
.Sub(dragRegion
, noDragRegion
);
1857 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
) const {
1858 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes
);
1859 aSizes
.mLayoutRetainedDisplayListSize
+=
1860 aSizes
.mState
.mMallocSizeOf(mTransformPreserves3D
.get());
1863 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
) const {
1864 mPool
.AddSizeOfExcludingThis(aSizes
, Arena::ArenaKind::DisplayList
);
1867 MallocSizeOf mallocSizeOf
= aSizes
.mState
.mMallocSizeOf
;
1868 n
+= mDocumentWillChangeBudgets
.ShallowSizeOfExcludingThis(mallocSizeOf
);
1869 n
+= mFrameWillChangeBudgets
.ShallowSizeOfExcludingThis(mallocSizeOf
);
1870 n
+= mAGRBudgetSet
.ShallowSizeOfExcludingThis(mallocSizeOf
);
1871 n
+= mEffectsUpdates
.ShallowSizeOfExcludingThis(mallocSizeOf
);
1872 n
+= mWindowExcludeGlassRegion
.SizeOfExcludingThis(mallocSizeOf
);
1873 n
+= mRetainedWindowDraggingRegion
.SizeOfExcludingThis(mallocSizeOf
);
1874 n
+= mRetainedWindowNoDraggingRegion
.SizeOfExcludingThis(mallocSizeOf
);
1875 n
+= mRetainedWindowOpaqueRegion
.SizeOfExcludingThis(mallocSizeOf
);
1876 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1878 aSizes
.mLayoutRetainedDisplayListSize
+= n
;
1881 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
) const {
1882 for (nsDisplayItem
* item
: *this) {
1883 item
->AddSizeOfExcludingThis(aSizes
);
1884 if (RetainedDisplayList
* children
= item
->GetChildren()) {
1885 children
->AddSizeOfExcludingThis(aSizes
);
1891 n
+= mDAG
.mDirectPredecessorList
.ShallowSizeOfExcludingThis(
1892 aSizes
.mState
.mMallocSizeOf
);
1893 n
+= mDAG
.mNodesInfo
.ShallowSizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
1894 n
+= mOldItems
.ShallowSizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
1896 aSizes
.mLayoutRetainedDisplayListSize
+= n
;
1899 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1900 MallocSizeOf aMallocSizeOf
) const {
1902 n
+= mFrames
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
1903 for (auto& frame
: mFrames
) {
1904 const UniquePtr
<WeakFrame
>& weakFrame
= frame
.mWeakFrame
;
1905 n
+= aMallocSizeOf(weakFrame
.get());
1907 n
+= mRects
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
1912 * Removes modified frames and rects from this WeakFrameRegion.
1914 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1915 MOZ_ASSERT(mFrames
.Length() == mRects
.Length());
1918 uint32_t length
= mFrames
.Length();
1920 while (i
< length
) {
1921 auto& wrapper
= mFrames
[i
];
1923 if (!wrapper
.mWeakFrame
->IsAlive() ||
1924 AnyContentAncestorModified(wrapper
.mWeakFrame
->GetFrame())) {
1925 // To avoid multiple O(n) shifts in the array, move the last element of
1926 // the array to the current position and decrease the array length.
1927 mFrameSet
.Remove(wrapper
.mFrame
);
1928 mFrames
[i
] = std::move(mFrames
[length
- 1]);
1929 mRects
[i
] = std::move(mRects
[length
- 1]);
1936 mFrames
.TruncateLength(length
);
1937 mRects
.TruncateLength(length
);
1940 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1941 mRetainedWindowDraggingRegion
.RemoveModifiedFramesAndRects();
1942 mRetainedWindowNoDraggingRegion
.RemoveModifiedFramesAndRects();
1943 mWindowExcludeGlassRegion
.RemoveModifiedFramesAndRects();
1944 mRetainedWindowOpaqueRegion
.RemoveModifiedFramesAndRects();
1946 mHasGlassItemDuringPartial
= false;
1949 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1950 mRetainedWindowDraggingRegion
.Clear();
1951 mRetainedWindowNoDraggingRegion
.Clear();
1952 mWindowExcludeGlassRegion
.Clear();
1953 mRetainedWindowOpaqueRegion
.Clear();
1955 mGlassDisplayItem
= nullptr;
1958 const uint32_t gWillChangeAreaMultiplier
= 3;
1959 static uint32_t GetLayerizationCost(const nsSize
& aSize
) {
1960 // There's significant overhead for each layer created from Gecko
1961 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1962 // Therefore we set a minimum cost threshold of a 64x64 area.
1963 const int minBudgetCost
= 64 * 64;
1965 const uint32_t budgetCost
= std::max(
1966 minBudgetCost
, nsPresContext::AppUnitsToIntCSSPixels(aSize
.width
) *
1967 nsPresContext::AppUnitsToIntCSSPixels(aSize
.height
));
1972 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame
* aFrame
,
1973 const nsSize
& aSize
) {
1974 MOZ_ASSERT(IsForPainting());
1976 if (aFrame
->MayHaveWillChangeBudget()) {
1977 // The frame is already in the will-change budget.
1981 const nsPresContext
* presContext
= aFrame
->PresContext();
1982 const nsRect area
= presContext
->GetVisibleArea();
1983 const uint32_t budgetLimit
=
1984 nsPresContext::AppUnitsToIntCSSPixels(area
.width
) *
1985 nsPresContext::AppUnitsToIntCSSPixels(area
.height
);
1986 const uint32_t cost
= GetLayerizationCost(aSize
);
1988 DocumentWillChangeBudget
& documentBudget
=
1989 mDocumentWillChangeBudgets
.LookupOrInsert(presContext
);
1991 const bool onBudget
=
1992 (documentBudget
+ cost
) / gWillChangeAreaMultiplier
< budgetLimit
;
1995 documentBudget
+= cost
;
1996 mFrameWillChangeBudgets
.InsertOrUpdate(
1997 aFrame
, FrameWillChangeBudget(presContext
, cost
));
1998 aFrame
->SetMayHaveWillChangeBudget(true);
2004 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame
* aFrame
,
2005 const nsSize
& aSize
) {
2006 if (!IsForPainting()) {
2007 // If this nsDisplayListBuilder is not for painting, the layerization should
2008 // not matter. Do the simple thing and return false.
2012 const bool onBudget
= AddToWillChangeBudget(aFrame
, aSize
);
2017 auto* pc
= aFrame
->PresContext();
2018 auto* doc
= pc
->Document();
2019 if (!doc
->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget
)) {
2020 AutoTArray
<nsString
, 2> params
;
2021 params
.AppendElement()->AppendInt(gWillChangeAreaMultiplier
);
2023 nsRect area
= pc
->GetVisibleArea();
2024 uint32_t budgetLimit
= nsPresContext::AppUnitsToIntCSSPixels(area
.width
) *
2025 nsPresContext::AppUnitsToIntCSSPixels(area
.height
);
2026 params
.AppendElement()->AppendInt(budgetLimit
);
2028 doc
->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget
, false, params
);
2034 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame
* aFrame
) {
2035 MOZ_ASSERT(IsForPainting());
2037 if (!aFrame
->MayHaveWillChangeBudget()) {
2041 aFrame
->SetMayHaveWillChangeBudget(false);
2042 RemoveFromWillChangeBudgets(aFrame
);
2045 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame
* aFrame
) {
2046 if (auto entry
= mFrameWillChangeBudgets
.Lookup(aFrame
)) {
2047 const FrameWillChangeBudget
& frameBudget
= entry
.Data();
2049 auto documentBudget
=
2050 mDocumentWillChangeBudgets
.Lookup(frameBudget
.mPresContext
);
2052 if (documentBudget
) {
2053 *documentBudget
-= frameBudget
.mUsage
;
2060 void nsDisplayListBuilder::ClearWillChangeBudgets() {
2061 mFrameWillChangeBudgets
.Clear();
2062 mDocumentWillChangeBudgets
.Clear();
2065 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
2066 const float gAGRBudgetAreaMultiplier
= 0.3;
2068 const float gAGRBudgetAreaMultiplier
= 3.0;
2071 bool nsDisplayListBuilder::AddToAGRBudget(nsIFrame
* aFrame
) {
2072 if (mAGRBudgetSet
.Contains(aFrame
)) {
2076 const nsPresContext
* presContext
=
2077 aFrame
->PresContext()->GetRootPresContext();
2082 const nsRect area
= presContext
->GetVisibleArea();
2083 const uint32_t budgetLimit
=
2084 gAGRBudgetAreaMultiplier
*
2085 nsPresContext::AppUnitsToIntCSSPixels(area
.width
) *
2086 nsPresContext::AppUnitsToIntCSSPixels(area
.height
);
2088 const uint32_t cost
= GetLayerizationCost(aFrame
->GetSize());
2089 const bool onBudget
= mUsedAGRBudget
+ cost
< budgetLimit
;
2092 mUsedAGRBudget
+= cost
;
2093 mAGRBudgetSet
.Insert(aFrame
);
2099 void nsDisplayListBuilder::EnterSVGEffectsContents(
2100 nsIFrame
* aEffectsFrame
, nsDisplayList
* aHoistedItemsStorage
) {
2101 MOZ_ASSERT(aHoistedItemsStorage
);
2102 if (mSVGEffectsFrames
.IsEmpty()) {
2103 MOZ_ASSERT(!mScrollInfoItemsForHoisting
);
2104 mScrollInfoItemsForHoisting
= aHoistedItemsStorage
;
2106 mSVGEffectsFrames
.AppendElement(aEffectsFrame
);
2109 void nsDisplayListBuilder::ExitSVGEffectsContents() {
2110 MOZ_ASSERT(!mSVGEffectsFrames
.IsEmpty());
2111 mSVGEffectsFrames
.RemoveLastElement();
2112 MOZ_ASSERT(mScrollInfoItemsForHoisting
);
2113 if (mSVGEffectsFrames
.IsEmpty()) {
2114 mScrollInfoItemsForHoisting
= nullptr;
2118 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
2120 * Note: if changing the conditions under which scroll info layers
2121 * are created, make a corresponding change to
2122 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
2124 for (nsIFrame
* frame
: mSVGEffectsFrames
) {
2125 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame
)) {
2132 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
2133 nsDisplayScrollInfoLayer
* aScrollInfoItem
) {
2134 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2135 MOZ_ASSERT(mScrollInfoItemsForHoisting
);
2136 mScrollInfoItemsForHoisting
->AppendToTop(aScrollInfoItem
);
2139 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2140 nsIFrame
* aFrame
, nsDisplayList
* aList
) {
2144 if (!BuildCompositorHitTestInfo()) {
2148 const CompositorHitTestInfo info
= aFrame
->GetCompositorHitTestInfo(this);
2149 if (info
!= CompositorHitTestInvisibleToHit
) {
2150 aList
->AppendNewToTop
<nsDisplayCompositorHitTestInfo
>(this, aFrame
);
2154 void nsDisplayListSet::MoveTo(const nsDisplayListSet
& aDestination
) const {
2155 aDestination
.BorderBackground()->AppendToTop(BorderBackground());
2156 aDestination
.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2157 aDestination
.Floats()->AppendToTop(Floats());
2158 aDestination
.Content()->AppendToTop(Content());
2159 aDestination
.PositionedDescendants()->AppendToTop(PositionedDescendants());
2160 aDestination
.Outlines()->AppendToTop(Outlines());
2163 static void MoveListTo(nsDisplayList
* aList
,
2164 nsTArray
<nsDisplayItem
*>* aElements
) {
2165 nsDisplayItem
* item
;
2166 while ((item
= aList
->RemoveBottom()) != nullptr) {
2167 aElements
->AppendElement(item
);
2171 nsRect
nsDisplayList::GetClippedBounds(nsDisplayListBuilder
* aBuilder
) const {
2173 for (nsDisplayItem
* i
: *this) {
2174 bounds
.UnionRect(bounds
, i
->GetClippedBounds(aBuilder
));
2179 nsRect
nsDisplayList::GetClippedBoundsWithRespectToASR(
2180 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
,
2181 nsRect
* aBuildingRect
) const {
2183 for (nsDisplayItem
* i
: *this) {
2184 nsRect r
= i
->GetClippedBounds(aBuilder
);
2185 if (aASR
!= i
->GetActiveScrolledRoot() && !r
.IsEmpty()) {
2186 if (Maybe
<nsRect
> clip
= i
->GetClipWithRespectToASR(aBuilder
, aASR
)) {
2190 if (aBuildingRect
) {
2191 aBuildingRect
->UnionRect(*aBuildingRect
, i
->GetBuildingRect());
2193 bounds
.UnionRect(bounds
, r
);
2198 nsRect
nsDisplayList::GetBuildingRect() const {
2200 for (nsDisplayItem
* i
: *this) {
2201 result
.UnionRect(result
, i
->GetBuildingRect());
2206 bool nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder
* aBuilder
,
2207 nsRegion
* aVisibleRegion
) {
2208 AUTO_PROFILER_LABEL("nsDisplayList::ComputeVisibilityForRoot", GRAPHICS
);
2211 const ActiveScrolledRoot
* rootASR
= nullptr;
2212 r
.And(*aVisibleRegion
, GetClippedBoundsWithRespectToASR(aBuilder
, rootASR
));
2213 return ComputeVisibilityForSublist(aBuilder
, aVisibleRegion
, r
.GetBounds());
2216 static nsRegion
TreatAsOpaque(nsDisplayItem
* aItem
,
2217 nsDisplayListBuilder
* aBuilder
) {
2219 nsRegion opaque
= aItem
->GetOpaqueRegion(aBuilder
, &snap
);
2221 (aBuilder
->IsForEventDelivery() && aBuilder
->HitTestIsForVisibility()) ||
2222 !opaque
.IsComplex());
2223 if (opaque
.IsEmpty()) {
2226 nsRegion opaqueClipped
;
2227 for (auto iter
= opaque
.RectIter(); !iter
.Done(); iter
.Next()) {
2228 opaqueClipped
.Or(opaqueClipped
,
2229 aItem
->GetClip().ApproximateIntersectInward(iter
.Get()));
2231 return opaqueClipped
;
2234 bool nsDisplayList::ComputeVisibilityForSublist(
2235 nsDisplayListBuilder
* aBuilder
, nsRegion
* aVisibleRegion
,
2236 const nsRect
& aListVisibleBounds
) {
2239 r
.And(*aVisibleRegion
, GetClippedBounds(aBuilder
));
2240 // XXX this fails sometimes:
2241 NS_WARNING_ASSERTION(r
.GetBounds().IsEqualInterior(aListVisibleBounds
),
2242 "bad aListVisibleBounds");
2245 bool anyVisible
= false;
2247 AutoTArray
<nsDisplayItem
*, 512> elements
;
2248 MoveListTo(this, &elements
);
2250 for (int32_t i
= elements
.Length() - 1; i
>= 0; --i
) {
2251 nsDisplayItem
* item
= elements
[i
];
2253 if (item
->ForceNotVisible() && !item
->GetSameCoordinateSystemChildren()) {
2254 NS_ASSERTION(item
->GetBuildingRect().IsEmpty(),
2255 "invisible items should have empty vis rect");
2256 item
->SetPaintRect(nsRect());
2258 nsRect bounds
= item
->GetClippedBounds(aBuilder
);
2260 nsRegion itemVisible
;
2261 itemVisible
.And(*aVisibleRegion
, bounds
);
2262 item
->SetPaintRect(itemVisible
.GetBounds());
2265 if (item
->ComputeVisibility(aBuilder
, aVisibleRegion
)) {
2268 nsRegion opaque
= TreatAsOpaque(item
, aBuilder
);
2269 // Subtract opaque item from the visible region
2270 aBuilder
->SubtractFromVisibleRegion(aVisibleRegion
, opaque
);
2272 AppendToBottom(item
);
2275 mIsOpaque
= !aVisibleRegion
->Intersects(aListVisibleBounds
);
2279 static void TriggerPendingAnimations(Document
& aDoc
,
2280 const TimeStamp
& aReadyTime
) {
2281 MOZ_ASSERT(!aReadyTime
.IsNull(),
2282 "Animation ready time is not set. Perhaps we're using a layer"
2283 " manager that doesn't update it");
2284 if (PendingAnimationTracker
* tracker
= aDoc
.GetPendingAnimationTracker()) {
2285 PresShell
* presShell
= aDoc
.GetPresShell();
2286 // If paint-suppression is in effect then we haven't finished painting
2287 // this document yet so we shouldn't start animations
2288 if (!presShell
|| !presShell
->IsPaintingSuppressed()) {
2289 tracker
->TriggerPendingAnimationsOnNextTick(aReadyTime
);
2292 auto recurse
= [&aReadyTime
](Document
& aDoc
) {
2293 TriggerPendingAnimations(aDoc
, aReadyTime
);
2294 return CallState::Continue
;
2296 aDoc
.EnumerateSubDocuments(recurse
);
2299 LayerManager
* nsDisplayListBuilder::GetWidgetLayerManager(nsView
** aView
) {
2301 *aView
= RootReferenceFrame()->GetView();
2303 if (RootReferenceFrame() !=
2304 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2307 nsIWidget
* window
= RootReferenceFrame()->GetNearestWidget();
2309 return window
->GetLayerManager();
2314 // Find the layer which should house the root scroll metadata for a given
2315 // layer tree. This is the async zoom container layer if there is one,
2316 // otherwise it's the root layer.
2317 Layer
* GetLayerForRootMetadata(Layer
* aRootLayer
, ViewID aRootScrollId
) {
2318 Layer
* asyncZoomContainer
= DepthFirstSearch
<ForwardIterator
>(
2319 aRootLayer
, [aRootScrollId
](Layer
* aLayer
) {
2320 if (auto id
= aLayer
->GetAsyncZoomContainerId()) {
2321 return *id
== aRootScrollId
;
2325 return asyncZoomContainer
? asyncZoomContainer
: aRootLayer
;
2328 FrameLayerBuilder
* nsDisplayList::BuildLayers(nsDisplayListBuilder
* aBuilder
,
2329 LayerManager
* aLayerManager
,
2331 bool aIsWidgetTransaction
) {
2332 nsIFrame
* frame
= aBuilder
->RootReferenceFrame();
2333 nsPresContext
* presContext
= frame
->PresContext();
2334 PresShell
* presShell
= presContext
->PresShell();
2336 FrameLayerBuilder
* layerBuilder
= new FrameLayerBuilder();
2337 layerBuilder
->Init(aBuilder
, aLayerManager
);
2339 if (aFlags
& PAINT_COMPRESSED
) {
2340 layerBuilder
->SetLayerTreeCompressionMode();
2343 RefPtr
<ContainerLayer
> root
;
2345 AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_LayerBuilding
);
2346 AUTO_PROFILER_TRACING_MARKER_DOCSHELL("Paint", "LayerBuilding", GRAPHICS
,
2347 presContext
->GetDocShell());
2349 if (XRE_IsContentProcess() && StaticPrefs::gfx_content_always_paint()) {
2350 FrameLayerBuilder::InvalidateAllLayers(aLayerManager
);
2353 if (aIsWidgetTransaction
) {
2354 layerBuilder
->DidBeginRetainedLayerTransaction(aLayerManager
);
2357 // Clear any ScrollMetadata that may have been set on the root layer on a
2358 // previous paint. This paint will set new metrics if necessary, and if we
2359 // don't clear the old one here, we may be left with extra metrics.
2360 if (Layer
* rootLayer
= aLayerManager
->GetRoot()) {
2361 rootLayer
->SetScrollMetadata(nsTArray
<ScrollMetadata
>());
2364 float resolutionUniform
= 1.0f
;
2365 float resolutionX
= resolutionUniform
;
2366 float resolutionY
= resolutionUniform
;
2368 // If we are in a remote browser, then apply scaling from ancestor browsers
2369 if (BrowserChild
* browserChild
= BrowserChild::GetFrom(presShell
)) {
2370 if (!browserChild
->IsTopLevel()) {
2371 resolutionX
*= browserChild
->GetEffectsInfo().mScaleX
;
2372 resolutionY
*= browserChild
->GetEffectsInfo().mScaleY
;
2376 ContainerLayerParameters
containerParameters(resolutionX
, resolutionY
);
2379 const auto start
= TimeStamp::Now();
2381 root
= layerBuilder
->BuildContainerLayerFor(aBuilder
, aLayerManager
,
2382 frame
, nullptr, this,
2383 containerParameters
, nullptr);
2385 aBuilder
->NotifyAndClearScrollFrames();
2387 if (StaticPrefs::layers_acceleration_draw_fps()) {
2388 if (PaintTiming
* pt
=
2389 ClientLayerManager::MaybeGetPaintTiming(aLayerManager
)) {
2390 pt
->flbMs() = (TimeStamp::Now() - start
).ToMilliseconds();
2398 // Root is being scaled up by the X/Y resolution. Scale it back down.
2399 root
->SetPostScale(1.0f
/ resolutionX
, 1.0f
/ resolutionY
);
2401 auto callback
= [root
](ScrollableLayerGuid::ViewID aScrollId
) -> bool {
2402 return nsLayoutUtils::ContainsMetricsWithId(root
, aScrollId
);
2404 if (Maybe
<ScrollMetadata
> rootMetadata
= nsLayoutUtils::GetRootMetadata(
2405 aBuilder
, root
->Manager(), containerParameters
, callback
)) {
2406 GetLayerForRootMetadata(root
, rootMetadata
->GetMetrics().GetScrollId())
2407 ->SetScrollMetadata(rootMetadata
.value());
2410 // NS_WARNING is debug-only, so don't even bother checking the conditions
2411 // in a release build.
2413 bool usingDisplayport
= false;
2414 if (nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame()) {
2415 nsIContent
* content
= rootScrollFrame
->GetContent();
2417 usingDisplayport
= DisplayPortUtils::HasDisplayPort(content
);
2420 if (usingDisplayport
&&
2421 !(root
->GetContentFlags() & Layer::CONTENT_OPAQUE
) &&
2422 SpammyLayoutWarningsEnabled()) {
2423 // See bug 693938, attachment 567017
2424 NS_WARNING("Transparent content with displayports can be expensive.");
2428 aLayerManager
->SetRoot(root
);
2429 layerBuilder
->WillEndTransaction();
2431 return layerBuilder
;
2434 void nsDisplayList::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
2435 int32_t aAppUnitsPerDevPixel
) {
2436 FlattenedDisplayListIterator
iter(aBuilder
, this);
2437 while (iter
.HasNext()) {
2438 nsPaintedDisplayItem
* item
= iter
.GetNextItem()->AsPaintedDisplayItem();
2443 nsRegion
visible(item
->GetClippedBounds(aBuilder
));
2444 visible
.And(visible
, item
->GetBuildingRect());
2445 item
->SetPaintRect(visible
.GetBounds());
2446 if (!item
->ComputeVisibility(aBuilder
, &visible
)) {
2450 DisplayItemClip currentClip
= item
->GetClip();
2451 if (currentClip
.HasClip()) {
2453 currentClip
.ApplyTo(aCtx
, aAppUnitsPerDevPixel
);
2457 item
->Paint(aBuilder
, aCtx
);
2459 if (currentClip
.HasClip()) {
2466 * We paint by executing a layer manager transaction, constructing a
2467 * single layer representing the display list, and then making it the
2468 * root of the layer manager, drawing into the PaintedLayers.
2470 already_AddRefed
<LayerManager
> nsDisplayList::PaintRoot(
2471 nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
, uint32_t aFlags
,
2472 mozilla::Maybe
<double> aDisplayListBuildTime
) {
2473 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS
);
2475 RefPtr
<LayerManager
> layerManager
;
2476 bool widgetTransaction
= false;
2477 bool doBeginTransaction
= true;
2478 nsView
* view
= nullptr;
2479 if (aFlags
& PAINT_USE_WIDGET_LAYERS
) {
2480 layerManager
= aBuilder
->GetWidgetLayerManager(&view
);
2482 layerManager
->SetContainsSVG(false);
2484 doBeginTransaction
= !(aFlags
& PAINT_EXISTING_TRANSACTION
);
2485 widgetTransaction
= true;
2489 nsIFrame
* frame
= aBuilder
->RootReferenceFrame();
2490 nsPresContext
* presContext
= frame
->PresContext();
2491 PresShell
* presShell
= presContext
->PresShell();
2492 Document
* document
= presShell
->GetDocument();
2494 if (!layerManager
) {
2496 NS_WARNING("Nowhere to paint into");
2499 bool prevIsCompositingCheap
= aBuilder
->SetIsCompositingCheap(false);
2500 Paint(aBuilder
, aCtx
, presContext
->AppUnitsPerDevPixel());
2502 if (document
&& document
->IsBeingUsedAsImage()) {
2503 frame
->ClearInvalidationStateBits();
2506 aBuilder
->SetIsCompositingCheap(prevIsCompositingCheap
);
2510 if (layerManager
->GetBackendType() == layers::LayersBackend::LAYERS_WR
) {
2511 if (doBeginTransaction
) {
2513 if (!layerManager
->BeginTransactionWithTarget(aCtx
)) {
2517 if (!layerManager
->BeginTransaction()) {
2523 bool prevIsCompositingCheap
=
2524 aBuilder
->SetIsCompositingCheap(layerManager
->IsCompositingCheap());
2525 MaybeSetupTransactionIdAllocator(layerManager
, presContext
);
2528 if (aFlags
& PAINT_IDENTICAL_DISPLAY_LIST
) {
2529 sent
= layerManager
->EndEmptyTransaction();
2533 auto* wrManager
= static_cast<WebRenderLayerManager
*>(layerManager
.get());
2535 nsIDocShell
* docShell
= presContext
->GetDocShell();
2536 WrFiltersHolder wrFilters
;
2537 gfx::Matrix5x4
* colorMatrix
=
2538 nsDocShell::Cast(docShell
)->GetColorMatrix();
2540 wrFilters
.filters
.AppendElement(
2541 wr::FilterOp::ColorMatrix(colorMatrix
->components
));
2544 wrManager
->EndTransactionWithoutLayer(this, aBuilder
,
2545 std::move(wrFilters
), nullptr,
2546 aDisplayListBuildTime
.valueOr(0.0));
2549 // For layers-free mode, we check the invalidation state bits in the
2550 // EndTransaction. So we clear the invalidation state bits after
2552 if (widgetTransaction
||
2553 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2554 // but they still need the invalidation state bits cleared in order for
2555 // invalidation for CSS/SMIL animation to work properly.
2556 (document
&& document
->IsBeingUsedAsImage())) {
2557 frame
->ClearInvalidationStateBits();
2560 aBuilder
->SetIsCompositingCheap(prevIsCompositingCheap
);
2561 if (document
&& widgetTransaction
) {
2562 TriggerPendingAnimations(*document
,
2563 layerManager
->GetAnimationReadyTime());
2566 if (presContext
->RefreshDriver()->HasScheduleFlush()) {
2567 presContext
->NotifyInvalidation(layerManager
->GetLastTransactionId(),
2571 return layerManager
.forget();
2574 NotifySubDocInvalidationFunc computeInvalidFunc
=
2575 presContext
->MayHavePaintEventListenerInSubDocument()
2576 ? nsPresContext::NotifySubDocInvalidation
2579 UniquePtr
<LayerProperties
> props
;
2581 bool computeInvalidRect
=
2582 (computeInvalidFunc
|| (!layerManager
->IsCompositingCheap() &&
2583 layerManager
->NeedsWidgetInvalidation())) &&
2586 if (computeInvalidRect
) {
2587 props
= LayerProperties::CloneFrom(layerManager
->GetRoot());
2590 if (doBeginTransaction
) {
2592 if (!layerManager
->BeginTransactionWithTarget(aCtx
)) {
2596 if (!layerManager
->BeginTransaction()) {
2603 aBuilder
->SetIsCompositingCheap(layerManager
->IsCompositingCheap());
2604 LayerManager::EndTransactionFlags flags
= LayerManager::END_DEFAULT
;
2605 if (layerManager
->NeedsWidgetInvalidation() &&
2606 (aFlags
& PAINT_NO_COMPOSITE
)) {
2607 flags
= LayerManager::END_NO_COMPOSITE
;
2610 MaybeSetupTransactionIdAllocator(layerManager
, presContext
);
2613 if (aFlags
& PAINT_IDENTICAL_DISPLAY_LIST
) {
2614 sent
= layerManager
->EndEmptyTransaction(flags
);
2618 const auto start
= TimeStamp::Now();
2620 FrameLayerBuilder
* layerBuilder
=
2621 BuildLayers(aBuilder
, layerManager
, aFlags
, widgetTransaction
);
2623 Telemetry::AccumulateTimeDelta(Telemetry::PAINT_BUILD_LAYERS_TIME
, start
);
2625 if (!layerBuilder
) {
2626 layerManager
->SetUserData(&gLayerManagerLayerBuilder
, nullptr);
2630 layerManager
->EndTransaction(FrameLayerBuilder::DrawPaintedLayer
, aBuilder
,
2632 layerBuilder
->DidEndTransaction();
2635 if (widgetTransaction
||
2636 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2637 // but they still need the invalidation state bits cleared in order for
2638 // invalidation for CSS/SMIL animation to work properly.
2639 (document
&& document
->IsBeingUsedAsImage())) {
2640 frame
->ClearInvalidationStateBits();
2643 aBuilder
->SetIsCompositingCheap(temp
);
2645 if (document
&& widgetTransaction
) {
2646 TriggerPendingAnimations(*document
, layerManager
->GetAnimationReadyTime());
2649 nsIntRegion invalid
;
2651 if (!props
->ComputeDifferences(layerManager
->GetRoot(), invalid
,
2652 computeInvalidFunc
)) {
2653 invalid
= nsIntRect::MaxIntRect();
2655 } else if (widgetTransaction
) {
2656 LayerProperties::ClearInvalidations(layerManager
->GetRoot());
2659 bool shouldInvalidate
= layerManager
->NeedsWidgetInvalidation();
2663 if (!invalid
.IsEmpty()) {
2664 nsIntRect bounds
= invalid
.GetBounds();
2665 nsRect
rect(presContext
->DevPixelsToAppUnits(bounds
.x
),
2666 presContext
->DevPixelsToAppUnits(bounds
.y
),
2667 presContext
->DevPixelsToAppUnits(bounds
.width
),
2668 presContext
->DevPixelsToAppUnits(bounds
.height
));
2669 // Treat the invalid region from the layer manager as being relative to
2670 // the widget, rather than relative to the view. (It's unclear whether
2671 // this is the right thing to do, but it matches some aspects of
2672 // painting more than the alternative would. Moreover, non-zero view to
2673 // widget offsets only occur for fractionally-positioned popups, for bad
2674 // reasons. See bug 1688899 for details.)
2675 // Make the rectangle relative to the view.
2676 rect
-= view
->ViewToWidgetOffset();
2677 if (shouldInvalidate
) {
2678 view
->GetViewManager()->InvalidateViewNoSuppression(view
, rect
);
2680 presContext
->NotifyInvalidation(layerManager
->GetLastTransactionId(),
2683 } else if (shouldInvalidate
) {
2684 view
->GetViewManager()->InvalidateView(view
);
2688 layerManager
->SetUserData(&gLayerManagerLayerBuilder
, nullptr);
2689 return layerManager
.forget();
2692 nsDisplayItem
* nsDisplayList::RemoveBottom() {
2693 nsDisplayItem
* item
= mSentinel
.mAbove
;
2697 mSentinel
.mAbove
= item
->mAbove
;
2699 // must have been the only item
2702 item
->mAbove
= nullptr;
2707 void nsDisplayList::DeleteAll(nsDisplayListBuilder
* aBuilder
) {
2708 nsDisplayItem
* item
;
2709 while ((item
= RemoveBottom()) != nullptr) {
2710 item
->Destroy(aBuilder
);
2714 static bool IsFrameReceivingPointerEvents(nsIFrame
* aFrame
) {
2715 return StylePointerEvents::None
!=
2716 aFrame
->StyleUI()->GetEffectivePointerEvents(aFrame
);
2719 // A list of frames, and their z depth. Used for sorting
2720 // the results of hit testing.
2721 struct FramesWithDepth
{
2722 explicit FramesWithDepth(float aDepth
) : mDepth(aDepth
) {}
2724 bool operator<(const FramesWithDepth
& aOther
) const {
2725 if (!FuzzyEqual(mDepth
, aOther
.mDepth
, 0.1f
)) {
2726 // We want to sort so that the shallowest item (highest depth value) is
2728 return mDepth
> aOther
.mDepth
;
2730 return this < &aOther
;
2732 bool operator==(const FramesWithDepth
& aOther
) const {
2733 return this == &aOther
;
2737 nsTArray
<nsIFrame
*> mFrames
;
2740 // Sort the frames by depth and then moves all the contained frames to the
2742 static void FlushFramesArray(nsTArray
<FramesWithDepth
>& aSource
,
2743 nsTArray
<nsIFrame
*>* aDest
) {
2744 if (aSource
.IsEmpty()) {
2748 uint32_t length
= aSource
.Length();
2749 for (uint32_t i
= 0; i
< length
; i
++) {
2750 aDest
->AppendElements(std::move(aSource
[i
].mFrames
));
2755 void nsDisplayList::HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
2756 nsDisplayItem::HitTestState
* aState
,
2757 nsTArray
<nsIFrame
*>* aOutFrames
) const {
2758 nsDisplayItem
* item
;
2760 if (aState
->mInPreserves3D
) {
2761 // Collect leaves of the current 3D rendering context.
2762 for (nsDisplayItem
* item
: *this) {
2763 auto itemType
= item
->GetType();
2764 if (itemType
!= DisplayItemType::TYPE_TRANSFORM
||
2765 !static_cast<nsDisplayTransform
*>(item
)->IsLeafOf3DContext()) {
2766 item
->HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
2768 // One of leaves in the current 3D rendering context.
2769 aState
->mItemBuffer
.AppendElement(item
);
2775 int32_t itemBufferStart
= aState
->mItemBuffer
.Length();
2776 for (nsDisplayItem
* item
: *this) {
2777 aState
->mItemBuffer
.AppendElement(item
);
2780 AutoTArray
<FramesWithDepth
, 16> temp
;
2781 for (int32_t i
= aState
->mItemBuffer
.Length() - 1; i
>= itemBufferStart
;
2783 // Pop element off the end of the buffer. We want to shorten the buffer
2784 // so that recursive calls to HitTest have more buffer space.
2785 item
= aState
->mItemBuffer
[i
];
2786 aState
->mItemBuffer
.SetLength(i
);
2789 nsRect r
= item
->GetBounds(aBuilder
, &snap
).Intersect(aRect
);
2790 auto itemType
= item
->GetType();
2791 bool same3DContext
=
2792 (itemType
== DisplayItemType::TYPE_TRANSFORM
&&
2793 static_cast<nsDisplayTransform
*>(item
)->IsParticipating3DContext()) ||
2794 (itemType
== DisplayItemType::TYPE_PERSPECTIVE
&&
2795 item
->Frame()->Extend3DContext());
2796 if (same3DContext
&&
2797 (itemType
!= DisplayItemType::TYPE_TRANSFORM
||
2798 !static_cast<nsDisplayTransform
*>(item
)->IsLeafOf3DContext())) {
2799 if (!item
->GetClip().MayIntersect(aRect
)) {
2802 AutoTArray
<nsIFrame
*, 1> neverUsed
;
2803 // Start gethering leaves of the 3D rendering context, and
2804 // append leaves at the end of mItemBuffer. Leaves are
2805 // processed at following iterations.
2806 aState
->mInPreserves3D
= true;
2807 item
->HitTest(aBuilder
, aRect
, aState
, &neverUsed
);
2808 aState
->mInPreserves3D
= false;
2809 i
= aState
->mItemBuffer
.Length();
2812 if (same3DContext
|| item
->GetClip().MayIntersect(r
)) {
2813 AutoTArray
<nsIFrame
*, 16> outFrames
;
2814 item
->HitTest(aBuilder
, aRect
, aState
, &outFrames
);
2816 // For 3d transforms with preserve-3d we add hit frames into the temp list
2817 // so we can sort them later, otherwise we add them directly to the output
2819 nsTArray
<nsIFrame
*>* writeFrames
= aOutFrames
;
2820 if (item
->GetType() == DisplayItemType::TYPE_TRANSFORM
&&
2821 static_cast<nsDisplayTransform
*>(item
)->IsLeafOf3DContext()) {
2822 if (outFrames
.Length()) {
2823 nsDisplayTransform
* transform
=
2824 static_cast<nsDisplayTransform
*>(item
);
2825 nsPoint point
= aRect
.TopLeft();
2826 // A 1x1 rect means a point, otherwise use the center of the rect
2827 if (aRect
.width
!= 1 || aRect
.height
!= 1) {
2828 point
= aRect
.Center();
2831 FramesWithDepth(transform
->GetHitDepthAtPoint(aBuilder
, point
)));
2832 writeFrames
= &temp
[temp
.Length() - 1].mFrames
;
2835 // We may have just finished a run of consecutive preserve-3d
2836 // transforms, so flush these into the destination array before
2837 // processing our frame list.
2838 FlushFramesArray(temp
, aOutFrames
);
2841 for (uint32_t j
= 0; j
< outFrames
.Length(); j
++) {
2842 nsIFrame
* f
= outFrames
.ElementAt(j
);
2843 // Filter out some frames depending on the type of hittest
2844 // we are doing. For visibility tests, pass through all frames.
2845 // For pointer tests, only pass through frames that are styled
2846 // to receive pointer events.
2847 if (aBuilder
->HitTestIsForVisibility() ||
2848 IsFrameReceivingPointerEvents(f
)) {
2849 writeFrames
->AppendElement(f
);
2853 if (aBuilder
->HitTestIsForVisibility()) {
2854 aState
->mHitOccludingItem
= [&] {
2855 if (aState
->mHitOccludingItem
) {
2856 // We already hit something before.
2859 if (aState
->mCurrentOpacity
== 1.0f
&&
2860 item
->GetOpaqueRegion(aBuilder
, &snap
).Contains(aRect
)) {
2861 // An opaque item always occludes everything. Note that we need to
2862 // check wrapping opacity and such as well.
2865 float threshold
= aBuilder
->VisibilityThreshold();
2866 if (threshold
== 1.0f
) {
2869 float itemOpacity
= [&] {
2870 switch (item
->GetType()) {
2871 case DisplayItemType::TYPE_OPACITY
:
2872 return static_cast<nsDisplayOpacity
*>(item
)->GetOpacity();
2873 case DisplayItemType::TYPE_BACKGROUND_COLOR
:
2874 return static_cast<nsDisplayBackgroundColor
*>(item
)
2877 // Be conservative and assume it won't occlude other items.
2881 return itemOpacity
* aState
->mCurrentOpacity
>= threshold
;
2884 if (aState
->mHitOccludingItem
) {
2885 // We're exiting early, so pop the remaining items off the buffer.
2886 aState
->mItemBuffer
.TruncateLength(itemBufferStart
);
2892 // Clear any remaining preserve-3d transforms.
2893 FlushFramesArray(temp
, aOutFrames
);
2894 NS_ASSERTION(aState
->mItemBuffer
.Length() == uint32_t(itemBufferStart
),
2895 "How did we forget to pop some elements?");
2898 static nsIContent
* FindContentInDocument(nsDisplayItem
* aItem
, Document
* aDoc
) {
2899 nsIFrame
* f
= aItem
->Frame();
2901 nsPresContext
* pc
= f
->PresContext();
2902 if (pc
->Document() == aDoc
) {
2903 return f
->GetContent();
2905 f
= nsLayoutUtils::GetCrossDocParentFrame(pc
->PresShell()->GetRootFrame());
2911 nsDisplayItem
* item
;
2914 explicit ZSortItem(nsDisplayItem
* aItem
)
2915 : item(aItem
), zIndex(aItem
->ZIndex()) {}
2917 operator nsDisplayItem
*() { return item
; }
2920 struct ZOrderComparator
{
2921 bool operator()(const ZSortItem
& aLeft
, const ZSortItem
& aRight
) const {
2922 // Note that we can't just take the difference of the two
2923 // z-indices here, because that might overflow a 32-bit int.
2924 return aLeft
.zIndex
< aRight
.zIndex
;
2928 void nsDisplayList::SortByZOrder() { Sort
<ZSortItem
>(ZOrderComparator()); }
2930 struct ContentComparator
{
2931 nsIContent
* mCommonAncestor
;
2933 explicit ContentComparator(nsIContent
* aCommonAncestor
)
2934 : mCommonAncestor(aCommonAncestor
) {}
2936 bool operator()(nsDisplayItem
* aLeft
, nsDisplayItem
* aRight
) const {
2937 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2938 // subdocument of commonAncestor, because display items for subdocuments
2939 // have been mixed into the same list. Ensure that we're looking at content
2940 // in commonAncestor's document.
2941 Document
* commonAncestorDoc
= mCommonAncestor
->OwnerDoc();
2942 nsIContent
* content1
= FindContentInDocument(aLeft
, commonAncestorDoc
);
2943 nsIContent
* content2
= FindContentInDocument(aRight
, commonAncestorDoc
);
2944 if (!content1
|| !content2
) {
2945 NS_ERROR("Document trees are mixed up!");
2946 // Something weird going on
2949 return nsLayoutUtils::CompareTreePosition(content1
, content2
,
2950 mCommonAncestor
) < 0;
2954 void nsDisplayList::SortByContentOrder(nsIContent
* aCommonAncestor
) {
2955 Sort
<nsDisplayItem
*>(ContentComparator(aCommonAncestor
));
2958 bool nsDisplayItemBase::HasModifiedFrame() const {
2959 return mItemFlags
.contains(ItemBaseFlag::ModifiedFrame
);
2962 void nsDisplayItemBase::SetModifiedFrame(bool aModified
) {
2964 mItemFlags
+= ItemBaseFlag::ModifiedFrame
;
2966 mItemFlags
-= ItemBaseFlag::ModifiedFrame
;
2970 void nsDisplayItemBase::SetDeletedFrame() {
2971 mItemFlags
+= ItemBaseFlag::DeletedFrame
;
2974 bool nsDisplayItemBase::HasDeletedFrame() const {
2975 bool retval
= mItemFlags
.contains(ItemBaseFlag::DeletedFrame
) ||
2976 (GetType() == DisplayItemType::TYPE_REMOTE
&&
2977 !static_cast<const nsDisplayRemote
*>(this)->GetFrameLoader());
2978 MOZ_ASSERT(retval
|| mFrame
);
2982 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2983 static_assert(sizeof(nsDisplayItem
) <= 176, "nsDisplayItem has grown");
2986 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
)
2987 : nsDisplayItem(aBuilder
, aFrame
, aBuilder
->CurrentActiveScrolledRoot()) {}
2989 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
2990 const ActiveScrolledRoot
* aActiveScrolledRoot
)
2991 : nsDisplayItemBase(aBuilder
, aFrame
),
2992 mActiveScrolledRoot(aActiveScrolledRoot
),
2993 mAnimatedGeometryRoot(nullptr) {
2994 MOZ_COUNT_CTOR(nsDisplayItem
);
2996 mReferenceFrame
= aBuilder
->FindReferenceFrameFor(aFrame
, &mToReferenceFrame
);
2997 // This can return the wrong result if the item override
2998 // ShouldFixToViewport(), the item needs to set it again in its constructor.
2999 mAnimatedGeometryRoot
= aBuilder
->FindAnimatedGeometryRootFor(aFrame
);
3000 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(
3001 aBuilder
->RootReferenceFrame(), *mAnimatedGeometryRoot
),
3004 aBuilder
->GetVisibleRect().width
>= 0 || !aBuilder
->IsForPainting(),
3005 "visible rect not set");
3007 nsDisplayItem::SetClipChain(
3008 aBuilder
->ClipState().GetCurrentCombinedClipChain(aBuilder
), true);
3010 // The visible rect is for mCurrentFrame, so we have to use
3011 // mCurrentOffsetToReferenceFrame
3012 nsRect visible
= aBuilder
->GetVisibleRect() +
3013 aBuilder
->GetCurrentFrameOffsetToReferenceFrame();
3014 SetBuildingRect(visible
);
3016 const nsStyleDisplay
* disp
= mFrame
->StyleDisplay();
3017 if (mFrame
->BackfaceIsHidden(disp
)) {
3018 mItemFlags
+= ItemFlag::BackfaceHidden
;
3020 if (mFrame
->Combines3DTransformWithAncestors()) {
3021 mItemFlags
+= ItemFlag::Combines3DTransformWithAncestors
;
3026 bool nsDisplayItem::ForceActiveLayers() {
3027 return StaticPrefs::layers_force_active();
3030 int32_t nsDisplayItem::ZIndex() const { return mFrame
->ZIndex().valueOr(0); }
3032 bool nsDisplayItem::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
3033 nsRegion
* aVisibleRegion
) {
3034 return !GetPaintRect().IsEmpty() &&
3035 !IsInvisibleInRect(aVisibleRegion
->GetBounds());
3038 bool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder
* aBuilder
,
3039 nsRegion
* aVisibleRegion
) {
3040 if (ForceNotVisible() && !GetSameCoordinateSystemChildren()) {
3041 // mForceNotVisible wants to ensure that this display item doesn't render
3042 // anything itself. If this item has contents, then we obviously want to
3043 // render those, so we don't need this check in that case.
3044 NS_ASSERTION(GetBuildingRect().IsEmpty(),
3045 "invisible items without children should have empty vis rect");
3046 SetPaintRect(nsRect());
3049 nsRect bounds
= GetBounds(aBuilder
, &snap
);
3051 nsRegion itemVisible
;
3052 itemVisible
.And(*aVisibleRegion
, bounds
);
3053 SetPaintRect(itemVisible
.GetBounds());
3056 if (!ComputeVisibility(aBuilder
, aVisibleRegion
)) {
3057 SetPaintRect(nsRect());
3061 nsRegion opaque
= TreatAsOpaque(this, aBuilder
);
3062 aBuilder
->SubtractFromVisibleRegion(aVisibleRegion
, opaque
);
3066 void nsDisplayItem::SetClipChain(const DisplayItemClipChain
* aClipChain
,
3068 mClipChain
= aClipChain
;
3069 mClip
= DisplayItemClipChain::ClipForASR(aClipChain
, mActiveScrolledRoot
);
3072 mState
.mClipChain
= mClipChain
;
3073 mState
.mClip
= mClip
;
3077 Maybe
<nsRect
> nsDisplayItem::GetClipWithRespectToASR(
3078 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
) const {
3079 if (const DisplayItemClip
* clip
=
3080 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR
)) {
3081 return Some(clip
->GetClipRect());
3084 MOZ_ASSERT(false, "item should have finite clip with respect to aASR");
3089 void nsDisplayItem::FuseClipChainUpTo(nsDisplayListBuilder
* aBuilder
,
3090 const ActiveScrolledRoot
* aASR
) {
3091 mClipChain
= aBuilder
->FuseClipChainUpTo(mClipChain
, aASR
);
3094 mClip
= &mClipChain
->mClip
;
3100 static const DisplayItemClipChain
* FindCommonAncestorClipForIntersection(
3101 const DisplayItemClipChain
* aOne
, const DisplayItemClipChain
* aTwo
) {
3102 for (const ActiveScrolledRoot
* asr
=
3103 ActiveScrolledRoot::PickDescendant(aOne
->mASR
, aTwo
->mASR
);
3104 asr
; asr
= asr
->mParent
) {
3108 if (aOne
->mASR
== asr
) {
3109 aOne
= aOne
->mParent
;
3111 if (aTwo
->mASR
== asr
) {
3112 aTwo
= aTwo
->mParent
;
3124 void nsDisplayItem::IntersectClip(nsDisplayListBuilder
* aBuilder
,
3125 const DisplayItemClipChain
* aOther
,
3127 if (!aOther
|| mClipChain
== aOther
) {
3131 // aOther might be a reference to a clip on the stack. We need to make sure
3132 // that CreateClipChainIntersection will allocate the actual intersected
3133 // clip in the builder's arena, so for the mClipChain == nullptr case,
3134 // we supply nullptr as the common ancestor so that
3135 // CreateClipChainIntersection clones the whole chain.
3136 const DisplayItemClipChain
* ancestorClip
=
3137 mClipChain
? FindCommonAncestorClipForIntersection(mClipChain
, aOther
)
3141 aBuilder
->CreateClipChainIntersection(ancestorClip
, mClipChain
, aOther
),
3145 nsRect
nsDisplayItem::GetClippedBounds(nsDisplayListBuilder
* aBuilder
) const {
3147 nsRect r
= GetBounds(aBuilder
, &snap
);
3148 return GetClip().ApplyNonRoundedIntersection(r
);
3151 nsDisplayContainer::nsDisplayContainer(
3152 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
3153 const ActiveScrolledRoot
* aActiveScrolledRoot
, nsDisplayList
* aList
)
3154 : nsDisplayItem(aBuilder
, aFrame
, aActiveScrolledRoot
) {
3155 MOZ_COUNT_CTOR(nsDisplayContainer
);
3156 mChildren
.AppendToTop(aList
);
3157 UpdateBounds(aBuilder
);
3159 // Clear and store the clip chain set by nsDisplayItem constructor.
3160 nsDisplayItem::SetClipChain(nullptr, true);
3163 bool nsDisplayContainer::CreateWebRenderCommands(
3164 mozilla::wr::DisplayListBuilder
& aBuilder
,
3165 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
3166 const StackingContextHelper
& aSc
,
3167 mozilla::layers::RenderRootStateManager
* aManager
,
3168 nsDisplayListBuilder
* aDisplayListBuilder
) {
3169 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
3170 GetChildren(), this, aDisplayListBuilder
, aSc
, aBuilder
, aResources
);
3175 * Like |nsDisplayList::ComputeVisibilityForSublist()|, but restricts
3176 * |aVisibleRegion| to given |aBounds| of the list.
3178 static bool ComputeClippedVisibilityForSubList(nsDisplayListBuilder
* aBuilder
,
3179 nsRegion
* aVisibleRegion
,
3180 nsDisplayList
* aList
,
3181 const nsRect
& aBounds
) {
3182 nsRegion visibleRegion
;
3183 visibleRegion
.And(*aVisibleRegion
, aBounds
);
3184 nsRegion originalVisibleRegion
= visibleRegion
;
3186 const bool anyItemVisible
=
3187 aList
->ComputeVisibilityForSublist(aBuilder
, &visibleRegion
, aBounds
);
3189 // removed = originalVisibleRegion - visibleRegion
3190 removed
.Sub(originalVisibleRegion
, visibleRegion
);
3191 // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications
3192 // SubtractFromVisibleRegion does)
3193 aBuilder
->SubtractFromVisibleRegion(aVisibleRegion
, removed
);
3195 return anyItemVisible
;
3198 bool nsDisplayContainer::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
3199 nsRegion
* aVisibleRegion
) {
3200 return ::ComputeClippedVisibilityForSubList(aBuilder
, aVisibleRegion
,
3201 GetChildren(), GetPaintRect());
3204 nsRect
nsDisplayContainer::GetBounds(nsDisplayListBuilder
* aBuilder
,
3205 bool* aSnap
) const {
3210 nsRect
nsDisplayContainer::GetComponentAlphaBounds(
3211 nsDisplayListBuilder
* aBuilder
) const {
3212 return mChildren
.GetComponentAlphaBounds(aBuilder
);
3215 static nsRegion
GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
3216 nsDisplayList
* aList
,
3217 const nsRect
& aListBounds
) {
3218 if (aList
->IsOpaque()) {
3219 // Everything within list bounds that's visible is opaque. This is an
3220 // optimization to avoid calculating the opaque region.
3224 if (aBuilder
->HitTestIsForVisibility()) {
3225 // If we care about an accurate opaque region, iterate the display list
3226 // and build up a region of opaque bounds.
3227 return aList
->GetOpaqueRegion(aBuilder
);
3233 nsRegion
nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
3234 bool* aSnap
) const {
3235 return ::GetOpaqueRegion(aBuilder
, GetChildren(), GetBounds(aBuilder
, aSnap
));
3238 Maybe
<nsRect
> nsDisplayContainer::GetClipWithRespectToASR(
3239 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
) const {
3240 // Our children should have finite bounds with respect to |aASR|.
3241 if (aASR
== mActiveScrolledRoot
) {
3242 return Some(mBounds
);
3245 return Some(mChildren
.GetClippedBoundsWithRespectToASR(aBuilder
, aASR
));
3248 void nsDisplayContainer::HitTest(nsDisplayListBuilder
* aBuilder
,
3249 const nsRect
& aRect
, HitTestState
* aState
,
3250 nsTArray
<nsIFrame
*>* aOutFrames
) {
3251 mChildren
.HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
3254 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder
* aBuilder
) {
3255 // Container item bounds are expected to be clipped.
3257 mChildren
.GetClippedBoundsWithRespectToASR(aBuilder
, mActiveScrolledRoot
);
3260 nsRect
nsDisplaySolidColor::GetBounds(nsDisplayListBuilder
* aBuilder
,
3261 bool* aSnap
) const {
3266 LayerState
nsDisplaySolidColor::GetLayerState(
3267 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
3268 const ContainerLayerParameters
& aParameters
) {
3269 if (ForceActiveLayers()) {
3270 return LayerState::LAYER_ACTIVE
;
3273 return LayerState::LAYER_NONE
;
3276 already_AddRefed
<Layer
> nsDisplaySolidColor::BuildLayer(
3277 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
3278 const ContainerLayerParameters
& aContainerParameters
) {
3279 RefPtr
<ColorLayer
> layer
= static_cast<ColorLayer
*>(
3280 aManager
->GetLayerBuilder()->GetLeafLayerFor(aBuilder
, this));
3282 layer
= aManager
->CreateColorLayer();
3287 layer
->SetColor(ToDeviceColor(mColor
));
3289 const int32_t appUnitsPerDevPixel
=
3290 mFrame
->PresContext()->AppUnitsPerDevPixel();
3291 layer
->SetBounds(mBounds
.ToNearestPixels(appUnitsPerDevPixel
));
3292 layer
->SetBaseTransform(gfx::Matrix4x4::Translation(
3293 aContainerParameters
.mOffset
.x
, aContainerParameters
.mOffset
.y
, 0));
3295 return layer
.forget();
3298 void nsDisplaySolidColor::Paint(nsDisplayListBuilder
* aBuilder
,
3300 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
3301 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
3303 NSRectToSnappedRect(GetPaintRect(), appUnitsPerDevPixel
, *drawTarget
);
3304 drawTarget
->FillRect(rect
, ColorPattern(ToDeviceColor(mColor
)));
3307 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream
& aStream
) {
3308 aStream
<< " (rgba " << (int)NS_GET_R(mColor
) << "," << (int)NS_GET_G(mColor
)
3309 << "," << (int)NS_GET_B(mColor
) << "," << (int)NS_GET_A(mColor
)
3313 bool nsDisplaySolidColor::CreateWebRenderCommands(
3314 mozilla::wr::DisplayListBuilder
& aBuilder
,
3315 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
3316 const StackingContextHelper
& aSc
,
3317 mozilla::layers::RenderRootStateManager
* aManager
,
3318 nsDisplayListBuilder
* aDisplayListBuilder
) {
3319 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
3320 mBounds
, mFrame
->PresContext()->AppUnitsPerDevPixel());
3321 wr::LayoutRect r
= wr::ToLayoutRect(bounds
);
3322 aBuilder
.PushRect(r
, r
, !BackfaceIsHidden(),
3323 wr::ToColorF(ToDeviceColor(mColor
)));
3328 nsRect
nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder
* aBuilder
,
3329 bool* aSnap
) const {
3331 return mRegion
.GetBounds();
3334 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder
* aBuilder
,
3336 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
3337 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
3338 ColorPattern
color(ToDeviceColor(mColor
));
3339 for (auto iter
= mRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
3341 NSRectToSnappedRect(iter
.Get(), appUnitsPerDevPixel
, *drawTarget
);
3342 drawTarget
->FillRect(rect
, color
);
3346 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream
& aStream
) {
3347 aStream
<< " (rgba " << int(mColor
.r
* 255) << "," << int(mColor
.g
* 255)
3348 << "," << int(mColor
.b
* 255) << "," << mColor
.a
<< ")";
3351 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
3352 mozilla::wr::DisplayListBuilder
& aBuilder
,
3353 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
3354 const StackingContextHelper
& aSc
,
3355 mozilla::layers::RenderRootStateManager
* aManager
,
3356 nsDisplayListBuilder
* aDisplayListBuilder
) {
3357 for (auto iter
= mRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
3358 nsRect rect
= iter
.Get();
3359 LayoutDeviceRect layerRects
= LayoutDeviceRect::FromAppUnits(
3360 rect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
3361 wr::LayoutRect r
= wr::ToLayoutRect(layerRects
);
3362 aBuilder
.PushRect(r
, r
, !BackfaceIsHidden(),
3363 wr::ToColorF(ToDeviceColor(mColor
)));
3369 static void RegisterThemeGeometry(nsDisplayListBuilder
* aBuilder
,
3370 nsDisplayItem
* aItem
, nsIFrame
* aFrame
,
3371 nsITheme::ThemeGeometryType aType
) {
3372 if (aBuilder
->IsInChromeDocumentOrPopup()) {
3373 nsIFrame
* displayRoot
= nsLayoutUtils::GetDisplayRootFrame(aFrame
);
3374 bool preservesAxisAlignedRectangles
= false;
3375 nsRect borderBox
= nsLayoutUtils::TransformFrameRectToAncestor(
3376 aFrame
, aFrame
->GetRectRelativeToSelf(), displayRoot
,
3377 &preservesAxisAlignedRectangles
);
3378 if (preservesAxisAlignedRectangles
) {
3379 aBuilder
->RegisterThemeGeometry(
3381 LayoutDeviceIntRect::FromUnknownRect(borderBox
.ToNearestPixels(
3382 aFrame
->PresContext()->AppUnitsPerDevPixel())));
3387 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
3388 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
3389 static Maybe
<nsRect
> GetViewportRectRelativeToReferenceFrame(
3390 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
) {
3391 nsIFrame
* rootFrame
= aFrame
->PresShell()->GetRootFrame();
3392 nsRect rootRect
= rootFrame
->GetRectRelativeToSelf();
3393 if (nsLayoutUtils::TransformRect(rootFrame
, aFrame
, rootRect
) ==
3394 nsLayoutUtils::TRANSFORM_SUCCEEDED
) {
3395 return Some(rootRect
+ aBuilder
->ToReferenceFrame(aFrame
));
3400 /* static */ nsDisplayBackgroundImage::InitData
3401 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder
* aBuilder
,
3402 nsIFrame
* aFrame
, uint16_t aLayer
,
3403 const nsRect
& aBackgroundRect
,
3404 ComputedStyle
* aBackgroundStyle
) {
3405 nsPresContext
* presContext
= aFrame
->PresContext();
3406 uint32_t flags
= aBuilder
->GetBackgroundPaintFlags();
3407 const nsStyleImageLayers::Layer
& layer
=
3408 aBackgroundStyle
->StyleBackground()->mImage
.mLayers
[aLayer
];
3410 bool isTransformedFixed
;
3411 nsBackgroundLayerState state
= nsCSSRendering::PrepareImageLayer(
3412 presContext
, aFrame
, flags
, aBackgroundRect
, aBackgroundRect
, layer
,
3413 &isTransformedFixed
);
3415 // background-attachment:fixed is treated as background-attachment:scroll
3416 // if it's affected by a transform.
3417 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
3418 bool shouldTreatAsFixed
=
3419 layer
.mAttachment
== StyleImageLayerAttachment::Fixed
&&
3420 !isTransformedFixed
;
3422 bool shouldFixToViewport
= shouldTreatAsFixed
&& !layer
.mImage
.IsNone();
3423 bool isRasterImage
= state
.mImageRenderer
.IsRasterImage();
3424 nsCOMPtr
<imgIContainer
> image
;
3425 if (isRasterImage
) {
3426 image
= state
.mImageRenderer
.GetImage();
3428 return InitData
{aBuilder
, aBackgroundStyle
, image
,
3429 aBackgroundRect
, state
.mFillArea
, state
.mDestArea
,
3430 aLayer
, isRasterImage
, shouldFixToViewport
};
3433 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
3434 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, const InitData
& aInitData
,
3435 nsIFrame
* aFrameForBounds
)
3436 : nsDisplayImageContainer(aBuilder
, aFrame
),
3437 mBackgroundStyle(aInitData
.backgroundStyle
),
3438 mImage(aInitData
.image
),
3439 mDependentFrame(nullptr),
3440 mBackgroundRect(aInitData
.backgroundRect
),
3441 mFillRect(aInitData
.fillArea
),
3442 mDestRect(aInitData
.destArea
),
3443 mLayer(aInitData
.layer
),
3444 mIsRasterImage(aInitData
.isRasterImage
),
3445 mShouldFixToViewport(aInitData
.shouldFixToViewport
),
3448 MOZ_COUNT_CTOR(nsDisplayBackgroundImage
);
3450 if (mBackgroundStyle
&& mBackgroundStyle
!= mFrame
->Style()) {
3451 // If this changes, then you also need to adjust css::ImageLoader to
3452 // invalidate mFrame as needed.
3453 MOZ_ASSERT(mFrame
->IsCanvasFrame() ||
3454 mFrame
->IsFrameOfType(nsIFrame::eTablePart
));
3458 mBounds
= GetBoundsInternal(aInitData
.builder
, aFrameForBounds
);
3459 if (mShouldFixToViewport
) {
3460 mAnimatedGeometryRoot
=
3461 aInitData
.builder
->FindAnimatedGeometryRootFor(this);
3463 // Expand the item's visible rect to cover the entire bounds, limited to the
3464 // viewport rect. This is necessary because the background's clip can move
3466 if (Maybe
<nsRect
> viewportRect
= GetViewportRectRelativeToReferenceFrame(
3467 aInitData
.builder
, mFrame
)) {
3468 SetBuildingRect(mBounds
.Intersect(*viewportRect
));
3473 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
3474 MOZ_COUNT_DTOR(nsDisplayBackgroundImage
);
3475 if (mDependentFrame
) {
3476 mDependentFrame
->RemoveDisplayItem(this);
3480 static nsIFrame
* GetBackgroundComputedStyleFrame(nsIFrame
* aFrame
) {
3482 if (!nsCSSRendering::FindBackgroundFrame(aFrame
, &f
)) {
3483 // We don't want to bail out if moz-appearance is set on a root
3484 // node. If it has a parent content node, bail because it's not
3485 // a root, other wise keep going in order to let the theme stuff
3486 // draw the background. The canvas really should be drawing the
3487 // bg, but there's no way to hook that up via css.
3488 if (!aFrame
->StyleDisplay()->HasAppearance()) {
3492 nsIContent
* content
= aFrame
->GetContent();
3493 if (!content
|| content
->GetParent()) {
3502 static void SetBackgroundClipRegion(
3503 DisplayListClipState::AutoSaveRestore
& aClipState
, nsIFrame
* aFrame
,
3504 const nsStyleImageLayers::Layer
& aLayer
, const nsRect
& aBackgroundRect
,
3505 bool aWillPaintBorder
) {
3506 nsCSSRendering::ImageLayerClipState clip
;
3507 nsCSSRendering::GetImageLayerClip(
3508 aLayer
, aFrame
, *aFrame
->StyleBorder(), aBackgroundRect
, aBackgroundRect
,
3509 aWillPaintBorder
, aFrame
->PresContext()->AppUnitsPerDevPixel(), &clip
);
3511 if (clip
.mHasAdditionalBGClipArea
) {
3512 aClipState
.ClipContentDescendants(
3513 clip
.mAdditionalBGClipArea
, clip
.mBGClipArea
,
3514 clip
.mHasRoundedCorners
? clip
.mRadii
: nullptr);
3516 aClipState
.ClipContentDescendants(
3517 clip
.mBGClipArea
, clip
.mHasRoundedCorners
? clip
.mRadii
: nullptr);
3522 * This is used for the find bar highlighter overlay. It's only accessible
3523 * through the AnonymousContent API, so it's not exposed to general web pages.
3525 static bool SpecialCutoutRegionCase(nsDisplayListBuilder
* aBuilder
,
3527 const nsRect
& aBackgroundRect
,
3528 nsDisplayList
* aList
, nscolor aColor
) {
3529 nsIContent
* content
= aFrame
->GetContent();
3534 void* cutoutRegion
= content
->GetProperty(nsGkAtoms::cutoutregion
);
3535 if (!cutoutRegion
) {
3539 if (NS_GET_A(aColor
) == 0) {
3544 region
.Sub(aBackgroundRect
, *static_cast<nsRegion
*>(cutoutRegion
));
3545 region
.MoveBy(aBuilder
->ToReferenceFrame(aFrame
));
3546 aList
->AppendNewToTop
<nsDisplaySolidColorRegion
>(aBuilder
, aFrame
, region
,
3552 enum class TableType
: uint8_t {
3563 enum class TableTypeBits
: uint8_t { Count
= 3 };
3565 static_assert(static_cast<uint8_t>(TableType::MAX
) <
3566 (1 << (static_cast<uint8_t>(TableTypeBits::Count
) + 1)),
3567 "TableType cannot fit with TableTypeBits::Count");
3568 TableType
GetTableTypeFromFrame(nsIFrame
* aFrame
);
3570 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex
,
3571 const TableType aType
) {
3572 const uint32_t key
= (aIndex
<< static_cast<uint8_t>(TableTypeBits::Count
)) |
3573 static_cast<uint8_t>(aType
);
3575 return static_cast<uint16_t>(key
);
3578 static nsDisplayBackgroundImage
* CreateBackgroundImage(
3579 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
3580 const nsDisplayBackgroundImage::InitData
& aBgData
) {
3581 const auto index
= aBgData
.layer
;
3583 if (aSecondaryFrame
) {
3584 const auto tableType
= GetTableTypeFromFrame(aFrame
);
3585 const uint16_t tableItemIndex
= CalculateTablePerFrameKey(index
, tableType
);
3587 return MakeDisplayItemWithIndex
<nsDisplayTableBackgroundImage
>(
3588 aBuilder
, aSecondaryFrame
, tableItemIndex
, aBgData
, aFrame
);
3591 return MakeDisplayItemWithIndex
<nsDisplayBackgroundImage
>(aBuilder
, aFrame
,
3595 static nsDisplayThemedBackground
* CreateThemedBackground(
3596 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
3598 if (aSecondaryFrame
) {
3599 const uint16_t index
= static_cast<uint16_t>(GetTableTypeFromFrame(aFrame
));
3600 return MakeDisplayItemWithIndex
<nsDisplayTableThemedBackground
>(
3601 aBuilder
, aSecondaryFrame
, index
, aBgRect
, aFrame
);
3604 return MakeDisplayItem
<nsDisplayThemedBackground
>(aBuilder
, aFrame
, aBgRect
);
3607 static nsDisplayBackgroundColor
* CreateBackgroundColor(
3608 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
3609 nsRect
& aBgRect
, ComputedStyle
* aBgSC
, nscolor aColor
) {
3610 if (aSecondaryFrame
) {
3611 const uint16_t index
= static_cast<uint16_t>(GetTableTypeFromFrame(aFrame
));
3612 return MakeDisplayItemWithIndex
<nsDisplayTableBackgroundColor
>(
3613 aBuilder
, aSecondaryFrame
, index
, aBgRect
, aBgSC
, aColor
, aFrame
);
3616 return MakeDisplayItem
<nsDisplayBackgroundColor
>(aBuilder
, aFrame
, aBgRect
,
3621 AppendedBackgroundType
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3622 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
3623 const nsRect
& aBackgroundRect
, nsDisplayList
* aList
,
3624 bool aAllowWillPaintBorderOptimization
, ComputedStyle
* aComputedStyle
,
3625 const nsRect
& aBackgroundOriginRect
, nsIFrame
* aSecondaryReferenceFrame
,
3626 Maybe
<nsDisplayListBuilder::AutoBuildingDisplayList
>*
3627 aAutoBuildingDisplayList
) {
3628 ComputedStyle
* bgSC
= aComputedStyle
;
3629 const nsStyleBackground
* bg
= nullptr;
3630 nsRect bgRect
= aBackgroundRect
;
3631 nsRect bgOriginRect
= bgRect
;
3632 if (!aBackgroundOriginRect
.IsEmpty()) {
3633 bgOriginRect
= aBackgroundOriginRect
;
3635 nsPresContext
* presContext
= aFrame
->PresContext();
3636 bool isThemed
= aFrame
->IsThemed();
3637 nsIFrame
* dependentFrame
= nullptr;
3640 dependentFrame
= GetBackgroundComputedStyleFrame(aFrame
);
3641 if (dependentFrame
) {
3642 bgSC
= dependentFrame
->Style();
3643 if (dependentFrame
== aFrame
) {
3644 dependentFrame
= nullptr;
3649 bg
= bgSC
->StyleBackground();
3653 bool drawBackgroundColor
= false;
3654 // XUL root frames need special handling for now even though they return true
3655 // from nsCSSRendering::IsCanvasFrame they rely on us painting the background
3656 // image from here, see bug 1665476.
3657 bool drawBackgroundImage
=
3658 aFrame
->IsXULRootFrame() && aFrame
->ComputeShouldPaintBackground().mImage
;
3659 nscolor color
= NS_RGBA(0, 0, 0, 0);
3660 if (!nsCSSRendering::IsCanvasFrame(aFrame
) && bg
) {
3661 color
= nsCSSRendering::DetermineBackgroundColor(
3662 presContext
, bgSC
, aFrame
, drawBackgroundImage
, drawBackgroundColor
);
3665 if (SpecialCutoutRegionCase(aBuilder
, aFrame
, aBackgroundRect
, aList
,
3667 return AppendedBackgroundType::None
;
3670 const nsStyleBorder
* borderStyle
= aFrame
->StyleBorder();
3671 const nsStyleEffects
* effectsStyle
= aFrame
->StyleEffects();
3672 bool hasInsetShadow
= effectsStyle
->HasBoxShadowWithInset(true);
3673 bool willPaintBorder
= aAllowWillPaintBorderOptimization
&& !isThemed
&&
3674 !hasInsetShadow
&& borderStyle
->HasBorder();
3676 // An auxiliary list is necessary in case we have background blending; if that
3677 // is the case, background items need to be wrapped by a blend container to
3678 // isolate blending to the background
3679 nsDisplayList bgItemList
;
3680 // Even if we don't actually have a background color to paint, we may still
3681 // need to create an item for hit testing and we still need to create an item
3682 // for background-color animations.
3683 if ((drawBackgroundColor
&& color
!= NS_RGBA(0, 0, 0, 0)) ||
3684 aBuilder
->IsForEventDelivery() ||
3685 EffectCompositor::HasAnimationsForCompositor(
3686 aFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
)) {
3687 if (aAutoBuildingDisplayList
&& !*aAutoBuildingDisplayList
) {
3688 nsPoint offset
= aBuilder
->GetCurrentFrame()->GetOffsetTo(aFrame
);
3689 aAutoBuildingDisplayList
->emplace(aBuilder
, aFrame
,
3690 aBuilder
->GetVisibleRect() + offset
,
3691 aBuilder
->GetDirtyRect() + offset
);
3693 Maybe
<DisplayListClipState::AutoSaveRestore
> clipState
;
3694 nsRect bgColorRect
= bgRect
;
3695 if (bg
&& !aBuilder
->IsForEventDelivery()) {
3696 // Disable the will-paint-border optimization for background
3697 // colors with no border-radius. Enabling it for background colors
3698 // doesn't help much (there are no tiling issues) and clipping the
3699 // background breaks detection of the element's border-box being
3700 // opaque. For nonzero border-radius we still need it because we
3701 // want to inset the background if possible to avoid antialiasing
3702 // artifacts along the rounded corners.
3703 bool useWillPaintBorderOptimization
=
3705 nsLayoutUtils::HasNonZeroCorner(borderStyle
->mBorderRadius
);
3707 nsCSSRendering::ImageLayerClipState clip
;
3708 nsCSSRendering::GetImageLayerClip(
3709 bg
->BottomLayer(), aFrame
, *aFrame
->StyleBorder(), bgRect
, bgRect
,
3710 useWillPaintBorderOptimization
,
3711 aFrame
->PresContext()->AppUnitsPerDevPixel(), &clip
);
3713 bgColorRect
= bgColorRect
.Intersect(clip
.mBGClipArea
);
3714 if (clip
.mHasAdditionalBGClipArea
) {
3715 bgColorRect
= bgColorRect
.Intersect(clip
.mAdditionalBGClipArea
);
3717 if (clip
.mHasRoundedCorners
) {
3718 clipState
.emplace(aBuilder
);
3719 clipState
->ClipContentDescendants(clip
.mBGClipArea
, clip
.mRadii
);
3723 nsDisplayBackgroundColor
* bgItem
= CreateBackgroundColor(
3724 aBuilder
, aFrame
, aSecondaryReferenceFrame
, bgColorRect
, bgSC
,
3725 drawBackgroundColor
? color
: NS_RGBA(0, 0, 0, 0));
3728 bgItem
->SetDependentFrame(aBuilder
, dependentFrame
);
3729 bgItemList
.AppendToTop(bgItem
);
3734 nsDisplayThemedBackground
* bgItem
= CreateThemedBackground(
3735 aBuilder
, aFrame
, aSecondaryReferenceFrame
, bgRect
);
3738 bgItem
->Init(aBuilder
);
3739 bgItemList
.AppendToTop(bgItem
);
3742 if (!bgItemList
.IsEmpty()) {
3743 aList
->AppendToTop(&bgItemList
);
3744 return AppendedBackgroundType::ThemedBackground
;
3747 return AppendedBackgroundType::None
;
3750 if (!bg
|| !drawBackgroundImage
) {
3751 if (!bgItemList
.IsEmpty()) {
3752 aList
->AppendToTop(&bgItemList
);
3753 return AppendedBackgroundType::Background
;
3756 return AppendedBackgroundType::None
;
3759 const ActiveScrolledRoot
* asr
= aBuilder
->CurrentActiveScrolledRoot();
3761 bool needBlendContainer
= false;
3763 // Passing bg == nullptr in this macro will result in one iteration with
3765 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i
, bg
->mImage
) {
3766 if (bg
->mImage
.mLayers
[i
].mImage
.IsNone()) {
3770 if (aAutoBuildingDisplayList
&& !*aAutoBuildingDisplayList
) {
3771 nsPoint offset
= aBuilder
->GetCurrentFrame()->GetOffsetTo(aFrame
);
3772 aAutoBuildingDisplayList
->emplace(aBuilder
, aFrame
,
3773 aBuilder
->GetVisibleRect() + offset
,
3774 aBuilder
->GetDirtyRect() + offset
);
3777 if (bg
->mImage
.mLayers
[i
].mBlendMode
!= StyleBlend::Normal
) {
3778 needBlendContainer
= true;
3781 DisplayListClipState::AutoSaveRestore
clipState(aBuilder
);
3782 if (!aBuilder
->IsForEventDelivery()) {
3783 const nsStyleImageLayers::Layer
& layer
= bg
->mImage
.mLayers
[i
];
3784 SetBackgroundClipRegion(clipState
, aFrame
, layer
, bgRect
,
3788 nsDisplayList thisItemList
;
3789 nsDisplayBackgroundImage::InitData bgData
=
3790 nsDisplayBackgroundImage::GetInitData(aBuilder
, aFrame
, i
, bgOriginRect
,
3793 if (bgData
.shouldFixToViewport
) {
3794 auto* displayData
= aBuilder
->GetCurrentFixedBackgroundDisplayData();
3795 nsDisplayListBuilder::AutoBuildingDisplayList
buildingDisplayList(
3796 aBuilder
, aFrame
, aBuilder
->GetVisibleRect(),
3797 aBuilder
->GetDirtyRect());
3799 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter
asrSetter(
3802 asrSetter
.SetCurrentActiveScrolledRoot(
3803 displayData
->mContainingBlockActiveScrolledRoot
);
3804 if (nsLayoutUtils::UsesAsyncScrolling(aFrame
)) {
3805 // Override the dirty rect on the builder to be the dirty rect of
3807 // displayData->mDirtyRect is relative to the presshell's viewport
3808 // frame (the root frame), and we need it to be relative to aFrame.
3809 nsIFrame
* rootFrame
=
3810 aBuilder
->CurrentPresShellState()->mPresShell
->GetRootFrame();
3811 // There cannot be any transforms between aFrame and rootFrame
3812 // because then bgData.shouldFixToViewport would have been false.
3813 nsRect visibleRect
=
3814 displayData
->mVisibleRect
+ aFrame
->GetOffsetTo(rootFrame
);
3815 aBuilder
->SetVisibleRect(visibleRect
);
3817 displayData
->mDirtyRect
+ aFrame
->GetOffsetTo(rootFrame
);
3818 aBuilder
->SetDirtyRect(dirtyRect
);
3822 nsDisplayBackgroundImage
* bgItem
= nullptr;
3824 // The clip is captured by the nsDisplayFixedPosition, so clear the
3825 // clip for the nsDisplayBackgroundImage inside.
3826 DisplayListClipState::AutoSaveRestore
bgImageClip(aBuilder
);
3827 bgImageClip
.Clear();
3828 bgItem
= CreateBackgroundImage(aBuilder
, aFrame
,
3829 aSecondaryReferenceFrame
, bgData
);
3832 bgItem
->SetDependentFrame(aBuilder
, dependentFrame
);
3834 thisItemList
.AppendToTop(
3835 nsDisplayFixedPosition::CreateForFixedBackground(
3836 aBuilder
, aFrame
, aSecondaryReferenceFrame
, bgItem
, i
));
3838 } else { // bgData.shouldFixToViewport == false
3839 nsDisplayBackgroundImage
* bgItem
= CreateBackgroundImage(
3840 aBuilder
, aFrame
, aSecondaryReferenceFrame
, bgData
);
3842 bgItem
->SetDependentFrame(aBuilder
, dependentFrame
);
3843 thisItemList
.AppendToTop(bgItem
);
3847 if (bg
->mImage
.mLayers
[i
].mBlendMode
!= StyleBlend::Normal
) {
3848 DisplayListClipState::AutoSaveRestore
blendClip(aBuilder
);
3849 // asr is scrolled. Even if we wrap a fixed background layer, that's
3850 // fine, because the item will have a scrolled clip that limits the
3851 // item with respect to asr.
3852 if (aSecondaryReferenceFrame
) {
3853 const auto tableType
= GetTableTypeFromFrame(aFrame
);
3854 const uint16_t index
= CalculateTablePerFrameKey(i
+ 1, tableType
);
3856 thisItemList
.AppendNewToTopWithIndex
<nsDisplayTableBlendMode
>(
3857 aBuilder
, aSecondaryReferenceFrame
, index
, &thisItemList
,
3858 bg
->mImage
.mLayers
[i
].mBlendMode
, asr
, aFrame
, true);
3860 thisItemList
.AppendNewToTopWithIndex
<nsDisplayBlendMode
>(
3861 aBuilder
, aFrame
, i
+ 1, &thisItemList
,
3862 bg
->mImage
.mLayers
[i
].mBlendMode
, asr
, true);
3865 bgItemList
.AppendToTop(&thisItemList
);
3868 if (needBlendContainer
) {
3869 DisplayListClipState::AutoSaveRestore
blendContainerClip(aBuilder
);
3871 bgItemList
.AppendToTop(
3872 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3873 aBuilder
, aFrame
, aSecondaryReferenceFrame
, &bgItemList
, asr
));
3876 if (!bgItemList
.IsEmpty()) {
3877 aList
->AppendToTop(&bgItemList
);
3878 return AppendedBackgroundType::Background
;
3881 return AppendedBackgroundType::None
;
3884 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3885 // intersects aRect. Assumes that the unrounded border has already
3886 // been checked for intersection.
3887 static bool RoundedBorderIntersectsRect(nsIFrame
* aFrame
,
3888 const nsPoint
& aFrameToReferenceFrame
,
3889 const nsRect
& aTestRect
) {
3890 if (!nsRect(aFrameToReferenceFrame
, aFrame
->GetSize()).Intersects(aTestRect
))
3894 return !aFrame
->GetBorderRadii(radii
) ||
3895 nsLayoutUtils::RoundedRectIntersectsRect(
3896 nsRect(aFrameToReferenceFrame
, aFrame
->GetSize()), radii
,
3900 // Returns TRUE if aContainedRect is guaranteed to be contained in
3901 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3902 // handled conservatively by returning FALSE in some situations where
3903 // a more thorough analysis could return TRUE.
3905 // See also RoundedRectIntersectsRect.
3906 static bool RoundedRectContainsRect(const nsRect
& aRoundedRect
,
3907 const nscoord aRadii
[8],
3908 const nsRect
& aContainedRect
) {
3909 nsRegion rgn
= nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect
, aRadii
,
3911 return rgn
.Contains(aContainedRect
);
3914 bool nsDisplayBackgroundImage::CanOptimizeToImageLayer(
3915 LayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
) {
3916 if (!mBackgroundStyle
) {
3920 // We currently can't handle tiled backgrounds.
3921 if (!mDestRect
.Contains(mFillRect
)) {
3925 // For 'contain' and 'cover', we allow any pixel of the image to be sampled
3926 // because there isn't going to be any spriting/atlasing going on.
3927 const nsStyleImageLayers::Layer
& layer
=
3928 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
3929 bool allowPartialImages
= layer
.mSize
.IsContain() || layer
.mSize
.IsCover();
3930 if (!allowPartialImages
&& !mFillRect
.Contains(mDestRect
)) {
3934 return nsDisplayImageContainer::CanOptimizeToImageLayer(aManager
, aBuilder
);
3937 nsRect
nsDisplayBackgroundImage::GetDestRect() const { return mDestRect
; }
3939 already_AddRefed
<imgIContainer
> nsDisplayBackgroundImage::GetImage() {
3940 nsCOMPtr
<imgIContainer
> image
= mImage
;
3941 return image
.forget();
3944 nsDisplayBackgroundImage::ImageLayerization
3945 nsDisplayBackgroundImage::ShouldCreateOwnLayer(nsDisplayListBuilder
* aBuilder
,
3946 LayerManager
* aManager
) {
3947 if (ForceActiveLayers()) {
3948 return WHENEVER_POSSIBLE
;
3951 nsIFrame
* backgroundStyleFrame
=
3952 nsCSSRendering::FindBackgroundStyleFrame(StyleFrame());
3953 if (ActiveLayerTracker::IsBackgroundPositionAnimated(aBuilder
,
3954 backgroundStyleFrame
)) {
3955 return WHENEVER_POSSIBLE
;
3958 if (StaticPrefs::layout_animated_image_layers_enabled() && mBackgroundStyle
) {
3959 const nsStyleImageLayers::Layer
& layer
=
3960 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
3961 const auto* image
= &layer
.mImage
;
3962 if (auto* request
= image
->GetImageRequest()) {
3963 nsCOMPtr
<imgIContainer
> image
;
3964 if (NS_SUCCEEDED(request
->GetImage(getter_AddRefs(image
))) && image
) {
3965 bool animated
= false;
3966 if (NS_SUCCEEDED(image
->GetAnimated(&animated
)) && animated
) {
3967 return WHENEVER_POSSIBLE
;
3973 if (nsLayoutUtils::GPUImageScalingEnabled() &&
3974 aManager
->IsCompositingCheap()) {
3975 return ONLY_FOR_SCALING
;
3978 return NO_LAYER_NEEDED
;
3981 static void CheckForBorderItem(nsDisplayItem
* aItem
, uint32_t& aFlags
) {
3982 nsDisplayItem
* nextItem
= aItem
->GetAbove();
3983 while (nextItem
&& nextItem
->GetType() == DisplayItemType::TYPE_BACKGROUND
) {
3984 nextItem
= nextItem
->GetAbove();
3986 if (nextItem
&& nextItem
->Frame() == aItem
->Frame() &&
3987 nextItem
->GetType() == DisplayItemType::TYPE_BORDER
) {
3988 aFlags
|= nsCSSRendering::PAINTBG_WILL_PAINT_BORDER
;
3992 LayerState
nsDisplayBackgroundImage::GetLayerState(
3993 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
3994 const ContainerLayerParameters
& aParameters
) {
3995 mImageFlags
= aBuilder
->GetBackgroundPaintFlags();
3996 CheckForBorderItem(this, mImageFlags
);
3998 ImageLayerization shouldLayerize
= ShouldCreateOwnLayer(aBuilder
, aManager
);
3999 if (shouldLayerize
== NO_LAYER_NEEDED
) {
4000 // We can skip the call to CanOptimizeToImageLayer if we don't want a
4002 return LayerState::LAYER_NONE
;
4005 if (CanOptimizeToImageLayer(aManager
, aBuilder
)) {
4006 if (shouldLayerize
== WHENEVER_POSSIBLE
) {
4007 return LayerState::LAYER_ACTIVE
;
4010 MOZ_ASSERT(shouldLayerize
== ONLY_FOR_SCALING
,
4011 "unhandled ImageLayerization value?");
4015 int32_t imageHeight
;
4016 mImage
->GetWidth(&imageWidth
);
4017 mImage
->GetHeight(&imageHeight
);
4018 NS_ASSERTION(imageWidth
!= 0 && imageHeight
!= 0, "Invalid image size!");
4020 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
4021 LayoutDeviceRect destRect
=
4022 LayoutDeviceRect::FromAppUnits(GetDestRect(), appUnitsPerDevPixel
);
4024 const LayerRect destLayerRect
= destRect
* aParameters
.Scale();
4026 // Calculate the scaling factor for the frame.
4027 const gfxSize scale
= gfxSize(destLayerRect
.width
/ imageWidth
,
4028 destLayerRect
.height
/ imageHeight
);
4030 if ((scale
.width
!= 1.0f
|| scale
.height
!= 1.0f
) &&
4031 (destLayerRect
.width
* destLayerRect
.height
>= 64 * 64)) {
4032 // Separate this image into a layer.
4033 // There's no point in doing this if we are not scaling at all or if the
4034 // target size is pretty small.
4035 return LayerState::LAYER_ACTIVE
;
4039 return LayerState::LAYER_NONE
;
4042 already_AddRefed
<Layer
> nsDisplayBackgroundImage::BuildLayer(
4043 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
4044 const ContainerLayerParameters
& aParameters
) {
4045 RefPtr
<ImageLayer
> layer
= static_cast<ImageLayer
*>(
4046 aManager
->GetLayerBuilder()->GetLeafLayerFor(aBuilder
, this));
4048 layer
= aManager
->CreateImageLayer();
4053 RefPtr
<ImageContainer
> imageContainer
= GetContainer(aManager
, aBuilder
);
4054 layer
->SetContainer(imageContainer
);
4055 ConfigureLayer(layer
, aParameters
);
4056 // ConfigureLayer doesn't know about our opacity, so apply it to layer here.
4057 layer
->SetOpacity(mOpacity
);
4058 return layer
.forget();
4061 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
4062 LayerManager
* aManager
, nsDisplayListBuilder
* aDisplayListBuilder
) {
4063 if (aDisplayListBuilder
) {
4064 mImageFlags
= aDisplayListBuilder
->GetBackgroundPaintFlags();
4067 return mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
].mClip
!=
4068 StyleGeometryBox::Text
&&
4069 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
4070 aManager
, *StyleFrame()->PresContext(), StyleFrame(),
4071 mBackgroundStyle
->StyleBackground(), mLayer
, mImageFlags
);
4074 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
4075 mozilla::wr::DisplayListBuilder
& aBuilder
,
4076 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
4077 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4078 nsDisplayListBuilder
* aDisplayListBuilder
) {
4079 if (!CanBuildWebRenderDisplayItems(aManager
->LayerManager(),
4080 aDisplayListBuilder
)) {
4084 CheckForBorderItem(this, mImageFlags
);
4085 nsCSSRendering::PaintBGParams params
=
4086 nsCSSRendering::PaintBGParams::ForSingleLayer(
4087 *StyleFrame()->PresContext(), GetPaintRect(), mBackgroundRect
,
4088 StyleFrame(), mImageFlags
, mLayer
, CompositionOp::OP_OVER
, mOpacity
);
4089 params
.bgClipRect
= &mBounds
;
4090 ImgDrawResult result
=
4091 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
4092 params
, aBuilder
, aResources
, aSc
, aManager
, this);
4093 if (result
== ImgDrawResult::NOT_SUPPORTED
) {
4097 nsDisplayBackgroundGeometry::UpdateDrawResult(this, result
);
4101 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder
* aBuilder
,
4102 const nsRect
& aRect
,
4103 HitTestState
* aState
,
4104 nsTArray
<nsIFrame
*>* aOutFrames
) {
4105 if (RoundedBorderIntersectsRect(mFrame
, ToReferenceFrame(), aRect
)) {
4106 aOutFrames
->AppendElement(mFrame
);
4110 bool nsDisplayBackgroundImage::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
4111 nsRegion
* aVisibleRegion
) {
4112 if (!nsDisplayImageContainer::ComputeVisibility(aBuilder
, aVisibleRegion
)) {
4116 // Return false if the background was propagated away from this
4117 // frame. We don't want this display item to show up and confuse
4119 return mBackgroundStyle
;
4122 static nsRect
GetInsideClipRect(const nsDisplayItem
* aItem
,
4123 StyleGeometryBox aClip
, const nsRect
& aRect
,
4124 const nsRect
& aBackgroundRect
) {
4125 if (aRect
.IsEmpty()) {
4129 nsIFrame
* frame
= aItem
->Frame();
4131 nsRect clipRect
= aBackgroundRect
;
4132 if (frame
->IsCanvasFrame()) {
4133 nsCanvasFrame
* canvasFrame
= static_cast<nsCanvasFrame
*>(frame
);
4134 clipRect
= canvasFrame
->CanvasArea() + aItem
->ToReferenceFrame();
4135 } else if (aClip
== StyleGeometryBox::PaddingBox
||
4136 aClip
== StyleGeometryBox::ContentBox
) {
4137 nsMargin border
= frame
->GetUsedBorder();
4138 if (aClip
== StyleGeometryBox::ContentBox
) {
4139 border
+= frame
->GetUsedPadding();
4141 border
.ApplySkipSides(frame
->GetSkipSides());
4142 clipRect
.Deflate(border
);
4145 return clipRect
.Intersect(aRect
);
4148 nsRegion
nsDisplayBackgroundImage::GetOpaqueRegion(
4149 nsDisplayListBuilder
* aBuilder
, bool* aSnap
) const {
4153 if (!mBackgroundStyle
|| mOpacity
!= 1.0f
) {
4159 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
4160 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
4161 // which expects frames to be sent to it in content order, not reverse
4162 // content order which we'll produce here.
4163 // Of course, if there's only one frame in the flow, it doesn't matter.
4164 if (mFrame
->StyleBorder()->mBoxDecorationBreak
==
4165 StyleBoxDecorationBreak::Clone
||
4166 (!mFrame
->GetPrevContinuation() && !mFrame
->GetNextContinuation())) {
4167 const nsStyleImageLayers::Layer
& layer
=
4168 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
4169 if (layer
.mImage
.IsOpaque() && layer
.mBlendMode
== StyleBlend::Normal
&&
4170 layer
.mRepeat
.mXRepeat
!= StyleImageLayerRepeat::Space
&&
4171 layer
.mRepeat
.mYRepeat
!= StyleImageLayerRepeat::Space
&&
4172 layer
.mClip
!= StyleGeometryBox::Text
) {
4173 result
= GetInsideClipRect(this, layer
.mClip
, mBounds
, mBackgroundRect
);
4180 Maybe
<nscolor
> nsDisplayBackgroundImage::IsUniform(
4181 nsDisplayListBuilder
* aBuilder
) const {
4182 if (!mBackgroundStyle
) {
4183 return Some(NS_RGBA(0, 0, 0, 0));
4188 nsRect
nsDisplayBackgroundImage::GetPositioningArea() const {
4189 if (!mBackgroundStyle
) {
4192 nsIFrame
* attachedToFrame
;
4193 bool transformedFixed
;
4194 return nsCSSRendering::ComputeImageLayerPositioningArea(
4195 mFrame
->PresContext(), mFrame
, mBackgroundRect
,
4196 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
],
4197 &attachedToFrame
, &transformedFixed
) +
4201 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
4203 if (!mBackgroundStyle
) {
4208 if (mFrame
->GetBorderRadii(radii
)) {
4209 // A change in the size of the positioning area might change the position
4210 // of the rounded corners.
4214 const nsStyleImageLayers::Layer
& layer
=
4215 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
4216 if (layer
.RenderingMightDependOnPositioningAreaSizeChange()) {
4222 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder
* aBuilder
,
4224 PaintInternal(aBuilder
, aCtx
, GetPaintRect(), &mBounds
);
4227 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder
* aBuilder
,
4229 const nsRect
& aBounds
,
4230 nsRect
* aClipRect
) {
4231 gfxContext
* ctx
= aCtx
;
4232 StyleGeometryBox clip
=
4233 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
].mClip
;
4235 if (clip
== StyleGeometryBox::Text
) {
4236 if (!GenerateAndPushTextMask(StyleFrame(), aCtx
, mBackgroundRect
,
4242 nsCSSRendering::PaintBGParams params
=
4243 nsCSSRendering::PaintBGParams::ForSingleLayer(
4244 *StyleFrame()->PresContext(), aBounds
, mBackgroundRect
, StyleFrame(),
4245 mImageFlags
, mLayer
, CompositionOp::OP_OVER
, mOpacity
);
4246 params
.bgClipRect
= aClipRect
;
4247 ImgDrawResult result
= nsCSSRendering::PaintStyleImageLayer(params
, *aCtx
);
4249 if (clip
== StyleGeometryBox::Text
) {
4250 ctx
->PopGroupAndBlend();
4253 nsDisplayBackgroundGeometry::UpdateDrawResult(this, result
);
4256 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
4257 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
4258 nsRegion
* aInvalidRegion
) const {
4259 if (!mBackgroundStyle
) {
4263 auto* geometry
= static_cast<const nsDisplayBackgroundGeometry
*>(aGeometry
);
4266 nsRect bounds
= GetBounds(aBuilder
, &snap
);
4267 nsRect positioningArea
= GetPositioningArea();
4268 if (positioningArea
.TopLeft() != geometry
->mPositioningArea
.TopLeft() ||
4269 (positioningArea
.Size() != geometry
->mPositioningArea
.Size() &&
4270 RenderingMightDependOnPositioningAreaSizeChange())) {
4271 // Positioning area changed in a way that could cause everything to change,
4272 // so invalidate everything (both old and new painting areas).
4273 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
4276 if (!mDestRect
.IsEqualInterior(geometry
->mDestRect
)) {
4277 // Dest area changed in a way that could cause everything to change,
4278 // so invalidate everything (both old and new painting areas).
4279 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
4282 if (aBuilder
->ShouldSyncDecodeImages()) {
4284 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
].mImage
;
4285 if (image
.IsImageRequestType() &&
4286 geometry
->ShouldInvalidateToSyncDecodeImages()) {
4287 aInvalidRegion
->Or(*aInvalidRegion
, bounds
);
4290 if (!bounds
.IsEqualInterior(geometry
->mBounds
)) {
4291 // Positioning area is unchanged, so invalidate just the change in the
4293 aInvalidRegion
->Xor(bounds
, geometry
->mBounds
);
4297 nsRect
nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder
* aBuilder
,
4298 bool* aSnap
) const {
4303 nsRect
nsDisplayBackgroundImage::GetBoundsInternal(
4304 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrameForBounds
) {
4305 // This allows nsDisplayTableBackgroundImage to change the frame used for
4306 // bounds calculation.
4307 nsIFrame
* frame
= aFrameForBounds
? aFrameForBounds
: mFrame
;
4309 nsPresContext
* presContext
= frame
->PresContext();
4311 if (!mBackgroundStyle
) {
4315 nsRect clipRect
= mBackgroundRect
;
4316 if (frame
->IsCanvasFrame()) {
4317 nsCanvasFrame
* canvasFrame
= static_cast<nsCanvasFrame
*>(frame
);
4318 clipRect
= canvasFrame
->CanvasArea() + ToReferenceFrame();
4320 const nsStyleImageLayers::Layer
& layer
=
4321 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
4322 return nsCSSRendering::GetBackgroundLayerRect(
4323 presContext
, frame
, mBackgroundRect
, clipRect
, layer
,
4324 aBuilder
->GetBackgroundPaintFlags());
4327 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
4328 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, const InitData
& aData
,
4329 nsIFrame
* aCellFrame
)
4330 : nsDisplayBackgroundImage(aBuilder
, aFrame
, aData
, aCellFrame
),
4331 mStyleFrame(aCellFrame
) {
4332 if (aBuilder
->IsRetainingDisplayList()) {
4333 mStyleFrame
->AddDisplayItem(this);
4337 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
4339 mStyleFrame
->RemoveDisplayItem(this);
4343 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect
& aRect
) const {
4344 bool result
= mStyleFrame
? mStyleFrame
->IsInvalid(aRect
) : false;
4345 aRect
+= ToReferenceFrame();
4349 nsDisplayThemedBackground::nsDisplayThemedBackground(
4350 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
4351 const nsRect
& aBackgroundRect
)
4352 : nsPaintedDisplayItem(aBuilder
, aFrame
), mBackgroundRect(aBackgroundRect
) {
4353 MOZ_COUNT_CTOR(nsDisplayThemedBackground
);
4356 void nsDisplayThemedBackground::Init(nsDisplayListBuilder
* aBuilder
) {
4357 const nsStyleDisplay
* disp
= StyleFrame()->StyleDisplay();
4358 mAppearance
= disp
->EffectiveAppearance();
4359 StyleFrame()->IsThemed(disp
, &mThemeTransparency
);
4361 // Perform necessary RegisterThemeGeometry
4362 nsITheme
* theme
= StyleFrame()->PresContext()->Theme();
4363 nsITheme::ThemeGeometryType type
=
4364 theme
->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance
);
4365 if (type
!= nsITheme::eThemeGeometryTypeUnknown
) {
4366 RegisterThemeGeometry(aBuilder
, this, StyleFrame(), type
);
4369 if (mAppearance
== StyleAppearance::MozWinBorderlessGlass
||
4370 mAppearance
== StyleAppearance::MozWinGlass
) {
4371 aBuilder
->SetGlassDisplayItem(this);
4374 mBounds
= GetBoundsInternal();
4377 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream
& aStream
) {
4378 aStream
<< " (themed, appearance:" << (int)mAppearance
<< ")";
4381 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder
* aBuilder
,
4382 const nsRect
& aRect
,
4383 HitTestState
* aState
,
4384 nsTArray
<nsIFrame
*>* aOutFrames
) {
4385 // Assume that any point in our background rect is a hit.
4386 if (mBackgroundRect
.Intersects(aRect
)) {
4387 aOutFrames
->AppendElement(mFrame
);
4391 nsRegion
nsDisplayThemedBackground::GetOpaqueRegion(
4392 nsDisplayListBuilder
* aBuilder
, bool* aSnap
) const {
4396 if (mThemeTransparency
== nsITheme::eOpaque
) {
4398 result
= mBackgroundRect
;
4403 Maybe
<nscolor
> nsDisplayThemedBackground::IsUniform(
4404 nsDisplayListBuilder
* aBuilder
) const {
4405 if (mAppearance
== StyleAppearance::MozWinBorderlessGlass
||
4406 mAppearance
== StyleAppearance::MozWinGlass
) {
4407 return Some(NS_RGBA(0, 0, 0, 0));
4412 nsRect
nsDisplayThemedBackground::GetPositioningArea() const {
4413 return mBackgroundRect
;
4416 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder
* aBuilder
,
4418 PaintInternal(aBuilder
, aCtx
, GetPaintRect(), nullptr);
4421 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder
* aBuilder
,
4423 const nsRect
& aBounds
,
4424 nsRect
* aClipRect
) {
4425 // XXXzw this ignores aClipRect.
4426 nsPresContext
* presContext
= StyleFrame()->PresContext();
4427 nsITheme
* theme
= presContext
->Theme();
4428 nsRect
drawing(mBackgroundRect
);
4429 theme
->GetWidgetOverflow(presContext
->DeviceContext(), StyleFrame(),
4430 mAppearance
, &drawing
);
4431 drawing
.IntersectRect(drawing
, aBounds
);
4432 theme
->DrawWidgetBackground(aCtx
, StyleFrame(), mAppearance
, mBackgroundRect
,
4436 bool nsDisplayThemedBackground::CreateWebRenderCommands(
4437 mozilla::wr::DisplayListBuilder
& aBuilder
,
4438 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
4439 const StackingContextHelper
& aSc
,
4440 mozilla::layers::RenderRootStateManager
* aManager
,
4441 nsDisplayListBuilder
* aDisplayListBuilder
) {
4442 nsITheme
* theme
= StyleFrame()->PresContext()->Theme();
4443 return theme
->CreateWebRenderCommandsForWidget(aBuilder
, aResources
, aSc
,
4444 aManager
, StyleFrame(),
4445 mAppearance
, mBackgroundRect
);
4448 bool nsDisplayThemedBackground::IsWindowActive() const {
4449 EventStates docState
= mFrame
->GetContent()->OwnerDoc()->GetDocumentState();
4450 return !docState
.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE
);
4453 void nsDisplayThemedBackground::ComputeInvalidationRegion(
4454 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
4455 nsRegion
* aInvalidRegion
) const {
4457 static_cast<const nsDisplayThemedBackgroundGeometry
*>(aGeometry
);
4460 nsRect bounds
= GetBounds(aBuilder
, &snap
);
4461 nsRect positioningArea
= GetPositioningArea();
4462 if (!positioningArea
.IsEqualInterior(geometry
->mPositioningArea
)) {
4463 // Invalidate everything (both old and new painting areas).
4464 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
4467 if (!bounds
.IsEqualInterior(geometry
->mBounds
)) {
4468 // Positioning area is unchanged, so invalidate just the change in the
4470 aInvalidRegion
->Xor(bounds
, geometry
->mBounds
);
4472 nsITheme
* theme
= StyleFrame()->PresContext()->Theme();
4473 if (theme
->WidgetAppearanceDependsOnWindowFocus(mAppearance
) &&
4474 IsWindowActive() != geometry
->mWindowIsActive
) {
4475 aInvalidRegion
->Or(*aInvalidRegion
, bounds
);
4479 nsRect
nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder
* aBuilder
,
4480 bool* aSnap
) const {
4485 nsRect
nsDisplayThemedBackground::GetBoundsInternal() {
4486 nsPresContext
* presContext
= mFrame
->PresContext();
4488 nsRect r
= mBackgroundRect
- ToReferenceFrame();
4489 presContext
->Theme()->GetWidgetOverflow(
4490 presContext
->DeviceContext(), mFrame
,
4491 mFrame
->StyleDisplay()->EffectiveAppearance(), &r
);
4492 return r
+ ToReferenceFrame();
4495 void nsDisplayImageContainer::ConfigureLayer(
4496 ImageLayer
* aLayer
, const ContainerLayerParameters
& aParameters
) {
4497 aLayer
->SetSamplingFilter(nsLayoutUtils::GetSamplingFilterForFrame(mFrame
));
4499 nsCOMPtr
<imgIContainer
> image
= GetImage();
4502 int32_t imageHeight
;
4503 image
->GetWidth(&imageWidth
);
4504 image
->GetHeight(&imageHeight
);
4505 NS_ASSERTION(imageWidth
!= 0 && imageHeight
!= 0, "Invalid image size!");
4507 if (imageWidth
> 0 && imageHeight
> 0) {
4508 // We're actually using the ImageContainer. Let our frame know that it
4509 // should consider itself to have painted successfully.
4510 UpdateDrawResult(ImgDrawResult::SUCCESS
);
4513 // It's possible (for example, due to downscale-during-decode) that the
4514 // ImageContainer this ImageLayer is holding has a different size from the
4515 // intrinsic size of the image. For this reason we compute the transform using
4516 // the ImageContainer's size rather than the image's intrinsic size.
4517 // XXX(seth): In reality, since the size of the ImageContainer may change
4518 // asynchronously, this is not enough. Bug 1183378 will provide a more
4519 // complete fix, but this solution is safe in more cases than simply relying
4520 // on the intrinsic size.
4521 IntSize containerSize
= aLayer
->GetContainer()
4522 ? aLayer
->GetContainer()->GetCurrentSize()
4523 : IntSize(imageWidth
, imageHeight
);
4525 const int32_t factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
4526 const LayoutDeviceRect
destRect(
4527 LayoutDeviceIntRect::FromAppUnitsToNearest(GetDestRect(), factor
));
4529 const LayoutDevicePoint p
= destRect
.TopLeft();
4530 Matrix transform
= Matrix::Translation(p
.x
+ aParameters
.mOffset
.x
,
4531 p
.y
+ aParameters
.mOffset
.y
);
4532 transform
.PreScale(destRect
.width
/ containerSize
.width
,
4533 destRect
.height
/ containerSize
.height
);
4534 aLayer
->SetBaseTransform(gfx::Matrix4x4::From2D(transform
));
4537 already_AddRefed
<ImageContainer
> nsDisplayImageContainer::GetContainer(
4538 LayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
) {
4539 nsCOMPtr
<imgIContainer
> image
= GetImage();
4541 MOZ_ASSERT_UNREACHABLE(
4542 "Must call CanOptimizeToImage() and get true "
4543 "before calling GetContainer()");
4547 uint32_t flags
= imgIContainer::FLAG_ASYNC_NOTIFY
;
4548 if (aBuilder
->ShouldSyncDecodeImages()) {
4549 flags
|= imgIContainer::FLAG_SYNC_DECODE
;
4552 RefPtr
<ImageContainer
> container
= image
->GetImageContainer(aManager
, flags
);
4553 if (!container
|| !container
->HasCurrentImage()) {
4557 return container
.forget();
4560 bool nsDisplayImageContainer::CanOptimizeToImageLayer(
4561 LayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
) {
4562 uint32_t flags
= aBuilder
->ShouldSyncDecodeImages()
4563 ? imgIContainer::FLAG_SYNC_DECODE
4564 : imgIContainer::FLAG_NONE
;
4566 nsCOMPtr
<imgIContainer
> image
= GetImage();
4571 if (!image
->IsImageContainerAvailable(aManager
, flags
)) {
4576 int32_t imageHeight
;
4577 image
->GetWidth(&imageWidth
);
4578 image
->GetHeight(&imageHeight
);
4580 if (imageWidth
== 0 || imageHeight
== 0) {
4581 NS_ASSERTION(false, "invalid image size");
4585 const int32_t factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
4586 const LayoutDeviceRect
destRect(
4587 LayoutDeviceIntRect::FromAppUnitsToNearest(GetDestRect(), factor
));
4589 // Calculate the scaling factor for the frame.
4590 const gfxSize scale
=
4591 gfxSize(destRect
.width
/ imageWidth
, destRect
.height
/ imageHeight
);
4593 if (scale
.width
< 0.34 || scale
.height
< 0.34) {
4594 // This would look awful as long as we can't use high-quality downscaling
4595 // for image layers (bug 803703), so don't turn this into an image layer.
4599 if (mFrame
->IsImageFrame() || mFrame
->IsImageControlFrame()) {
4600 // Image layer doesn't support draw focus ring for image map.
4601 nsImageFrame
* f
= static_cast<nsImageFrame
*>(mFrame
);
4602 if (f
->HasImageMap()) {
4610 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
4611 void nsDisplayReflowCount::Paint(nsDisplayListBuilder
* aBuilder
,
4613 mFrame
->PresShell()->PaintCount(mFrameName
, aCtx
, mFrame
->PresContext(),
4614 mFrame
, ToReferenceFrame(), mColor
);
4618 void nsDisplayBackgroundColor::ApplyOpacity(nsDisplayListBuilder
* aBuilder
,
4620 const DisplayItemClipChain
* aClip
) {
4621 NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
4622 mColor
.a
= mColor
.a
* aOpacity
;
4623 IntersectClip(aBuilder
, aClip
, false);
4626 bool nsDisplayBackgroundColor::CanApplyOpacity() const {
4627 // Don't apply opacity if the background color is animated since the color is
4628 // going to be changed on the compositor.
4629 return !EffectCompositor::HasAnimationsForCompositor(
4630 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
);
4633 LayerState
nsDisplayBackgroundColor::GetLayerState(
4634 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
4635 const ContainerLayerParameters
& aParameters
) {
4636 if (ForceActiveLayers() && !HasBackgroundClipText()) {
4637 return LayerState::LAYER_ACTIVE
;
4640 if (EffectCompositor::HasAnimationsForCompositor(
4641 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
)) {
4642 return LayerState::LAYER_ACTIVE_FORCE
;
4645 return LayerState::LAYER_NONE
;
4648 already_AddRefed
<Layer
> nsDisplayBackgroundColor::BuildLayer(
4649 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
4650 const ContainerLayerParameters
& aContainerParameters
) {
4651 if (mColor
== sRGBColor() &&
4652 !EffectCompositor::HasAnimationsForCompositor(
4653 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
)) {
4657 RefPtr
<ColorLayer
> layer
= static_cast<ColorLayer
*>(
4658 aManager
->GetLayerBuilder()->GetLeafLayerFor(aBuilder
, this));
4660 layer
= aManager
->CreateColorLayer();
4665 layer
->SetColor(ToDeviceColor(mColor
));
4667 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
4668 layer
->SetBounds(mBackgroundRect
.ToNearestPixels(appUnitsPerDevPixel
));
4669 layer
->SetBaseTransform(gfx::Matrix4x4::Translation(
4670 aContainerParameters
.mOffset
.x
, aContainerParameters
.mOffset
.y
, 0));
4672 // Both nsDisplayBackgroundColor and nsDisplayTableBackgroundColor use this
4673 // function, but only nsDisplayBackgroundColor supports compositor animations.
4674 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR
) {
4675 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
4676 layer
, aBuilder
, this, mFrame
, GetType());
4678 return layer
.forget();
4681 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
4682 mozilla::wr::DisplayListBuilder
& aBuilder
,
4683 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
4684 const StackingContextHelper
& aSc
,
4685 mozilla::layers::RenderRootStateManager
* aManager
,
4686 nsDisplayListBuilder
* aDisplayListBuilder
) {
4687 if (mColor
== sRGBColor() &&
4688 !EffectCompositor::HasAnimationsForCompositor(
4689 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
)) {
4693 if (HasBackgroundClipText()) {
4697 uint64_t animationsId
= 0;
4698 // We don't support background-color animations on table elements yet.
4699 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR
) {
4701 AddAnimationsForWebRender(this, aManager
, aDisplayListBuilder
);
4704 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
4705 mBackgroundRect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
4706 wr::LayoutRect r
= wr::ToLayoutRect(bounds
);
4709 wr::WrAnimationProperty prop
{
4710 wr::WrAnimationType::BackgroundColor
,
4713 aBuilder
.PushRectWithAnimation(r
, r
, !BackfaceIsHidden(),
4714 wr::ToColorF(ToDeviceColor(mColor
)), &prop
);
4716 aBuilder
.StartGroup(this);
4717 aBuilder
.PushRect(r
, r
, !BackfaceIsHidden(),
4718 wr::ToColorF(ToDeviceColor(mColor
)));
4719 aBuilder
.FinishGroup();
4725 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder
* aBuilder
,
4727 const DisplayItemClip
& aClip
) {
4728 MOZ_ASSERT(!HasBackgroundClipText());
4730 if (mColor
== sRGBColor()) {
4734 nsRect fillRect
= mBackgroundRect
;
4735 if (aClip
.HasClip()) {
4736 fillRect
.IntersectRect(fillRect
, aClip
.GetClipRect());
4739 DrawTarget
* dt
= aCtx
->GetDrawTarget();
4740 int32_t A2D
= mFrame
->PresContext()->AppUnitsPerDevPixel();
4741 Rect bounds
= ToRect(nsLayoutUtils::RectToGfxRect(fillRect
, A2D
));
4742 MaybeSnapToDevicePixels(bounds
, *dt
);
4743 ColorPattern
fill(ToDeviceColor(mColor
));
4745 if (aClip
.GetRoundedRectCount()) {
4746 MOZ_ASSERT(aClip
.GetRoundedRectCount() == 1);
4748 AutoTArray
<DisplayItemClip::RoundedRect
, 1> roundedRect
;
4749 aClip
.AppendRoundedRects(&roundedRect
);
4751 bool pushedClip
= false;
4752 if (!fillRect
.Contains(roundedRect
[0].mRect
)) {
4753 dt
->PushClipRect(bounds
);
4757 RectCornerRadii pixelRadii
;
4758 nsCSSRendering::ComputePixelRadii(roundedRect
[0].mRadii
, A2D
, &pixelRadii
);
4759 dt
->FillRoundedRect(
4760 RoundedRect(NSRectToSnappedRect(roundedRect
[0].mRect
, A2D
, *dt
),
4767 dt
->FillRect(bounds
, fill
);
4771 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder
* aBuilder
,
4773 if (mColor
== sRGBColor()) {
4778 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
4779 // results in a precision induced rounding issue that makes the rect one
4780 // pixel shorter in rare cases. Disabled in favor of the old code for now.
4781 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
4782 // reproduce the bug.
4785 // This new path does not include support for background-clip:text; need to
4786 // be fixed if/when we switch to this new code path.
4788 DrawTarget
& aDrawTarget
= *aCtx
->GetDrawTarget();
4790 Rect rect
= NSRectToSnappedRect(mBackgroundRect
,
4791 mFrame
->PresContext()->AppUnitsPerDevPixel(),
4793 ColorPattern
color(ToDeviceColor(mColor
));
4794 aDrawTarget
.FillRect(rect
, color
);
4796 gfxContext
* ctx
= aCtx
;
4797 gfxRect bounds
= nsLayoutUtils::RectToGfxRect(
4798 mBackgroundRect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
4800 if (HasBackgroundClipText()) {
4801 if (!GenerateAndPushTextMask(mFrame
, aCtx
, mBackgroundRect
, aBuilder
)) {
4805 ctx
->SetColor(mColor
);
4807 ctx
->SnappedRectangle(bounds
);
4809 ctx
->PopGroupAndBlend();
4813 ctx
->SetColor(mColor
);
4815 ctx
->SnappedRectangle(bounds
);
4820 nsRegion
nsDisplayBackgroundColor::GetOpaqueRegion(
4821 nsDisplayListBuilder
* aBuilder
, bool* aSnap
) const {
4824 if (mColor
.a
!= 1 ||
4825 // Even if the current alpha channel is 1, we treat this item as if it's
4826 // non-opaque if there is a background-color animation since the animation
4827 // might change the alpha channel.
4828 EffectCompositor::HasAnimationsForCompositor(
4829 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
)) {
4833 if (!mHasStyle
|| HasBackgroundClipText()) {
4838 return GetInsideClipRect(this, mBottomLayerClip
, mBackgroundRect
,
4842 Maybe
<nscolor
> nsDisplayBackgroundColor::IsUniform(
4843 nsDisplayListBuilder
* aBuilder
) const {
4844 return Some(mColor
.ToABGR());
4847 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder
* aBuilder
,
4848 const nsRect
& aRect
,
4849 HitTestState
* aState
,
4850 nsTArray
<nsIFrame
*>* aOutFrames
) {
4851 if (!RoundedBorderIntersectsRect(mFrame
, ToReferenceFrame(), aRect
)) {
4852 // aRect doesn't intersect our border-radius curve.
4856 aOutFrames
->AppendElement(mFrame
);
4859 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream
& aStream
) {
4860 aStream
<< " (rgba " << mColor
.r
<< "," << mColor
.g
<< "," << mColor
.b
<< ","
4862 aStream
<< " backgroundRect" << mBackgroundRect
;
4865 nsRect
nsDisplayOutline::GetBounds(nsDisplayListBuilder
* aBuilder
,
4866 bool* aSnap
) const {
4868 return mFrame
->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
4871 nsRect
nsDisplayOutline::GetInnerRect() const {
4872 nsRect
* savedOutlineInnerRect
=
4873 mFrame
->GetProperty(nsIFrame::OutlineInnerRectProperty());
4874 if (savedOutlineInnerRect
) {
4875 return *savedOutlineInnerRect
;
4878 // FIXME bug 1221888
4879 NS_ERROR("we should have saved a frame property");
4880 return nsRect(nsPoint(), mFrame
->GetSize());
4883 void nsDisplayOutline::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
4884 // TODO join outlines together
4885 MOZ_ASSERT(mFrame
->StyleOutline()->ShouldPaintOutline(),
4886 "Should have not created a nsDisplayOutline!");
4888 nsRect rect
= GetInnerRect() + ToReferenceFrame();
4889 nsPresContext
* pc
= mFrame
->PresContext();
4890 if (IsThemedOutline()) {
4891 rect
.Inflate(mFrame
->StyleOutline()->mOutlineOffset
.ToAppUnits());
4892 pc
->Theme()->DrawWidgetBackground(
4893 aCtx
, mFrame
, StyleAppearance::FocusOutline
, rect
, GetPaintRect());
4897 nsCSSRendering::PaintNonThemedOutline(pc
, *aCtx
, mFrame
, GetPaintRect(), rect
,
4901 bool nsDisplayOutline::IsThemedOutline() const {
4902 const auto& outlineStyle
= mFrame
->StyleOutline()->mOutlineStyle
;
4903 if (!outlineStyle
.IsAuto() ||
4904 !StaticPrefs::layout_css_outline_style_auto_enabled()) {
4908 nsPresContext
* pc
= mFrame
->PresContext();
4909 nsITheme
* theme
= pc
->Theme();
4910 return theme
->ThemeSupportsWidget(pc
, mFrame
, StyleAppearance::FocusOutline
);
4913 bool nsDisplayOutline::CreateWebRenderCommands(
4914 mozilla::wr::DisplayListBuilder
& aBuilder
,
4915 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
4916 const StackingContextHelper
& aSc
,
4917 mozilla::layers::RenderRootStateManager
* aManager
,
4918 nsDisplayListBuilder
* aDisplayListBuilder
) {
4919 nsPresContext
* pc
= mFrame
->PresContext();
4920 nsRect rect
= GetInnerRect() + ToReferenceFrame();
4921 if (IsThemedOutline()) {
4922 rect
.Inflate(mFrame
->StyleOutline()->mOutlineOffset
.ToAppUnits());
4923 return pc
->Theme()->CreateWebRenderCommandsForWidget(
4924 aBuilder
, aResources
, aSc
, aManager
, mFrame
,
4925 StyleAppearance::FocusOutline
, rect
);
4928 Maybe
<nsCSSBorderRenderer
> borderRenderer
=
4929 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4930 pc
, /* aDrawTarget = */ nullptr, mFrame
, GetPaintRect(), rect
,
4933 if (!borderRenderer
) {
4934 // No border renderer means "there is no outline".
4935 // Paint nothing and return success.
4939 borderRenderer
->CreateWebRenderCommands(this, aBuilder
, aResources
, aSc
);
4943 bool nsDisplayOutline::HasRadius() const {
4944 const auto& radius
=
4945 StaticPrefs::layout_css_outline_follows_border_radius_enabled()
4946 ? mFrame
->StyleBorder()->mBorderRadius
4947 : mFrame
->StyleOutline()->mOutlineRadius
;
4948 return !nsLayoutUtils::HasNonZeroCorner(radius
);
4951 bool nsDisplayOutline::IsInvisibleInRect(const nsRect
& aRect
) const {
4952 const nsStyleOutline
* outline
= mFrame
->StyleOutline();
4953 nsRect
borderBox(ToReferenceFrame(), mFrame
->GetSize());
4954 if (borderBox
.Contains(aRect
) && !HasRadius() &&
4955 outline
->mOutlineOffset
.ToCSSPixels() >= 0.0f
) {
4956 // aRect is entirely inside the border-rect, and the outline isn't rendered
4957 // inside the border-rect, so the outline is not visible.
4963 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder
* aBuilder
,
4964 const nsRect
& aRect
, HitTestState
* aState
,
4965 nsTArray
<nsIFrame
*>* aOutFrames
) {
4966 if (!RoundedBorderIntersectsRect(mFrame
, ToReferenceFrame(), aRect
)) {
4967 // aRect doesn't intersect our border-radius curve.
4971 aOutFrames
->AppendElement(mFrame
);
4974 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4975 mozilla::wr::DisplayListBuilder
& aBuilder
,
4976 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
4977 const StackingContextHelper
& aSc
,
4978 mozilla::layers::RenderRootStateManager
* aManager
,
4979 nsDisplayListBuilder
* aDisplayListBuilder
) {
4983 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4984 return mOverrideZIndex
? *mOverrideZIndex
: nsDisplayItem::ZIndex();
4987 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex
) {
4988 mOverrideZIndex
= Some(aZIndex
);
4991 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder
* aBuilder
,
4992 nsIFrame
* aCaretFrame
)
4993 : nsPaintedDisplayItem(aBuilder
, aCaretFrame
),
4994 mCaret(aBuilder
->GetCaret()),
4995 mBounds(aBuilder
->GetCaretRect() + ToReferenceFrame()) {
4996 MOZ_COUNT_CTOR(nsDisplayCaret
);
4997 // The presence of a caret doesn't change the overflow rect
4998 // of the owning frame, so the normal building rect might not
4999 // include the caret at all. We use MarkFrameForDisplay to ensure
5000 // we build this item, and here we override the building rect
5001 // to cover the pixels we're going to draw.
5002 SetBuildingRect(mBounds
);
5005 #ifdef NS_BUILD_REFCNT_LOGGING
5006 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret
); }
5009 nsRect
nsDisplayCaret::GetBounds(nsDisplayListBuilder
* aBuilder
,
5010 bool* aSnap
) const {
5012 // The caret returns a rect in the coordinates of mFrame.
5016 void nsDisplayCaret::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
5017 // Note: Because we exist, we know that the caret is visible, so we don't
5018 // need to check for the caret's visibility.
5019 mCaret
->PaintCaret(*aCtx
->GetDrawTarget(), mFrame
, ToReferenceFrame());
5022 bool nsDisplayCaret::CreateWebRenderCommands(
5023 mozilla::wr::DisplayListBuilder
& aBuilder
,
5024 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
5025 const StackingContextHelper
& aSc
,
5026 mozilla::layers::RenderRootStateManager
* aManager
,
5027 nsDisplayListBuilder
* aDisplayListBuilder
) {
5028 using namespace mozilla::layers
;
5029 int32_t contentOffset
;
5030 nsIFrame
* frame
= mCaret
->GetFrame(&contentOffset
);
5034 NS_ASSERTION(frame
== mFrame
, "We're referring different frame");
5036 int32_t appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
5040 mCaret
->ComputeCaretRects(frame
, contentOffset
, &caretRect
, &hookRect
);
5042 gfx::DeviceColor color
= ToDeviceColor(frame
->GetCaretColorAt(contentOffset
));
5043 LayoutDeviceRect devCaretRect
= LayoutDeviceRect::FromAppUnits(
5044 caretRect
+ ToReferenceFrame(), appUnitsPerDevPixel
);
5045 LayoutDeviceRect devHookRect
= LayoutDeviceRect::FromAppUnits(
5046 hookRect
+ ToReferenceFrame(), appUnitsPerDevPixel
);
5048 wr::LayoutRect caret
= wr::ToLayoutRect(devCaretRect
);
5049 wr::LayoutRect hook
= wr::ToLayoutRect(devHookRect
);
5051 // Note, WR will pixel snap anything that is layout aligned.
5052 aBuilder
.PushRect(caret
, caret
, !BackfaceIsHidden(), wr::ToColorF(color
));
5054 if (!devHookRect
.IsEmpty()) {
5055 aBuilder
.PushRect(hook
, hook
, !BackfaceIsHidden(), wr::ToColorF(color
));
5060 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder
* aBuilder
,
5062 : nsPaintedDisplayItem(aBuilder
, aFrame
) {
5063 MOZ_COUNT_CTOR(nsDisplayBorder
);
5065 mBounds
= CalculateBounds
<nsRect
>(*mFrame
->StyleBorder());
5068 bool nsDisplayBorder::IsInvisibleInRect(const nsRect
& aRect
) const {
5069 nsRect paddingRect
= GetPaddingRect();
5070 const nsStyleBorder
* styleBorder
;
5071 if (paddingRect
.Contains(aRect
) &&
5072 !(styleBorder
= mFrame
->StyleBorder())->IsBorderImageSizeAvailable() &&
5073 !nsLayoutUtils::HasNonZeroCorner(styleBorder
->mBorderRadius
)) {
5074 // aRect is entirely inside the content rect, and no part
5075 // of the border is rendered inside the content rect, so we are not
5077 // Skip this if there's a border-image (which draws a background
5078 // too) or if there is a border-radius (which makes the border draw
5086 nsDisplayItemGeometry
* nsDisplayBorder::AllocateGeometry(
5087 nsDisplayListBuilder
* aBuilder
) {
5088 return new nsDisplayBorderGeometry(this, aBuilder
);
5091 void nsDisplayBorder::ComputeInvalidationRegion(
5092 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
5093 nsRegion
* aInvalidRegion
) const {
5094 auto* geometry
= static_cast<const nsDisplayBorderGeometry
*>(aGeometry
);
5097 if (!geometry
->mBounds
.IsEqualInterior(GetBounds(aBuilder
, &snap
))) {
5098 // We can probably get away with only invalidating the difference
5099 // between the border and padding rects, but the XUL ui at least
5100 // is apparently painting a background with this?
5101 aInvalidRegion
->Or(GetBounds(aBuilder
, &snap
), geometry
->mBounds
);
5104 if (aBuilder
->ShouldSyncDecodeImages() &&
5105 geometry
->ShouldInvalidateToSyncDecodeImages()) {
5106 aInvalidRegion
->Or(*aInvalidRegion
, GetBounds(aBuilder
, &snap
));
5110 LayerState
nsDisplayBorder::GetLayerState(
5111 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
5112 const ContainerLayerParameters
& aParameters
) {
5113 return LayerState::LAYER_NONE
;
5116 bool nsDisplayBorder::CreateWebRenderCommands(
5117 mozilla::wr::DisplayListBuilder
& aBuilder
,
5118 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
5119 const StackingContextHelper
& aSc
,
5120 mozilla::layers::RenderRootStateManager
* aManager
,
5121 nsDisplayListBuilder
* aDisplayListBuilder
) {
5122 nsRect rect
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
5124 aBuilder
.StartGroup(this);
5125 ImgDrawResult drawResult
= nsCSSRendering::CreateWebRenderCommandsForBorder(
5126 this, mFrame
, rect
, aBuilder
, aResources
, aSc
, aManager
,
5127 aDisplayListBuilder
);
5129 if (drawResult
== ImgDrawResult::NOT_SUPPORTED
) {
5130 aBuilder
.CancelGroup(true);
5134 aBuilder
.FinishGroup();
5136 nsDisplayBorderGeometry::UpdateDrawResult(this, drawResult
);
5140 void nsDisplayBorder::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
5141 nsPoint offset
= ToReferenceFrame();
5143 PaintBorderFlags flags
= aBuilder
->ShouldSyncDecodeImages()
5144 ? PaintBorderFlags::SyncDecodeImages
5145 : PaintBorderFlags();
5147 ImgDrawResult result
= nsCSSRendering::PaintBorder(
5148 mFrame
->PresContext(), *aCtx
, mFrame
, GetPaintRect(),
5149 nsRect(offset
, mFrame
->GetSize()), mFrame
->Style(), flags
,
5150 mFrame
->GetSkipSides());
5152 nsDisplayBorderGeometry::UpdateDrawResult(this, result
);
5155 nsRect
nsDisplayBorder::GetBounds(nsDisplayListBuilder
* aBuilder
,
5156 bool* aSnap
) const {
5161 // Given a region, compute a conservative approximation to it as a list
5162 // of rectangles that aren't vertically adjacent (i.e., vertically
5163 // adjacent or overlapping rectangles are combined).
5164 // Right now this is only approximate, some vertically overlapping rectangles
5165 // aren't guaranteed to be combined.
5166 static void ComputeDisjointRectangles(const nsRegion
& aRegion
,
5167 nsTArray
<nsRect
>* aRects
) {
5168 nscoord accumulationMargin
= nsPresContext::CSSPixelsToAppUnits(25);
5171 for (auto iter
= aRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
5172 const nsRect
& r
= iter
.Get();
5173 if (accumulated
.IsEmpty()) {
5178 if (accumulated
.YMost() >= r
.y
- accumulationMargin
) {
5179 accumulated
.UnionRect(accumulated
, r
);
5181 aRects
->AppendElement(accumulated
);
5186 // Finish the in-flight rectangle, if there is one.
5187 if (!accumulated
.IsEmpty()) {
5188 aRects
->AppendElement(accumulated
);
5192 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder
* aBuilder
,
5194 nsPoint offset
= ToReferenceFrame();
5195 nsRect borderRect
= mFrame
->VisualBorderRectRelativeToSelf() + offset
;
5196 nsPresContext
* presContext
= mFrame
->PresContext();
5197 AutoTArray
<nsRect
, 10> rects
;
5198 ComputeDisjointRectangles(mVisibleRegion
, &rects
);
5200 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS
);
5202 for (uint32_t i
= 0; i
< rects
.Length(); ++i
) {
5203 nsCSSRendering::PaintBoxShadowOuter(presContext
, *aCtx
, mFrame
, borderRect
,
5204 rects
[i
], mOpacity
);
5208 nsRect
nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder
* aBuilder
,
5209 bool* aSnap
) const {
5214 nsRect
nsDisplayBoxShadowOuter::GetBoundsInternal() {
5215 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame
, mFrame
->GetSize()) +
5219 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect
& aRect
) const {
5220 nsPoint origin
= ToReferenceFrame();
5221 nsRect
frameRect(origin
, mFrame
->GetSize());
5222 if (!frameRect
.Contains(aRect
)) {
5226 // the visible region is entirely inside the border-rect, and box shadows
5227 // never render within the border-rect (unless there's a border radius).
5228 nscoord twipsRadii
[8];
5229 bool hasBorderRadii
= mFrame
->GetBorderRadii(twipsRadii
);
5230 if (!hasBorderRadii
) {
5234 return RoundedRectContainsRect(frameRect
, twipsRadii
, aRect
);
5237 bool nsDisplayBoxShadowOuter::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
5238 nsRegion
* aVisibleRegion
) {
5239 if (!nsPaintedDisplayItem::ComputeVisibility(aBuilder
, aVisibleRegion
)) {
5243 mVisibleRegion
.And(*aVisibleRegion
, GetPaintRect());
5247 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() {
5248 auto shadows
= mFrame
->StyleEffects()->mBoxShadow
.AsSpan();
5249 if (shadows
.IsEmpty()) {
5253 bool hasBorderRadius
;
5254 // We don't support native themed things yet like box shadows around
5257 // TODO(emilio): The non-native theme could provide the right rect+radius
5258 // instead relatively painlessly, if we find this causes performance issues or
5260 if (nsCSSRendering::HasBoxShadowNativeTheme(mFrame
, hasBorderRadius
)) {
5267 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
5268 mozilla::wr::DisplayListBuilder
& aBuilder
,
5269 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
5270 const StackingContextHelper
& aSc
,
5271 mozilla::layers::RenderRootStateManager
* aManager
,
5272 nsDisplayListBuilder
* aDisplayListBuilder
) {
5273 if (!CanBuildWebRenderDisplayItems()) {
5277 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
5278 nsPoint offset
= ToReferenceFrame();
5279 nsRect borderRect
= mFrame
->VisualBorderRectRelativeToSelf() + offset
;
5280 AutoTArray
<nsRect
, 10> rects
;
5282 nsRect bounds
= GetBounds(aDisplayListBuilder
, &snap
);
5283 ComputeDisjointRectangles(bounds
, &rects
);
5285 bool hasBorderRadius
;
5287 nsCSSRendering::HasBoxShadowNativeTheme(mFrame
, hasBorderRadius
);
5289 // Don't need the full size of the shadow rect like we do in
5290 // nsCSSRendering since WR takes care of calculations for blur
5291 // and spread radius.
5293 nsCSSRendering::GetShadowRect(borderRect
, nativeTheme
, mFrame
);
5295 RectCornerRadii borderRadii
;
5296 if (hasBorderRadius
) {
5297 hasBorderRadius
= nsCSSRendering::GetBorderRadii(frameRect
, borderRect
,
5298 mFrame
, borderRadii
);
5301 // Everything here is in app units, change to device units.
5302 for (uint32_t i
= 0; i
< rects
.Length(); ++i
) {
5303 LayoutDeviceRect clipRect
=
5304 LayoutDeviceRect::FromAppUnits(rects
[i
], appUnitsPerDevPixel
);
5305 auto shadows
= mFrame
->StyleEffects()->mBoxShadow
.AsSpan();
5306 MOZ_ASSERT(!shadows
.IsEmpty());
5308 for (auto& shadow
: Reversed(shadows
)) {
5314 float(shadow
.base
.blur
.ToAppUnits()) / float(appUnitsPerDevPixel
);
5315 gfx::sRGBColor shadowColor
=
5316 nsCSSRendering::GetShadowColor(shadow
.base
, mFrame
, mOpacity
);
5318 // We don't move the shadow rect here since WR does it for us
5319 // Now translate everything to device pixels.
5320 const nsRect
& shadowRect
= frameRect
;
5321 LayoutDevicePoint shadowOffset
= LayoutDevicePoint::FromAppUnits(
5322 nsPoint(shadow
.base
.horizontal
.ToAppUnits(),
5323 shadow
.base
.vertical
.ToAppUnits()),
5324 appUnitsPerDevPixel
);
5326 LayoutDeviceRect deviceBox
=
5327 LayoutDeviceRect::FromAppUnits(shadowRect
, appUnitsPerDevPixel
);
5328 wr::LayoutRect deviceBoxRect
= wr::ToLayoutRect(deviceBox
);
5329 wr::LayoutRect deviceClipRect
= wr::ToLayoutRect(clipRect
);
5331 LayoutDeviceSize zeroSize
;
5332 wr::BorderRadius borderRadius
=
5333 wr::ToBorderRadius(zeroSize
, zeroSize
, zeroSize
, zeroSize
);
5334 if (hasBorderRadius
) {
5335 borderRadius
= wr::ToBorderRadius(
5336 LayoutDeviceSize::FromUnknownSize(borderRadii
.TopLeft()),
5337 LayoutDeviceSize::FromUnknownSize(borderRadii
.TopRight()),
5338 LayoutDeviceSize::FromUnknownSize(borderRadii
.BottomLeft()),
5339 LayoutDeviceSize::FromUnknownSize(borderRadii
.BottomRight()));
5342 float spreadRadius
=
5343 float(shadow
.spread
.ToAppUnits()) / float(appUnitsPerDevPixel
);
5345 aBuilder
.PushBoxShadow(deviceBoxRect
, deviceClipRect
, !BackfaceIsHidden(),
5346 deviceBoxRect
, wr::ToLayoutVector2D(shadowOffset
),
5347 wr::ToColorF(ToDeviceColor(shadowColor
)),
5348 blurRadius
, spreadRadius
, borderRadius
,
5349 wr::BoxShadowClipMode::Outset
);
5356 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
5357 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
5358 nsRegion
* aInvalidRegion
) const {
5360 static_cast<const nsDisplayBoxShadowOuterGeometry
*>(aGeometry
);
5362 if (!geometry
->mBounds
.IsEqualInterior(GetBounds(aBuilder
, &snap
)) ||
5363 !geometry
->mBorderRect
.IsEqualInterior(GetBorderRect()) ||
5364 mOpacity
!= geometry
->mOpacity
) {
5365 nsRegion oldShadow
, newShadow
;
5366 nscoord dontCare
[8];
5367 bool hasBorderRadius
= mFrame
->GetBorderRadii(dontCare
);
5368 if (hasBorderRadius
) {
5369 // If we have rounded corners then we need to invalidate the frame area
5370 // too since we paint into it.
5371 oldShadow
= geometry
->mBounds
;
5372 newShadow
= GetBounds(aBuilder
, &snap
);
5374 oldShadow
.Sub(geometry
->mBounds
, geometry
->mBorderRect
);
5375 newShadow
.Sub(GetBounds(aBuilder
, &snap
), GetBorderRect());
5377 aInvalidRegion
->Or(oldShadow
, newShadow
);
5381 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder
* aBuilder
,
5383 nsPoint offset
= ToReferenceFrame();
5384 nsRect borderRect
= nsRect(offset
, mFrame
->GetSize());
5385 nsPresContext
* presContext
= mFrame
->PresContext();
5386 AutoTArray
<nsRect
, 10> rects
;
5387 ComputeDisjointRectangles(mVisibleRegion
, &rects
);
5389 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS
);
5391 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
5392 gfxContext
* gfx
= aCtx
;
5393 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
5395 for (uint32_t i
= 0; i
< rects
.Length(); ++i
) {
5397 gfx
->Clip(NSRectToSnappedRect(rects
[i
], appUnitsPerDevPixel
, *drawTarget
));
5398 nsCSSRendering::PaintBoxShadowInner(presContext
, *aCtx
, mFrame
, borderRect
);
5403 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
5404 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
5405 const nsPoint
& aReferenceOffset
) {
5406 auto shadows
= aFrame
->StyleEffects()->mBoxShadow
.AsSpan();
5407 if (shadows
.IsEmpty()) {
5408 // Means we don't have to paint anything
5412 bool hasBorderRadius
;
5414 nsCSSRendering::HasBoxShadowNativeTheme(aFrame
, hasBorderRadius
);
5416 // We don't support native themed things yet like box shadows around
5426 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
5427 mozilla::wr::DisplayListBuilder
& aBuilder
, const StackingContextHelper
& aSc
,
5428 nsRegion
& aVisibleRegion
, nsIFrame
* aFrame
, const nsRect
& aBorderRect
) {
5429 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame
)) {
5433 int32_t appUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
5435 AutoTArray
<nsRect
, 10> rects
;
5436 ComputeDisjointRectangles(aVisibleRegion
, &rects
);
5438 auto shadows
= aFrame
->StyleEffects()->mBoxShadow
.AsSpan();
5440 for (uint32_t i
= 0; i
< rects
.Length(); ++i
) {
5441 LayoutDeviceRect clipRect
=
5442 LayoutDeviceRect::FromAppUnits(rects
[i
], appUnitsPerDevPixel
);
5444 for (auto& shadow
: Reversed(shadows
)) {
5445 if (!shadow
.inset
) {
5450 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame
, aBorderRect
);
5451 RectCornerRadii innerRadii
;
5452 nsCSSRendering::GetShadowInnerRadii(aFrame
, aBorderRect
, innerRadii
);
5454 // Now translate everything to device pixels.
5455 LayoutDeviceRect deviceBoxRect
=
5456 LayoutDeviceRect::FromAppUnits(shadowRect
, appUnitsPerDevPixel
);
5457 wr::LayoutRect deviceClipRect
= wr::ToLayoutRect(clipRect
);
5458 sRGBColor shadowColor
=
5459 nsCSSRendering::GetShadowColor(shadow
.base
, aFrame
, 1.0);
5461 LayoutDevicePoint shadowOffset
= LayoutDevicePoint::FromAppUnits(
5462 nsPoint(shadow
.base
.horizontal
.ToAppUnits(),
5463 shadow
.base
.vertical
.ToAppUnits()),
5464 appUnitsPerDevPixel
);
5467 float(shadow
.base
.blur
.ToAppUnits()) / float(appUnitsPerDevPixel
);
5469 wr::BorderRadius borderRadius
= wr::ToBorderRadius(
5470 LayoutDeviceSize::FromUnknownSize(innerRadii
.TopLeft()),
5471 LayoutDeviceSize::FromUnknownSize(innerRadii
.TopRight()),
5472 LayoutDeviceSize::FromUnknownSize(innerRadii
.BottomLeft()),
5473 LayoutDeviceSize::FromUnknownSize(innerRadii
.BottomRight()));
5474 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
5475 float spreadRadius
=
5476 float(shadow
.spread
.ToAppUnits()) / float(appUnitsPerDevPixel
);
5478 aBuilder
.PushBoxShadow(
5479 wr::ToLayoutRect(deviceBoxRect
), deviceClipRect
,
5480 !aFrame
->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect
),
5481 wr::ToLayoutVector2D(shadowOffset
),
5482 wr::ToColorF(ToDeviceColor(shadowColor
)), blurRadius
, spreadRadius
,
5483 borderRadius
, wr::BoxShadowClipMode::Inset
);
5488 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
5489 mozilla::wr::DisplayListBuilder
& aBuilder
,
5490 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
5491 const StackingContextHelper
& aSc
,
5492 mozilla::layers::RenderRootStateManager
* aManager
,
5493 nsDisplayListBuilder
* aDisplayListBuilder
) {
5494 if (!CanCreateWebRenderCommands(aDisplayListBuilder
, mFrame
,
5495 ToReferenceFrame())) {
5500 nsRegion visible
= GetBounds(aDisplayListBuilder
, &snap
);
5501 nsPoint offset
= ToReferenceFrame();
5502 nsRect borderRect
= nsRect(offset
, mFrame
->GetSize());
5503 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
5504 aBuilder
, aSc
, visible
, mFrame
, borderRect
);
5509 bool nsDisplayBoxShadowInner::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
5510 nsRegion
* aVisibleRegion
) {
5511 if (!nsPaintedDisplayItem::ComputeVisibility(aBuilder
, aVisibleRegion
)) {
5515 mVisibleRegion
.And(*aVisibleRegion
, GetPaintRect());
5519 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder
* aBuilder
,
5520 nsIFrame
* aFrame
, nsDisplayList
* aList
)
5521 : nsDisplayWrapList(aBuilder
, aFrame
, aList
,
5522 aBuilder
->CurrentActiveScrolledRoot(), false) {}
5524 nsDisplayWrapList::nsDisplayWrapList(
5525 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5526 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aClearClipChain
)
5527 : nsPaintedDisplayItem(aBuilder
, aFrame
, aActiveScrolledRoot
),
5528 mFrameActiveScrolledRoot(aBuilder
->CurrentActiveScrolledRoot()),
5530 mHasZIndexOverride(false),
5531 mClearingClipChain(aClearClipChain
) {
5532 MOZ_COUNT_CTOR(nsDisplayWrapList
);
5534 mBaseBuildingRect
= GetBuildingRect();
5537 mListPtr
->AppendToTop(aList
);
5538 nsDisplayWrapList::UpdateBounds(aBuilder
);
5541 if (!aFrame
|| !aFrame
->IsTransformed()) {
5545 nsDisplayItem
* i
= mListPtr
->GetBottom();
5547 (!i
->GetAbove() || i
->GetType() == DisplayItemType::TYPE_TRANSFORM
) &&
5548 i
->Frame() == mFrame
) {
5549 MOZ_ASSERT(mReferenceFrame
== i
->ReferenceFrame());
5554 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder
* aBuilder
,
5555 nsIFrame
* aFrame
, nsDisplayItem
* aItem
)
5556 : nsPaintedDisplayItem(aBuilder
, aFrame
,
5557 aBuilder
->CurrentActiveScrolledRoot()),
5559 mHasZIndexOverride(false) {
5560 MOZ_COUNT_CTOR(nsDisplayWrapList
);
5562 mBaseBuildingRect
= GetBuildingRect();
5565 mListPtr
->AppendToTop(aItem
);
5566 nsDisplayWrapList::UpdateBounds(aBuilder
);
5568 if (!aFrame
|| !aFrame
->IsTransformed()) {
5572 // See the previous nsDisplayWrapList constructor
5573 if (aItem
->Frame() == aFrame
) {
5574 mReferenceFrame
= aItem
->ReferenceFrame();
5575 mToReferenceFrame
= aItem
->ToReferenceFrame();
5578 nsRect visible
= aBuilder
->GetVisibleRect() +
5579 aBuilder
->GetCurrentFrameOffsetToReferenceFrame();
5581 SetBuildingRect(visible
);
5584 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList
); }
5586 void nsDisplayWrapList::MergeDisplayListFromItem(
5587 nsDisplayListBuilder
* aBuilder
, const nsDisplayWrapList
* aItem
) {
5588 const nsDisplayWrapList
* wrappedItem
= aItem
->AsDisplayWrapList();
5589 MOZ_ASSERT(wrappedItem
);
5591 // Create a new nsDisplayWrapList using a copy-constructor. This is done
5592 // to preserve the information about bounds.
5593 nsDisplayWrapList
* wrapper
=
5594 new (aBuilder
) nsDisplayWrapper(aBuilder
, *wrappedItem
);
5595 wrapper
->SetType(nsDisplayWrapper::ItemType());
5596 MOZ_ASSERT(wrapper
);
5598 // Set the display list pointer of the new wrapper item to the display list
5599 // of the wrapped item.
5600 wrapper
->mListPtr
= wrappedItem
->mListPtr
;
5602 mListPtr
->AppendToBottom(wrapper
);
5605 void nsDisplayWrapList::HitTest(nsDisplayListBuilder
* aBuilder
,
5606 const nsRect
& aRect
, HitTestState
* aState
,
5607 nsTArray
<nsIFrame
*>* aOutFrames
) {
5608 mListPtr
->HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
5611 nsRect
nsDisplayWrapList::GetBounds(nsDisplayListBuilder
* aBuilder
,
5612 bool* aSnap
) const {
5617 bool nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
5618 nsRegion
* aVisibleRegion
) {
5619 return ::ComputeClippedVisibilityForSubList(aBuilder
, aVisibleRegion
,
5620 GetChildren(), GetPaintRect());
5623 nsRegion
nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
5624 bool* aSnap
) const {
5627 return ::GetOpaqueRegion(aBuilder
, GetChildren(), GetBounds(aBuilder
, &snap
));
5630 Maybe
<nscolor
> nsDisplayWrapList::IsUniform(
5631 nsDisplayListBuilder
* aBuilder
) const {
5632 // We could try to do something but let's conservatively just return Nothing.
5636 void nsDisplayWrapper::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
5637 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
5641 * Returns true if all descendant display items can be placed in the same
5642 * PaintedLayer --- GetLayerState returns LayerState::LAYER_INACTIVE or
5643 * LayerState::LAYER_NONE, and they all have the expected animated geometry
5646 static LayerState
RequiredLayerStateForChildren(
5647 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
5648 const ContainerLayerParameters
& aParameters
, const nsDisplayList
& aList
,
5649 const AnimatedGeometryRoot
* aExpectedAGRForChildren
,
5650 const ActiveScrolledRoot
* aExpectedASRForChildren
) {
5651 LayerState result
= LayerState::LAYER_INACTIVE
;
5652 for (nsDisplayItem
* i
: aList
) {
5653 if (result
== LayerState::LAYER_INACTIVE
&&
5654 (i
->GetAnimatedGeometryRoot() != aExpectedAGRForChildren
||
5655 i
->GetActiveScrolledRoot() != aExpectedASRForChildren
)) {
5656 result
= LayerState::LAYER_ACTIVE
;
5659 LayerState state
= i
->GetLayerState(aBuilder
, aManager
, aParameters
);
5660 if (state
== LayerState::LAYER_ACTIVE
&&
5661 (i
->GetType() == DisplayItemType::TYPE_BLEND_MODE
||
5662 i
->GetType() == DisplayItemType::TYPE_TABLE_BLEND_MODE
)) {
5663 // nsDisplayBlendMode always returns LayerState::LAYER_ACTIVE to ensure
5664 // that the blending operation happens in the intermediate surface of its
5665 // parent display item (usually an nsDisplayBlendContainer). But this does
5666 // not mean that it needs all its ancestor display items to become active.
5667 // So we ignore its layer state and look at its children instead.
5668 state
= RequiredLayerStateForChildren(
5669 aBuilder
, aManager
, aParameters
,
5670 *i
->GetSameCoordinateSystemChildren(), i
->GetAnimatedGeometryRoot(),
5671 i
->GetActiveScrolledRoot());
5673 if ((state
== LayerState::LAYER_ACTIVE
||
5674 state
== LayerState::LAYER_ACTIVE_FORCE
) &&
5678 if (state
== LayerState::LAYER_ACTIVE_EMPTY
&& state
> result
) {
5679 result
= LayerState::LAYER_ACTIVE_FORCE
;
5681 if (state
== LayerState::LAYER_NONE
) {
5682 nsDisplayList
* list
= i
->GetSameCoordinateSystemChildren();
5684 LayerState childState
= RequiredLayerStateForChildren(
5685 aBuilder
, aManager
, aParameters
, *list
, aExpectedAGRForChildren
,
5686 aExpectedASRForChildren
);
5687 if (childState
> result
) {
5688 result
= childState
;
5696 nsRect
nsDisplayWrapList::GetComponentAlphaBounds(
5697 nsDisplayListBuilder
* aBuilder
) const {
5698 return mListPtr
->GetComponentAlphaBounds(aBuilder
);
5701 bool nsDisplayWrapList::CreateWebRenderCommands(
5702 mozilla::wr::DisplayListBuilder
& aBuilder
,
5703 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
5704 const StackingContextHelper
& aSc
,
5705 mozilla::layers::RenderRootStateManager
* aManager
,
5706 nsDisplayListBuilder
* aDisplayListBuilder
) {
5707 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
5708 GetChildren(), this, aDisplayListBuilder
, aSc
, aBuilder
, aResources
);
5712 static nsresult
WrapDisplayList(nsDisplayListBuilder
* aBuilder
,
5713 nsIFrame
* aFrame
, nsDisplayList
* aList
,
5714 nsDisplayItemWrapper
* aWrapper
) {
5715 if (!aList
->GetTop()) {
5718 nsDisplayItem
* item
= aWrapper
->WrapList(aBuilder
, aFrame
, aList
);
5720 return NS_ERROR_OUT_OF_MEMORY
;
5722 // aList was emptied
5723 aList
->AppendToTop(item
);
5727 static nsresult
WrapEachDisplayItem(nsDisplayListBuilder
* aBuilder
,
5728 nsDisplayList
* aList
,
5729 nsDisplayItemWrapper
* aWrapper
) {
5730 nsDisplayList newList
;
5731 nsDisplayItem
* item
;
5732 while ((item
= aList
->RemoveBottom())) {
5733 item
= aWrapper
->WrapItem(aBuilder
, item
);
5735 return NS_ERROR_OUT_OF_MEMORY
;
5737 newList
.AppendToTop(item
);
5739 // aList was emptied
5740 aList
->AppendToTop(&newList
);
5744 nsresult
nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder
* aBuilder
,
5746 const nsDisplayListSet
& aIn
,
5747 const nsDisplayListSet
& aOut
) {
5748 nsresult rv
= WrapListsInPlace(aBuilder
, aFrame
, aIn
);
5749 NS_ENSURE_SUCCESS(rv
, rv
);
5751 if (&aOut
== &aIn
) {
5754 aOut
.BorderBackground()->AppendToTop(aIn
.BorderBackground());
5755 aOut
.BlockBorderBackgrounds()->AppendToTop(aIn
.BlockBorderBackgrounds());
5756 aOut
.Floats()->AppendToTop(aIn
.Floats());
5757 aOut
.Content()->AppendToTop(aIn
.Content());
5758 aOut
.PositionedDescendants()->AppendToTop(aIn
.PositionedDescendants());
5759 aOut
.Outlines()->AppendToTop(aIn
.Outlines());
5763 nsresult
nsDisplayItemWrapper::WrapListsInPlace(
5764 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
5765 const nsDisplayListSet
& aLists
) {
5767 if (WrapBorderBackground()) {
5768 // Our border-backgrounds are in-flow
5769 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.BorderBackground(), this);
5770 NS_ENSURE_SUCCESS(rv
, rv
);
5772 // Our block border-backgrounds are in-flow
5773 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.BlockBorderBackgrounds(), this);
5774 NS_ENSURE_SUCCESS(rv
, rv
);
5775 // The floats are not in flow
5776 rv
= WrapEachDisplayItem(aBuilder
, aLists
.Floats(), this);
5777 NS_ENSURE_SUCCESS(rv
, rv
);
5778 // Our child content is in flow
5779 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.Content(), this);
5780 NS_ENSURE_SUCCESS(rv
, rv
);
5781 // The positioned descendants may not be in-flow
5782 rv
= WrapEachDisplayItem(aBuilder
, aLists
.PositionedDescendants(), this);
5783 NS_ENSURE_SUCCESS(rv
, rv
);
5784 // The outlines may not be in-flow
5785 return WrapEachDisplayItem(aBuilder
, aLists
.Outlines(), this);
5788 nsDisplayOpacity::nsDisplayOpacity(
5789 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5790 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aForEventsOnly
,
5791 bool aNeedsActiveLayer
)
5792 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
5793 mOpacity(aFrame
->StyleEffects()->mOpacity
),
5794 mForEventsOnly(aForEventsOnly
),
5795 mNeedsActiveLayer(aNeedsActiveLayer
),
5796 mChildOpacityState(ChildOpacityState::Unknown
) {
5797 MOZ_COUNT_CTOR(nsDisplayOpacity
);
5798 mState
.mOpacity
= mOpacity
;
5801 void nsDisplayOpacity::HitTest(nsDisplayListBuilder
* aBuilder
,
5802 const nsRect
& aRect
,
5803 nsDisplayItem::HitTestState
* aState
,
5804 nsTArray
<nsIFrame
*>* aOutFrames
) {
5805 AutoRestore
<float> opacity(aState
->mCurrentOpacity
);
5806 aState
->mCurrentOpacity
*= mOpacity
;
5808 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
5809 // only consider fully opaque items? Or make this configurable somehow?
5810 if (aBuilder
->HitTestIsForVisibility() && mOpacity
== 0.0f
) {
5813 nsDisplayWrapList::HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
5816 nsRegion
nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
5817 bool* aSnap
) const {
5819 // The only time where mOpacity == 1.0 should be when we have will-change.
5820 // We could report this as opaque then but when the will-change value starts
5821 // animating the element would become non opaque and could cause repaints.
5825 // nsDisplayOpacity uses layers for rendering
5826 already_AddRefed
<Layer
> nsDisplayOpacity::BuildLayer(
5827 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
5828 const ContainerLayerParameters
& aContainerParameters
) {
5829 MOZ_ASSERT(mChildOpacityState
!= ChildOpacityState::Applied
);
5831 ContainerLayerParameters params
= aContainerParameters
;
5832 RefPtr
<Layer
> container
= aManager
->GetLayerBuilder()->BuildContainerLayerFor(
5833 aBuilder
, aManager
, mFrame
, this, &mList
, params
, nullptr,
5834 FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR
);
5839 container
->SetOpacity(mOpacity
);
5840 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
5841 container
, aBuilder
, this, mFrame
, GetType());
5842 return container
.forget();
5845 void nsDisplayOpacity::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
5846 if (GetOpacity() == 0.0f
) {
5850 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
5852 aCtx
->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr,
5853 mozilla::gfx::Matrix());
5854 GetChildren()->Paint(aBuilder
, aCtx
,
5855 mFrame
->PresContext()->AppUnitsPerDevPixel());
5856 aCtx
->GetDrawTarget()->PopLayer();
5860 * This doesn't take into account layer scaling --- the layer may be
5861 * rendered at a higher (or lower) resolution, affecting the retained layer
5862 * size --- but this should be good enough.
5864 static bool IsItemTooSmallForActiveLayer(nsIFrame
* aFrame
) {
5865 nsIntRect visibleDevPixels
=
5866 aFrame
->InkOverflowRectRelativeToSelf().ToOutsidePixels(
5867 aFrame
->PresContext()->AppUnitsPerDevPixel());
5868 return visibleDevPixels
.Size() <
5869 nsIntSize(StaticPrefs::layout_min_active_layer_size(),
5870 StaticPrefs::layout_min_active_layer_size());
5874 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder
* aBuilder
,
5876 bool aEnforceMinimumSize
) {
5877 if (EffectCompositor::HasAnimationsForCompositor(
5878 aFrame
, DisplayItemType::TYPE_OPACITY
) ||
5879 (ActiveLayerTracker::IsStyleAnimated(
5880 aBuilder
, aFrame
, nsCSSPropertyIDSet::OpacityProperties()) &&
5881 !(aEnforceMinimumSize
&& IsItemTooSmallForActiveLayer(aFrame
)))) {
5887 void nsDisplayOpacity::ApplyOpacity(nsDisplayListBuilder
* aBuilder
,
5889 const DisplayItemClipChain
* aClip
) {
5890 NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
5891 mOpacity
= mOpacity
* aOpacity
;
5892 IntersectClip(aBuilder
, aClip
, false);
5895 bool nsDisplayOpacity::CanApplyOpacity() const {
5896 return !EffectCompositor::HasAnimationsForCompositor(
5897 mFrame
, DisplayItemType::TYPE_OPACITY
);
5900 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
5901 // children that don't overlap and can all apply the opacity to themselves.
5902 static const size_t kOpacityMaxChildCount
= 3;
5904 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
5905 // are likely have more child items than |kOpacityMaxChildCount|.
5906 static const size_t kOpacityMaxListSize
= kOpacityMaxChildCount
* 2;
5909 * Recursively iterates through |aList| and collects at most
5910 * |kOpacityMaxChildCount| display item pointers to items that return true for
5911 * CanApplyOpacity(). The item pointers are added to |aArray|.
5913 * LayerEventRegions and WrapList items are ignored.
5915 * We need to do this recursively, because the child display items might contain
5916 * nested nsDisplayWrapLists.
5918 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
5919 * item that returns false for CanApplyOpacity() is encountered.
5920 * Otherwise returns true.
5922 static bool CollectItemsWithOpacity(nsDisplayList
* aList
,
5923 nsTArray
<nsPaintedDisplayItem
*>& aArray
) {
5924 if (aList
->Count() > kOpacityMaxListSize
) {
5925 // Exit early, since |aList| will likely contain more than
5926 // |kOpacityMaxChildCount| items.
5930 for (nsDisplayItem
* i
: *aList
) {
5931 const DisplayItemType type
= i
->GetType();
5933 if (type
== DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
) {
5937 // Descend only into wraplists.
5938 if (type
== DisplayItemType::TYPE_WRAP_LIST
||
5939 type
== DisplayItemType::TYPE_CONTAINER
) {
5940 // The current display item has children, process them first.
5941 if (!CollectItemsWithOpacity(i
->GetChildren(), aArray
)) {
5948 if (aArray
.Length() == kOpacityMaxChildCount
) {
5952 auto* item
= i
->AsPaintedDisplayItem();
5953 if (!item
|| !item
->CanApplyOpacity()) {
5957 aArray
.AppendElement(item
);
5963 bool nsDisplayOpacity::ApplyToChildren(nsDisplayListBuilder
* aBuilder
) {
5964 if (mChildOpacityState
== ChildOpacityState::Deferred
) {
5968 // Iterate through the child display list and copy at most
5969 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
5970 AutoTArray
<nsPaintedDisplayItem
*, kOpacityMaxChildCount
> items
;
5971 if (!CollectItemsWithOpacity(&mList
, items
)) {
5972 mChildOpacityState
= ChildOpacityState::Deferred
;
5977 nsPaintedDisplayItem
* item
;
5979 } children
[kOpacityMaxChildCount
];
5982 size_t childCount
= 0;
5983 for (nsPaintedDisplayItem
* item
: items
) {
5984 children
[childCount
].item
= item
;
5985 children
[childCount
].bounds
= item
->GetBounds(aBuilder
, &snap
);
5989 for (size_t i
= 0; i
< childCount
; i
++) {
5990 for (size_t j
= i
+ 1; j
< childCount
; j
++) {
5991 if (children
[i
].bounds
.Intersects(children
[j
].bounds
)) {
5992 mChildOpacityState
= ChildOpacityState::Deferred
;
5998 for (uint32_t i
= 0; i
< childCount
; i
++) {
5999 children
[i
].item
->ApplyOpacity(aBuilder
, mOpacity
, mClipChain
);
6002 mChildOpacityState
= ChildOpacityState::Applied
;
6007 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
6008 * that has the same frame as the opacity item, and that supports painting with
6009 * opacity. In this case the opacity item can be optimized away.
6011 bool nsDisplayOpacity::ApplyToFilterOrMask(const bool aUsingLayers
) {
6012 if (mList
.Count() != 1) {
6016 nsDisplayItem
* item
= mList
.GetBottom();
6017 if (item
->Frame() != mFrame
) {
6018 // The effect item needs to have the same frame as the opacity item.
6022 const DisplayItemType type
= item
->GetType();
6023 if (type
== DisplayItemType::TYPE_MASK
||
6024 type
== DisplayItemType::TYPE_FILTER
) {
6025 auto* filterOrMaskItem
= static_cast<nsDisplayEffectsBase
*>(item
);
6026 filterOrMaskItem
->SelectOpacityOptimization(aUsingLayers
);
6033 bool nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) {
6034 if (mFrame
->GetPrevContinuation() || mFrame
->GetNextContinuation() ||
6035 mFrame
->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT
)) {
6036 // If we've been split, then we might need to merge, so
6037 // don't flatten us away.
6041 if (mNeedsActiveLayer
|| mOpacity
== 0.0) {
6042 // If our opacity is zero then we'll discard all descendant display items
6043 // except for layer event regions, so there's no point in doing this
6044 // optimization (and if we do do it, then invalidations of those descendants
6045 // might trigger repainting).
6049 if (mList
.IsEmpty()) {
6053 const bool usingLayers
= !aBuilder
->IsPaintingForWebRender();
6055 if (ApplyToFilterOrMask(usingLayers
)) {
6056 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame
));
6057 mChildOpacityState
= ChildOpacityState::Applied
;
6061 // Return true if we successfully applied opacity to child items.
6062 return ApplyToChildren(aBuilder
);
6065 nsDisplayItem::LayerState
nsDisplayOpacity::GetLayerState(
6066 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
6067 const ContainerLayerParameters
& aParameters
) {
6068 // If we only created this item so that we'd get correct nsDisplayEventRegions
6069 // for child frames, then force us to inactive to avoid unnecessary
6070 // layerization changes for content that won't ever be painted.
6071 if (mForEventsOnly
) {
6072 MOZ_ASSERT(mOpacity
== 0);
6073 return LayerState::LAYER_INACTIVE
;
6076 if (mNeedsActiveLayer
) {
6077 // Returns LayerState::LAYER_ACTIVE_FORCE to avoid flatterning the layer for
6078 // async animations.
6079 return LayerState::LAYER_ACTIVE_FORCE
;
6082 return RequiredLayerStateForChildren(aBuilder
, aManager
, aParameters
, mList
,
6083 GetAnimatedGeometryRoot(),
6084 GetActiveScrolledRoot());
6087 bool nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
6088 nsRegion
* aVisibleRegion
) {
6089 // Our children are translucent so we should not allow them to subtract
6090 // area from aVisibleRegion. We do need to find out what is visible under
6091 // our children in the temporary compositing buffer, because if our children
6092 // paint our entire bounds opaquely then we don't need an alpha channel in
6093 // the temporary compositing buffer.
6094 nsRect bounds
= GetClippedBounds(aBuilder
);
6095 nsRegion visibleUnderChildren
;
6096 visibleUnderChildren
.And(*aVisibleRegion
, bounds
);
6097 return nsDisplayWrapList::ComputeVisibility(aBuilder
, &visibleUnderChildren
);
6100 void nsDisplayOpacity::ComputeInvalidationRegion(
6101 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
6102 nsRegion
* aInvalidRegion
) const {
6103 auto* geometry
= static_cast<const nsDisplayOpacityGeometry
*>(aGeometry
);
6106 if (mOpacity
!= geometry
->mOpacity
) {
6107 aInvalidRegion
->Or(GetBounds(aBuilder
, &snap
), geometry
->mBounds
);
6111 void nsDisplayOpacity::WriteDebugInfo(std::stringstream
& aStream
) {
6112 aStream
<< " (opacity " << mOpacity
<< ", mChildOpacityState: ";
6113 switch (mChildOpacityState
) {
6114 case ChildOpacityState::Unknown
:
6115 aStream
<< "Unknown";
6117 case ChildOpacityState::Applied
:
6118 aStream
<< "Applied";
6120 case ChildOpacityState::Deferred
:
6121 aStream
<< "Deferred";
6130 bool nsDisplayOpacity::CreateWebRenderCommands(
6131 mozilla::wr::DisplayListBuilder
& aBuilder
,
6132 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
6133 const StackingContextHelper
& aSc
,
6134 mozilla::layers::RenderRootStateManager
* aManager
,
6135 nsDisplayListBuilder
* aDisplayListBuilder
) {
6136 MOZ_ASSERT(mChildOpacityState
!= ChildOpacityState::Applied
);
6137 float* opacityForSC
= &mOpacity
;
6139 uint64_t animationsId
=
6140 AddAnimationsForWebRender(this, aManager
, aDisplayListBuilder
);
6141 wr::WrAnimationProperty prop
{
6142 wr::WrAnimationType::Opacity
,
6146 wr::StackingContextParams params
;
6147 params
.animation
= animationsId
? &prop
: nullptr;
6148 params
.opacity
= opacityForSC
;
6150 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
6151 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
6154 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6155 &mList
, this, aDisplayListBuilder
, sc
, aBuilder
, aResources
);
6159 nsDisplayBlendMode::nsDisplayBlendMode(
6160 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
6161 mozilla::StyleBlend aBlendMode
,
6162 const ActiveScrolledRoot
* aActiveScrolledRoot
, const bool aIsForBackground
)
6163 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
6164 mBlendMode(aBlendMode
),
6165 mIsForBackground(aIsForBackground
) {
6166 MOZ_COUNT_CTOR(nsDisplayBlendMode
);
6169 nsRegion
nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
6170 bool* aSnap
) const {
6172 // We are never considered opaque
6176 LayerState
nsDisplayBlendMode::GetLayerState(
6177 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
6178 const ContainerLayerParameters
& aParameters
) {
6179 return LayerState::LAYER_ACTIVE
;
6182 bool nsDisplayBlendMode::CreateWebRenderCommands(
6183 mozilla::wr::DisplayListBuilder
& aBuilder
,
6184 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
6185 const StackingContextHelper
& aSc
,
6186 mozilla::layers::RenderRootStateManager
* aManager
,
6187 nsDisplayListBuilder
* aDisplayListBuilder
) {
6188 wr::StackingContextParams params
;
6189 params
.mix_blend_mode
=
6190 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode
));
6192 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
6193 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
6196 return nsDisplayWrapList::CreateWebRenderCommands(
6197 aBuilder
, aResources
, sc
, aManager
, aDisplayListBuilder
);
6200 // nsDisplayBlendMode uses layers for rendering
6201 already_AddRefed
<Layer
> nsDisplayBlendMode::BuildLayer(
6202 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
6203 const ContainerLayerParameters
& aContainerParameters
) {
6204 ContainerLayerParameters newContainerParameters
= aContainerParameters
;
6205 newContainerParameters
.mDisableSubpixelAntialiasingInDescendants
= true;
6207 RefPtr
<Layer
> container
= aManager
->GetLayerBuilder()->BuildContainerLayerFor(
6208 aBuilder
, aManager
, mFrame
, this, &mList
, newContainerParameters
,
6214 container
->SetMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode
));
6216 return container
.forget();
6219 void nsDisplayBlendMode::Paint(nsDisplayListBuilder
* aBuilder
,
6221 // This should be switched to use PushLayerWithBlend, once it's
6222 // been implemented for all DrawTarget backends.
6223 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6225 IntRect::RoundOut(NSRectToRect(GetPaintRect(), appUnitsPerDevPixel
));
6227 RefPtr
<DrawTarget
> dt
= aCtx
->GetDrawTarget()->CreateSimilarDrawTarget(
6228 rect
.Size(), SurfaceFormat::B8G8R8A8
);
6229 dt
->SetTransform(Matrix::Translation(-rect
.x
, -rect
.y
));
6230 RefPtr
<gfxContext
> ctx
= gfxContext::CreatePreservingTransformOrNull(dt
);
6232 GetChildren()->Paint(aBuilder
, ctx
,
6233 mFrame
->PresContext()->AppUnitsPerDevPixel());
6236 RefPtr
<SourceSurface
> surface
= dt
->Snapshot();
6237 aCtx
->GetDrawTarget()->DrawSurface(
6238 surface
, Rect(rect
.x
, rect
.y
, rect
.width
, rect
.height
),
6239 Rect(0, 0, rect
.width
, rect
.height
), DrawSurfaceOptions(),
6240 DrawOptions(1.0f
, nsCSSRendering::GetGFXBlendMode(mBlendMode
)));
6243 mozilla::gfx::CompositionOp
nsDisplayBlendMode::BlendMode() {
6244 return nsCSSRendering::GetGFXBlendMode(mBlendMode
);
6247 bool nsDisplayBlendMode::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
6248 nsRegion
* aVisibleRegion
) {
6249 // Our children are need their backdrop so we should not allow them to
6250 // subtract area from aVisibleRegion. We do need to find out what is visible
6251 // under our children in the temporary compositing buffer, because if our
6252 // children paint our entire bounds opaquely then we don't need an alpha
6253 // channel in the temporary compositing buffer.
6254 nsRect bounds
= GetClippedBounds(aBuilder
);
6255 nsRegion visibleUnderChildren
;
6256 visibleUnderChildren
.And(*aVisibleRegion
, bounds
);
6257 return nsDisplayWrapList::ComputeVisibility(aBuilder
, &visibleUnderChildren
);
6260 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem
* aItem
) const {
6261 // Items for the same content element should be merged into a single
6262 // compositing group.
6263 if (!HasDifferentFrame(aItem
) || !HasSameTypeAndClip(aItem
) ||
6264 !HasSameContent(aItem
)) {
6268 const auto* item
= static_cast<const nsDisplayBlendMode
*>(aItem
);
6269 if (mIsForBackground
|| item
->mIsForBackground
) {
6270 // Don't merge background-blend-mode items
6278 nsDisplayBlendContainer
* nsDisplayBlendContainer::CreateForMixBlendMode(
6279 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
6280 const ActiveScrolledRoot
* aActiveScrolledRoot
) {
6281 return MakeDisplayItem
<nsDisplayBlendContainer
>(aBuilder
, aFrame
, aList
,
6282 aActiveScrolledRoot
, false);
6286 nsDisplayBlendContainer
* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
6287 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
6288 nsDisplayList
* aList
, const ActiveScrolledRoot
* aActiveScrolledRoot
) {
6289 if (aSecondaryFrame
) {
6290 auto type
= GetTableTypeFromFrame(aFrame
);
6291 auto index
= static_cast<uint16_t>(type
);
6293 return MakeDisplayItemWithIndex
<nsDisplayTableBlendContainer
>(
6294 aBuilder
, aSecondaryFrame
, index
, aList
, aActiveScrolledRoot
, true,
6298 return MakeDisplayItemWithIndex
<nsDisplayBlendContainer
>(
6299 aBuilder
, aFrame
, 1, aList
, aActiveScrolledRoot
, true);
6302 nsDisplayBlendContainer::nsDisplayBlendContainer(
6303 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
6304 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aIsForBackground
)
6305 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
6306 mIsForBackground(aIsForBackground
) {
6307 MOZ_COUNT_CTOR(nsDisplayBlendContainer
);
6310 // nsDisplayBlendContainer uses layers for rendering
6311 already_AddRefed
<Layer
> nsDisplayBlendContainer::BuildLayer(
6312 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
6313 const ContainerLayerParameters
& aContainerParameters
) {
6314 // turn off anti-aliasing in the parent stacking context because it changes
6315 // how the group is initialized.
6316 ContainerLayerParameters newContainerParameters
= aContainerParameters
;
6317 newContainerParameters
.mDisableSubpixelAntialiasingInDescendants
= true;
6319 RefPtr
<Layer
> container
= aManager
->GetLayerBuilder()->BuildContainerLayerFor(
6320 aBuilder
, aManager
, mFrame
, this, &mList
, newContainerParameters
,
6326 container
->SetForceIsolatedGroup(true);
6327 return container
.forget();
6330 LayerState
nsDisplayBlendContainer::GetLayerState(
6331 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
6332 const ContainerLayerParameters
& aParameters
) {
6333 return RequiredLayerStateForChildren(aBuilder
, aManager
, aParameters
, mList
,
6334 GetAnimatedGeometryRoot(),
6335 GetActiveScrolledRoot());
6338 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder
* aBuilder
,
6340 aCtx
->GetDrawTarget()->PushLayer(false, 1.0, nullptr, mozilla::gfx::Matrix());
6341 GetChildren()->Paint(aBuilder
, aCtx
,
6342 mFrame
->PresContext()->AppUnitsPerDevPixel());
6343 aCtx
->GetDrawTarget()->PopLayer();
6346 bool nsDisplayBlendContainer::CreateWebRenderCommands(
6347 mozilla::wr::DisplayListBuilder
& aBuilder
,
6348 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
6349 const StackingContextHelper
& aSc
,
6350 mozilla::layers::RenderRootStateManager
* aManager
,
6351 nsDisplayListBuilder
* aDisplayListBuilder
) {
6352 wr::StackingContextParams params
;
6353 params
.flags
|= wr::StackingContextFlags::IS_BLEND_CONTAINER
;
6355 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
6356 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
6359 return nsDisplayWrapList::CreateWebRenderCommands(
6360 aBuilder
, aResources
, sc
, aManager
, aDisplayListBuilder
);
6363 nsDisplayOwnLayer::nsDisplayOwnLayer(
6364 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
6365 const ActiveScrolledRoot
* aActiveScrolledRoot
,
6366 nsDisplayOwnLayerFlags aFlags
, const ScrollbarData
& aScrollbarData
,
6367 bool aForceActive
, bool aClearClipChain
)
6368 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
,
6371 mScrollbarData(aScrollbarData
),
6372 mForceActive(aForceActive
),
6374 MOZ_COUNT_CTOR(nsDisplayOwnLayer
);
6376 // For scroll thumb layers, override the AGR to be the thumb's AGR rather
6377 // than the AGR for mFrame (which is the slider frame).
6378 if (IsScrollThumbLayer()) {
6379 if (nsIFrame
* thumbFrame
= nsIFrame::GetChildXULBox(mFrame
)) {
6380 mAnimatedGeometryRoot
= aBuilder
->FindAnimatedGeometryRootFor(thumbFrame
);
6385 LayerState
nsDisplayOwnLayer::GetLayerState(
6386 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
6387 const ContainerLayerParameters
& aParameters
) {
6389 return mozilla::LayerState::LAYER_ACTIVE_FORCE
;
6392 return RequiredLayerStateForChildren(aBuilder
, aManager
, aParameters
, mList
,
6393 GetAnimatedGeometryRoot(),
6394 GetActiveScrolledRoot());
6397 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
6398 return mScrollbarData
.mScrollbarLayerType
==
6399 layers::ScrollbarLayerType::Thumb
;
6402 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
6403 return mScrollbarData
.mScrollbarLayerType
==
6404 layers::ScrollbarLayerType::Container
;
6407 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
6408 if (!IsScrollbarContainer()) {
6412 return mFrame
->PresContext()->IsRootContentDocumentCrossProcess() &&
6413 mScrollbarData
.mTargetViewId
==
6414 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame
->PresContext());
6417 bool nsDisplayOwnLayer::IsZoomingLayer() const {
6418 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM
;
6421 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
6422 return GetType() == DisplayItemType::TYPE_FIXED_POSITION
;
6425 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
6426 return GetType() == DisplayItemType::TYPE_STICKY_POSITION
;
6429 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
6430 if (!mFrame
->PresContext()->IsRootContentDocumentCrossProcess()) {
6433 return mFrame
->PresContext()->HasDynamicToolbar() ||
6434 // For tests on Android, this pref is set to simulate the dynamic
6436 StaticPrefs::apz_fixed_margin_override_enabled();
6439 // nsDisplayOpacity uses layers for rendering
6440 already_AddRefed
<Layer
> nsDisplayOwnLayer::BuildLayer(
6441 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
6442 const ContainerLayerParameters
& aContainerParameters
) {
6443 RefPtr
<ContainerLayer
> layer
=
6444 aManager
->GetLayerBuilder()->BuildContainerLayerFor(
6445 aBuilder
, aManager
, mFrame
, this, &mList
, aContainerParameters
,
6446 nullptr, FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR
);
6448 if (IsScrollThumbLayer() || IsScrollbarContainer()) {
6449 layer
->SetScrollbarData(mScrollbarData
);
6452 if (mFlags
& nsDisplayOwnLayerFlags::GenerateSubdocInvalidations
) {
6453 mFrame
->PresContext()->SetNotifySubDocInvalidationData(layer
);
6455 return layer
.forget();
6458 bool nsDisplayOwnLayer::CreateWebRenderCommands(
6459 mozilla::wr::DisplayListBuilder
& aBuilder
,
6460 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
6461 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
6462 nsDisplayListBuilder
* aDisplayListBuilder
) {
6463 Maybe
<wr::WrAnimationProperty
> prop
;
6464 bool needsProp
= aManager
->LayerManager()->AsyncPanZoomEnabled() &&
6465 (IsScrollThumbLayer() || IsZoomingLayer() ||
6466 (IsFixedPositionLayer() && HasDynamicToolbar()) ||
6467 (IsStickyPositionLayer() && HasDynamicToolbar()) ||
6468 (IsRootScrollbarContainer() && HasDynamicToolbar()));
6471 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
6472 // to create and set an animation id. That way APZ can adjust the position/
6473 // zoom of this content asynchronously as needed.
6474 RefPtr
<WebRenderAPZAnimationData
> animationData
=
6475 aManager
->CommandBuilder()
6476 .CreateOrRecycleWebRenderUserData
<WebRenderAPZAnimationData
>(this);
6477 mWrAnimationId
= animationData
->GetAnimationId();
6480 prop
->id
= mWrAnimationId
;
6481 prop
->effect_type
= wr::WrAnimationType::Transform
;
6484 wr::StackingContextParams params
;
6485 params
.animation
= prop
.ptrOr(nullptr);
6487 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
6488 if (IsScrollbarContainer()) {
6489 params
.prim_flags
|= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER
;
6491 if (IsScrollThumbLayer()) {
6492 params
.prim_flags
|= wr::PrimitiveFlags::IS_SCROLLBAR_THUMB
;
6494 if (IsZoomingLayer() ||
6495 ((IsFixedPositionLayer() && HasDynamicToolbar()) ||
6496 (IsStickyPositionLayer() && HasDynamicToolbar()) ||
6497 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
6498 params
.is_2d_scale_translation
= true;
6499 params
.should_snap
= true;
6502 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
6505 nsDisplayWrapList::CreateWebRenderCommands(aBuilder
, aResources
, sc
, aManager
,
6506 aDisplayListBuilder
);
6510 bool nsDisplayOwnLayer::UpdateScrollData(
6511 mozilla::layers::WebRenderScrollData
* aData
,
6512 mozilla::layers::WebRenderLayerScrollData
* aLayerData
) {
6513 bool isRelevantToApz
=
6514 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
6515 (IsFixedPositionLayer() && HasDynamicToolbar()) ||
6516 (IsStickyPositionLayer() && HasDynamicToolbar()));
6518 if (!isRelevantToApz
) {
6526 if (IsZoomingLayer()) {
6527 aLayerData
->SetZoomAnimationId(mWrAnimationId
);
6531 if (IsFixedPositionLayer() && HasDynamicToolbar()) {
6532 aLayerData
->SetFixedPositionAnimationId(mWrAnimationId
);
6536 if (IsStickyPositionLayer() && HasDynamicToolbar()) {
6537 aLayerData
->SetStickyPositionAnimationId(mWrAnimationId
);
6541 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
6543 aLayerData
->SetScrollbarData(mScrollbarData
);
6545 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
6546 aLayerData
->SetScrollbarAnimationId(mWrAnimationId
);
6550 if (IsScrollThumbLayer()) {
6551 aLayerData
->SetScrollbarAnimationId(mWrAnimationId
);
6552 LayoutDeviceRect bounds
= LayoutDeviceIntRect::FromAppUnits(
6553 mBounds
, mFrame
->PresContext()->AppUnitsPerDevPixel());
6554 // We use a resolution of 1.0 because this is a WebRender codepath which
6555 // always uses containerless scrolling, and so resolution doesn't apply to
6557 LayerIntRect layerBounds
=
6558 RoundedOut(bounds
* LayoutDeviceToLayerScale(1.0f
));
6559 aLayerData
->SetVisibleRegion(LayerIntRegion(layerBounds
));
6564 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream
& aStream
) {
6565 aStream
<< nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64
")",
6566 (int)mFlags
, mScrollbarData
.mTargetViewId
)
6570 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder
* aBuilder
,
6572 nsSubDocumentFrame
* aSubDocFrame
,
6573 nsDisplayList
* aList
,
6574 nsDisplayOwnLayerFlags aFlags
)
6575 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
,
6576 aBuilder
->CurrentActiveScrolledRoot(), aFlags
),
6577 mScrollParentId(aBuilder
->GetCurrentScrollParentId()),
6578 mShouldFlatten(false),
6579 mSubDocFrame(aSubDocFrame
) {
6580 MOZ_COUNT_CTOR(nsDisplaySubDocument
);
6582 // The SubDocument display item is conceptually outside the viewport frame,
6583 // so in cases where the viewport frame is an AGR, the SubDocument's AGR
6584 // should be not the viewport frame itself, but its parent AGR.
6585 if (*mAnimatedGeometryRoot
== mFrame
&& mAnimatedGeometryRoot
->mParentAGR
) {
6586 mAnimatedGeometryRoot
= mAnimatedGeometryRoot
->mParentAGR
;
6589 if (mSubDocFrame
&& mSubDocFrame
!= mFrame
) {
6590 mSubDocFrame
->AddDisplayItem(this);
6594 nsDisplaySubDocument::~nsDisplaySubDocument() {
6595 MOZ_COUNT_DTOR(nsDisplaySubDocument
);
6597 mSubDocFrame
->RemoveDisplayItem(this);
6601 nsIFrame
* nsDisplaySubDocument::FrameForInvalidation() const {
6602 return mSubDocFrame
? mSubDocFrame
: mFrame
;
6605 void nsDisplaySubDocument::RemoveFrame(nsIFrame
* aFrame
) {
6606 if (aFrame
== mSubDocFrame
) {
6607 mSubDocFrame
= nullptr;
6610 nsDisplayOwnLayer::RemoveFrame(aFrame
);
6613 void nsDisplaySubDocument::Disown() {
6615 mFrame
->RemoveDisplayItem(this);
6616 RemoveFrame(mFrame
);
6619 mSubDocFrame
->RemoveDisplayItem(this);
6620 RemoveFrame(mSubDocFrame
);
6624 static bool UseDisplayPortForViewport(nsDisplayListBuilder
* aBuilder
,
6626 return aBuilder
->IsPaintingToWindow() &&
6627 DisplayPortUtils::ViewportHasDisplayPort(aFrame
->PresContext());
6630 nsRect
nsDisplaySubDocument::GetBounds(nsDisplayListBuilder
* aBuilder
,
6631 bool* aSnap
) const {
6632 bool usingDisplayPort
= UseDisplayPortForViewport(aBuilder
, mFrame
);
6634 if ((mFlags
& nsDisplayOwnLayerFlags::GenerateScrollableLayer
) &&
6637 return mFrame
->GetRect() + aBuilder
->ToReferenceFrame(mFrame
);
6640 return nsDisplayOwnLayer::GetBounds(aBuilder
, aSnap
);
6643 bool nsDisplaySubDocument::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
6644 nsRegion
* aVisibleRegion
) {
6645 bool usingDisplayPort
= UseDisplayPortForViewport(aBuilder
, mFrame
);
6647 if (!(mFlags
& nsDisplayOwnLayerFlags::GenerateScrollableLayer
) ||
6648 !usingDisplayPort
) {
6649 return nsDisplayWrapList::ComputeVisibility(aBuilder
, aVisibleRegion
);
6653 nsIFrame
* rootScrollFrame
= mFrame
->PresShell()->GetRootScrollFrame();
6654 MOZ_ASSERT(rootScrollFrame
);
6655 Unused
<< DisplayPortUtils::GetDisplayPort(
6656 rootScrollFrame
->GetContent(), &displayport
,
6657 DisplayPortOptions().With(DisplayportRelativeTo::ScrollFrame
));
6659 nsRegion childVisibleRegion
;
6660 // The visible region for the children may be much bigger than the hole we
6661 // are viewing the children from, so that the compositor process has enough
6662 // content to asynchronously pan while content is being refreshed.
6663 childVisibleRegion
=
6664 displayport
+ mFrame
->GetOffsetToCrossDoc(ReferenceFrame());
6666 nsRect boundedRect
= childVisibleRegion
.GetBounds().Intersect(
6667 mList
.GetClippedBoundsWithRespectToASR(aBuilder
, mActiveScrolledRoot
));
6668 bool visible
= mList
.ComputeVisibilityForSublist(
6669 aBuilder
, &childVisibleRegion
, boundedRect
);
6671 // If APZ is enabled then don't allow this computation to influence
6672 // aVisibleRegion, on the assumption that the layer can be asynchronously
6673 // scrolled so we'll definitely need all the content under it.
6674 if (!nsLayoutUtils::UsesAsyncScrolling(mFrame
)) {
6676 nsRect bounds
= GetBounds(aBuilder
, &snap
);
6678 removed
.Sub(bounds
, childVisibleRegion
);
6680 aBuilder
->SubtractFromVisibleRegion(aVisibleRegion
, removed
);
6686 nsRegion
nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
6687 bool* aSnap
) const {
6688 bool usingDisplayPort
= UseDisplayPortForViewport(aBuilder
, mFrame
);
6690 if ((mFlags
& nsDisplayOwnLayerFlags::GenerateScrollableLayer
) &&
6696 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder
, aSnap
);
6700 nsDisplayFixedPosition
* nsDisplayFixedPosition::CreateForFixedBackground(
6701 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
6702 nsDisplayBackgroundImage
* aImage
, const uint16_t aIndex
) {
6704 temp
.AppendToTop(aImage
);
6706 if (aSecondaryFrame
) {
6707 auto tableType
= GetTableTypeFromFrame(aFrame
);
6708 const uint16_t index
= CalculateTablePerFrameKey(aIndex
+ 1, tableType
);
6709 return MakeDisplayItemWithIndex
<nsDisplayTableFixedPosition
>(
6710 aBuilder
, aSecondaryFrame
, index
, &temp
, aFrame
);
6713 return MakeDisplayItemWithIndex
<nsDisplayFixedPosition
>(aBuilder
, aFrame
,
6717 nsDisplayFixedPosition::nsDisplayFixedPosition(
6718 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
6719 const ActiveScrolledRoot
* aActiveScrolledRoot
,
6720 const ActiveScrolledRoot
* aContainerASR
)
6721 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
),
6722 mContainerASR(aContainerASR
),
6723 mIsFixedBackground(false) {
6724 MOZ_COUNT_CTOR(nsDisplayFixedPosition
);
6728 nsDisplayFixedPosition::nsDisplayFixedPosition(nsDisplayListBuilder
* aBuilder
,
6730 nsDisplayList
* aList
)
6731 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
,
6732 aBuilder
->CurrentActiveScrolledRoot()),
6733 mContainerASR(nullptr), // XXX maybe this should be something?
6734 mIsFixedBackground(true) {
6735 MOZ_COUNT_CTOR(nsDisplayFixedPosition
);
6739 void nsDisplayFixedPosition::Init(nsDisplayListBuilder
* aBuilder
) {
6740 mAnimatedGeometryRootForScrollMetadata
= mAnimatedGeometryRoot
;
6741 if (ShouldFixToViewport(aBuilder
)) {
6742 mAnimatedGeometryRoot
= aBuilder
->FindAnimatedGeometryRootFor(this);
6746 already_AddRefed
<Layer
> nsDisplayFixedPosition::BuildLayer(
6747 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
6748 const ContainerLayerParameters
& aContainerParameters
) {
6749 RefPtr
<Layer
> layer
=
6750 nsDisplayOwnLayer::BuildLayer(aBuilder
, aManager
, aContainerParameters
);
6752 layer
->SetIsFixedPosition(true);
6754 nsPresContext
* presContext
= mFrame
->PresContext();
6755 nsIFrame
* fixedFrame
=
6756 mIsFixedBackground
? presContext
->PresShell()->GetRootFrame() : mFrame
;
6758 const nsIFrame
* viewportFrame
= fixedFrame
->GetParent();
6759 // anchorRect will be in the container's coordinate system (aLayer's parent
6760 // layer). This is the same as the display items' reference frame.
6762 if (viewportFrame
) {
6763 anchorRect
.SizeTo(viewportFrame
->GetSize());
6764 // Fixed position frames are reflowed into the scroll-port size if one has
6766 if (const ViewportFrame
* viewport
= do_QueryFrame(viewportFrame
)) {
6768 viewport
->AdjustViewportSizeForFixedPosition(anchorRect
));
6771 // A display item directly attached to the viewport.
6772 // For background-attachment:fixed items, the anchor point is always the
6773 // top-left of the viewport currently.
6774 viewportFrame
= fixedFrame
;
6776 // The anchorRect top-left is always the viewport top-left.
6777 anchorRect
.MoveTo(viewportFrame
->GetOffsetToCrossDoc(ReferenceFrame()));
6779 nsLayoutUtils::SetFixedPositionLayerData(layer
, viewportFrame
, anchorRect
,
6780 fixedFrame
, presContext
,
6781 aContainerParameters
);
6783 return layer
.forget();
6786 ViewID
nsDisplayFixedPosition::GetScrollTargetId() {
6787 if (mContainerASR
&& !nsLayoutUtils::IsReallyFixedPos(mFrame
)) {
6788 return mContainerASR
->GetViewId();
6790 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame
->PresContext());
6793 bool nsDisplayFixedPosition::CreateWebRenderCommands(
6794 mozilla::wr::DisplayListBuilder
& aBuilder
,
6795 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
6796 const StackingContextHelper
& aSc
,
6797 mozilla::layers::RenderRootStateManager
* aManager
,
6798 nsDisplayListBuilder
* aDisplayListBuilder
) {
6799 SideBits sides
= SideBits::eNone
;
6800 if (!mIsFixedBackground
) {
6801 sides
= nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame
);
6804 // We install this RAII scrolltarget tracker so that any
6805 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
6806 // share the same ASR as this item) use the correct scroll target. That way
6807 // attempts to scroll on those items will scroll the root scroll frame.
6808 mozilla::wr::DisplayListBuilder::FixedPosScrollTargetTracker
tracker(
6809 aBuilder
, GetActiveScrolledRoot(), GetScrollTargetId(), sides
);
6810 return nsDisplayOwnLayer::CreateWebRenderCommands(
6811 aBuilder
, aResources
, aSc
, aManager
, aDisplayListBuilder
);
6814 bool nsDisplayFixedPosition::UpdateScrollData(
6815 mozilla::layers::WebRenderScrollData
* aData
,
6816 mozilla::layers::WebRenderLayerScrollData
* aLayerData
) {
6818 if (!mIsFixedBackground
) {
6819 aLayerData
->SetFixedPositionSides(
6820 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame
));
6822 aLayerData
->SetFixedPositionScrollContainerId(GetScrollTargetId());
6824 nsDisplayOwnLayer::UpdateScrollData(aData
, aLayerData
);
6828 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream
& aStream
) {
6829 aStream
<< nsPrintfCString(" (containerASR %s) (scrolltarget %" PRIu64
")",
6830 ActiveScrolledRoot::ToString(mContainerASR
).get(),
6831 GetScrollTargetId())
6835 TableType
GetTableTypeFromFrame(nsIFrame
* aFrame
) {
6836 if (aFrame
->IsTableFrame()) {
6837 return TableType::Table
;
6840 if (aFrame
->IsTableColFrame()) {
6841 return TableType::TableCol
;
6844 if (aFrame
->IsTableColGroupFrame()) {
6845 return TableType::TableColGroup
;
6848 if (aFrame
->IsTableRowFrame()) {
6849 return TableType::TableRow
;
6852 if (aFrame
->IsTableRowGroupFrame()) {
6853 return TableType::TableRowGroup
;
6856 if (aFrame
->IsTableCellFrame()) {
6857 return TableType::TableCell
;
6860 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
6861 return TableType::Table
;
6864 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
6865 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
6866 nsIFrame
* aAncestorFrame
)
6867 : nsDisplayFixedPosition(aBuilder
, aFrame
, aList
),
6868 mAncestorFrame(aAncestorFrame
) {
6869 if (aBuilder
->IsRetainingDisplayList()) {
6870 mAncestorFrame
->AddDisplayItem(this);
6874 nsDisplayStickyPosition::nsDisplayStickyPosition(
6875 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
6876 const ActiveScrolledRoot
* aActiveScrolledRoot
,
6877 const ActiveScrolledRoot
* aContainerASR
, bool aClippedToDisplayPort
)
6878 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
),
6879 mContainerASR(aContainerASR
),
6880 mClippedToDisplayPort(aClippedToDisplayPort
) {
6881 MOZ_COUNT_CTOR(nsDisplayStickyPosition
);
6884 void nsDisplayStickyPosition::SetClipChain(
6885 const DisplayItemClipChain
* aClipChain
, bool aStore
) {
6886 mClipChain
= aClipChain
;
6890 "There should never be a clip on this item because no clip moves "
6894 mState
.mClipChain
= aClipChain
;
6895 mState
.mClip
= mClip
;
6899 already_AddRefed
<Layer
> nsDisplayStickyPosition::BuildLayer(
6900 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
6901 const ContainerLayerParameters
& aContainerParameters
) {
6902 RefPtr
<Layer
> layer
=
6903 nsDisplayOwnLayer::BuildLayer(aBuilder
, aManager
, aContainerParameters
);
6905 StickyScrollContainer
* stickyScrollContainer
=
6906 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame
);
6907 if (!stickyScrollContainer
) {
6908 return layer
.forget();
6911 nsIFrame
* scrollFrame
= do_QueryFrame(stickyScrollContainer
->ScrollFrame());
6912 nsPresContext
* presContext
= scrollFrame
->PresContext();
6914 // Sticky position frames whose scroll frame is the root scroll frame are
6915 // reflowed into the scroll-port size if one has been set.
6916 nsSize scrollFrameSize
= scrollFrame
->GetSize();
6917 if (scrollFrame
== presContext
->PresShell()->GetRootScrollFrame() &&
6918 presContext
->PresShell()->IsVisualViewportSizeSet()) {
6919 scrollFrameSize
= presContext
->PresShell()->GetVisualViewportSize();
6922 nsLayoutUtils::SetFixedPositionLayerData(
6924 nsRect(scrollFrame
->GetOffsetToCrossDoc(ReferenceFrame()),
6926 mFrame
, presContext
, aContainerParameters
);
6928 ViewID scrollId
= nsLayoutUtils::FindOrCreateIDFor(
6929 stickyScrollContainer
->ScrollFrame()->GetScrolledFrame()->GetContent());
6931 float factor
= presContext
->AppUnitsPerDevPixel();
6932 LayerRectAbsolute stickyOuter
;
6933 LayerRectAbsolute stickyInner
;
6934 CalculateLayerScrollRanges(
6935 stickyScrollContainer
, factor
, aContainerParameters
.mXScale
,
6936 aContainerParameters
.mYScale
, stickyOuter
, stickyInner
);
6937 layer
->SetStickyPositionData(scrollId
, stickyOuter
, stickyInner
);
6939 return layer
.forget();
6942 // Returns the smallest distance from "0" to the range [min, max] where
6943 // min <= max. Despite the name, the return value is actually a 1-D vector,
6944 // and so may be negative if max < 0.
6945 static nscoord
DistanceToRange(nscoord min
, nscoord max
) {
6946 MOZ_ASSERT(min
<= max
);
6953 MOZ_ASSERT(min
<= 0 && max
>= 0);
6957 // Returns the magnitude of the part of the range [min, max] that is greater
6958 // than zero. The return value is always non-negative.
6959 static nscoord
PositivePart(nscoord min
, nscoord max
) {
6960 MOZ_ASSERT(min
<= max
);
6970 // Returns the magnitude of the part of the range [min, max] that is less
6971 // than zero. The return value is always non-negative.
6972 static nscoord
NegativePart(nscoord min
, nscoord max
) {
6973 MOZ_ASSERT(min
<= max
);
6983 StickyScrollContainer
* nsDisplayStickyPosition::GetStickyScrollContainer() {
6984 StickyScrollContainer
* stickyScrollContainer
=
6985 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame
);
6986 if (stickyScrollContainer
) {
6987 // If there's no ASR for the scrollframe that this sticky item is attached
6988 // to, then don't create a WR sticky item for it either. Trying to do so
6989 // will end in sadness because WR will interpret some coordinates as
6990 // relative to the nearest enclosing scrollframe, which will correspond
6991 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
6992 // same as the scrollframe this sticky item is actually supposed to be
6993 // attached to, thus the sadness.
6994 // Not sending WR the sticky item is ok, because the enclosing scrollframe
6995 // will never be asynchronously scrolled. Instead we will always position
6996 // the sticky items correctly on the gecko side and WR will never need to
6997 // adjust their position itself.
6998 if (!stickyScrollContainer
->ScrollFrame()
6999 ->IsMaybeAsynchronouslyScrolled()) {
7000 stickyScrollContainer
= nullptr;
7003 return stickyScrollContainer
;
7006 bool nsDisplayStickyPosition::CreateWebRenderCommands(
7007 mozilla::wr::DisplayListBuilder
& aBuilder
,
7008 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
7009 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
7010 nsDisplayListBuilder
* aDisplayListBuilder
) {
7011 StickyScrollContainer
* stickyScrollContainer
= GetStickyScrollContainer();
7013 Maybe
<wr::SpaceAndClipChainHelper
> saccHelper
;
7015 if (stickyScrollContainer
) {
7016 float auPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7019 nsRect itemBounds
= GetBounds(aDisplayListBuilder
, &snap
);
7021 Maybe
<float> topMargin
;
7022 Maybe
<float> rightMargin
;
7023 Maybe
<float> bottomMargin
;
7024 Maybe
<float> leftMargin
;
7025 wr::StickyOffsetBounds vBounds
= {0.0, 0.0};
7026 wr::StickyOffsetBounds hBounds
= {0.0, 0.0};
7027 nsPoint appliedOffset
;
7029 nsRectAbsolute outer
;
7030 nsRectAbsolute inner
;
7031 stickyScrollContainer
->GetScrollRanges(mFrame
, &outer
, &inner
);
7033 nsIFrame
* scrollFrame
= do_QueryFrame(stickyScrollContainer
->ScrollFrame());
7034 nsPoint offset
= scrollFrame
->GetOffsetToCrossDoc(ReferenceFrame());
7036 // Adjust the scrollPort coordinates to be relative to the reference frame,
7037 // so that it is in the same space as everything else.
7039 stickyScrollContainer
->ScrollFrame()->GetScrollPortRect();
7040 scrollPort
+= offset
;
7042 // The following computations make more sense upon understanding the
7043 // semantics of "inner" and "outer", which is explained in the comment on
7044 // SetStickyPositionData in Layers.h.
7046 if (outer
.YMost() != inner
.YMost()) {
7047 // Question: How far will itemBounds.y be from the top of the scrollport
7048 // when we have scrolled from the current scroll position of "0" to
7049 // reach the range [inner.YMost(), outer.YMost()] where the item gets
7051 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
7052 // needs to be adjusted by the distance to the range, less any other
7053 // sticky ranges that fall between 0 and the range. If the distance is
7054 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
7055 // scrolling upwards (decreasing scroll offset) to reach that range,
7056 // which would increase itemBounds.y and make it farther away from the
7057 // top of the scrollport. So in that case the adjustment is -distance.
7058 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
7059 // we would be scrolling downwards, itemBounds.y would decrease, and we
7060 // again need to adjust by -distance. If we are already in the range
7061 // then no adjustment is needed and distance is 0 so again using
7062 // -distance works. If the distance is positive, and the item has both
7063 // top and bottom sticky ranges, then the bottom sticky range may fall
7064 // (entirely[1] or partly[2]) between the current scroll position.
7065 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
7066 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
7067 // In these cases, the item doesn't actually move for that part of the
7068 // distance, so we need to subtract out that bit, which can be computed
7069 // as the positive portion of the range [outer.Y(), inner.Y()].
7070 nscoord distance
= DistanceToRange(inner
.YMost(), outer
.YMost());
7072 distance
-= PositivePart(outer
.Y(), inner
.Y());
7074 topMargin
= Some(NSAppUnitsToFloatPixels(
7075 itemBounds
.y
- scrollPort
.y
- distance
, auPerDevPixel
));
7076 // Question: What is the maximum positive ("downward") offset that WR
7077 // will have to apply to this item in order to prevent the item from
7079 // Answer: Since the item is "sticky" in the range [inner.YMost(),
7080 // outer.YMost()], the maximum offset will be the size of the range, which
7081 // is outer.YMost() - inner.YMost().
7083 NSAppUnitsToFloatPixels(outer
.YMost() - inner
.YMost(), auPerDevPixel
);
7084 // Question: how much of an offset has layout already applied to the item?
7085 // Answer: if we are
7086 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
7087 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
7088 // then layout has already applied some offset to the position of the
7089 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
7090 // and |outer.YMost() - inner.YMost()| in case (b).
7091 if (inner
.YMost() < 0) {
7092 appliedOffset
.y
= std::min(0, outer
.YMost()) - inner
.YMost();
7093 MOZ_ASSERT(appliedOffset
.y
> 0);
7096 if (outer
.Y() != inner
.Y()) {
7097 // Similar logic as in the previous section, but this time we care about
7098 // the distance from itemBounds.YMost() to scrollPort.YMost().
7099 nscoord distance
= DistanceToRange(outer
.Y(), inner
.Y());
7101 distance
+= NegativePart(inner
.YMost(), outer
.YMost());
7103 bottomMargin
= Some(NSAppUnitsToFloatPixels(
7104 scrollPort
.YMost() - itemBounds
.YMost() + distance
, auPerDevPixel
));
7105 // And here WR will be moving the item upwards rather than downwards so
7106 // again things are inverted from the previous block.
7108 NSAppUnitsToFloatPixels(outer
.Y() - inner
.Y(), auPerDevPixel
);
7109 // We can't have appliedOffset be both positive and negative, and the top
7110 // adjustment takes priority. So here we only update appliedOffset.y if
7111 // it wasn't set by the top-sticky case above.
7112 if (appliedOffset
.y
== 0 && inner
.Y() > 0) {
7113 appliedOffset
.y
= std::max(0, outer
.Y()) - inner
.Y();
7114 MOZ_ASSERT(appliedOffset
.y
< 0);
7117 // Same as above, but for the x-axis
7118 if (outer
.XMost() != inner
.XMost()) {
7119 nscoord distance
= DistanceToRange(inner
.XMost(), outer
.XMost());
7121 distance
-= PositivePart(outer
.X(), inner
.X());
7123 leftMargin
= Some(NSAppUnitsToFloatPixels(
7124 itemBounds
.x
- scrollPort
.x
- distance
, auPerDevPixel
));
7126 NSAppUnitsToFloatPixels(outer
.XMost() - inner
.XMost(), auPerDevPixel
);
7127 if (inner
.XMost() < 0) {
7128 appliedOffset
.x
= std::min(0, outer
.XMost()) - inner
.XMost();
7129 MOZ_ASSERT(appliedOffset
.x
> 0);
7132 if (outer
.X() != inner
.X()) {
7133 nscoord distance
= DistanceToRange(outer
.X(), inner
.X());
7135 distance
+= NegativePart(inner
.XMost(), outer
.XMost());
7137 rightMargin
= Some(NSAppUnitsToFloatPixels(
7138 scrollPort
.XMost() - itemBounds
.XMost() + distance
, auPerDevPixel
));
7140 NSAppUnitsToFloatPixels(outer
.X() - inner
.X(), auPerDevPixel
);
7141 if (appliedOffset
.x
== 0 && inner
.X() > 0) {
7142 appliedOffset
.x
= std::max(0, outer
.X()) - inner
.X();
7143 MOZ_ASSERT(appliedOffset
.x
< 0);
7147 LayoutDeviceRect bounds
=
7148 LayoutDeviceRect::FromAppUnits(itemBounds
, auPerDevPixel
);
7149 wr::LayoutVector2D applied
= {
7150 NSAppUnitsToFloatPixels(appliedOffset
.x
, auPerDevPixel
),
7151 NSAppUnitsToFloatPixels(appliedOffset
.y
, auPerDevPixel
)};
7152 wr::WrSpatialId spatialId
= aBuilder
.DefineStickyFrame(
7153 wr::ToLayoutRect(bounds
), topMargin
.ptrOr(nullptr),
7154 rightMargin
.ptrOr(nullptr), bottomMargin
.ptrOr(nullptr),
7155 leftMargin
.ptrOr(nullptr), vBounds
, hBounds
, applied
);
7157 saccHelper
.emplace(aBuilder
, spatialId
);
7158 aManager
->CommandBuilder().PushOverrideForASR(mContainerASR
, spatialId
);
7162 wr::StackingContextParams params
;
7164 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
7165 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this,
7167 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder
, aResources
, sc
,
7168 aManager
, aDisplayListBuilder
);
7171 if (stickyScrollContainer
) {
7172 aManager
->CommandBuilder().PopOverrideForASR(mContainerASR
);
7178 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
7179 StickyScrollContainer
* aStickyScrollContainer
, float aAppUnitsPerDevPixel
,
7180 float aScaleX
, float aScaleY
, LayerRectAbsolute
& aStickyOuter
,
7181 LayerRectAbsolute
& aStickyInner
) {
7182 nsRectAbsolute outer
;
7183 nsRectAbsolute inner
;
7184 aStickyScrollContainer
->GetScrollRanges(mFrame
, &outer
, &inner
);
7185 aStickyOuter
.SetBox(
7186 NSAppUnitsToFloatPixels(outer
.X(), aAppUnitsPerDevPixel
) * aScaleX
,
7187 NSAppUnitsToFloatPixels(outer
.Y(), aAppUnitsPerDevPixel
) * aScaleY
,
7188 NSAppUnitsToFloatPixels(outer
.XMost(), aAppUnitsPerDevPixel
) * aScaleX
,
7189 NSAppUnitsToFloatPixels(outer
.YMost(), aAppUnitsPerDevPixel
) * aScaleY
);
7190 aStickyInner
.SetBox(
7191 NSAppUnitsToFloatPixels(inner
.X(), aAppUnitsPerDevPixel
) * aScaleX
,
7192 NSAppUnitsToFloatPixels(inner
.Y(), aAppUnitsPerDevPixel
) * aScaleY
,
7193 NSAppUnitsToFloatPixels(inner
.XMost(), aAppUnitsPerDevPixel
) * aScaleX
,
7194 NSAppUnitsToFloatPixels(inner
.YMost(), aAppUnitsPerDevPixel
) * aScaleY
);
7197 bool nsDisplayStickyPosition::UpdateScrollData(
7198 mozilla::layers::WebRenderScrollData
* aData
,
7199 mozilla::layers::WebRenderLayerScrollData
* aLayerData
) {
7200 bool hasDynamicToolbar
= HasDynamicToolbar();
7201 if (aLayerData
&& hasDynamicToolbar
) {
7202 StickyScrollContainer
* stickyScrollContainer
= GetStickyScrollContainer();
7203 if (stickyScrollContainer
) {
7204 float auPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7205 float cumulativeResolution
=
7206 mFrame
->PresShell()->GetCumulativeResolution();
7207 LayerRectAbsolute stickyOuter
;
7208 LayerRectAbsolute stickyInner
;
7209 CalculateLayerScrollRanges(stickyScrollContainer
, auPerDevPixel
,
7210 cumulativeResolution
, cumulativeResolution
,
7211 stickyOuter
, stickyInner
);
7212 aLayerData
->SetStickyScrollRangeOuter(stickyOuter
);
7213 aLayerData
->SetStickyScrollRangeInner(stickyInner
);
7216 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame
);
7217 aLayerData
->SetFixedPositionSides(sides
);
7220 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer
->ScrollFrame()
7221 ->GetScrolledFrame()
7223 aLayerData
->SetStickyPositionScrollContainerId(scrollId
);
7226 // Return true if either there is a dynamic toolbar affecting this sticky
7227 // item or the OwnLayer base implementation returns true for some other
7229 bool ret
= hasDynamicToolbar
;
7230 ret
|= nsDisplayOwnLayer::UpdateScrollData(aData
, aLayerData
);
7234 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
7235 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aScrolledFrame
,
7236 nsIFrame
* aScrollFrame
, const CompositorHitTestInfo
& aHitInfo
,
7237 const nsRect
& aHitArea
)
7238 : nsDisplayWrapList(aBuilder
, aScrollFrame
),
7239 mScrollFrame(aScrollFrame
),
7240 mScrolledFrame(aScrolledFrame
),
7241 mScrollParentId(aBuilder
->GetCurrentScrollParentId()),
7243 mHitArea(aHitArea
) {
7244 #ifdef NS_BUILD_REFCNT_LOGGING
7245 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer
);
7249 already_AddRefed
<Layer
> nsDisplayScrollInfoLayer::BuildLayer(
7250 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
7251 const ContainerLayerParameters
& aContainerParameters
) {
7252 // In general for APZ with event-regions we no longer have a need for
7253 // scrollinfo layers. However, in some cases, there might be content that
7254 // cannot be layerized, and so needs to scroll synchronously. To handle those
7255 // cases, we still want to generate scrollinfo layers.
7257 return aManager
->GetLayerBuilder()->BuildContainerLayerFor(
7258 aBuilder
, aManager
, mFrame
, this, &mList
, aContainerParameters
, nullptr,
7259 FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR
);
7262 LayerState
nsDisplayScrollInfoLayer::GetLayerState(
7263 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
7264 const ContainerLayerParameters
& aParameters
) {
7265 return LayerState::LAYER_ACTIVE_EMPTY
;
7268 UniquePtr
<ScrollMetadata
> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
7269 nsDisplayListBuilder
* aBuilder
, LayerManager
* aLayerManager
,
7270 const ContainerLayerParameters
& aContainerParameters
) {
7271 ScrollMetadata metadata
= nsLayoutUtils::ComputeScrollMetadata(
7272 mScrolledFrame
, mScrollFrame
, mScrollFrame
->GetContent(),
7273 ReferenceFrame(), aLayerManager
, mScrollParentId
, mScrollFrame
->GetSize(),
7274 Nothing(), false, Some(aContainerParameters
));
7275 metadata
.GetMetrics().SetIsScrollInfoLayer(true);
7276 nsIScrollableFrame
* scrollableFrame
= mScrollFrame
->GetScrollTargetFrame();
7277 if (scrollableFrame
) {
7278 aBuilder
->AddScrollFrameToNotify(scrollableFrame
);
7281 return UniquePtr
<ScrollMetadata
>(new ScrollMetadata(metadata
));
7284 bool nsDisplayScrollInfoLayer::UpdateScrollData(
7285 mozilla::layers::WebRenderScrollData
* aData
,
7286 mozilla::layers::WebRenderLayerScrollData
* aLayerData
) {
7288 UniquePtr
<ScrollMetadata
> metadata
= ComputeScrollMetadata(
7289 aData
->GetBuilder(), aData
->GetManager(), ContainerLayerParameters());
7291 MOZ_ASSERT(metadata
);
7292 aLayerData
->AppendScrollMetadata(*aData
, *metadata
);
7297 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
7298 mozilla::wr::DisplayListBuilder
& aBuilder
,
7299 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
7300 const StackingContextHelper
& aSc
,
7301 mozilla::layers::RenderRootStateManager
* aManager
,
7302 nsDisplayListBuilder
* aDisplayListBuilder
) {
7303 ScrollableLayerGuid::ViewID scrollId
=
7304 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame
->GetContent());
7306 const LayoutDeviceRect devRect
= LayoutDeviceRect::FromAppUnits(
7307 mHitArea
, mScrollFrame
->PresContext()->AppUnitsPerDevPixel());
7309 const wr::LayoutRect rect
= wr::ToLayoutRect(devRect
);
7311 aBuilder
.PushHitTest(rect
, rect
, !BackfaceIsHidden(), scrollId
, mHitInfo
,
7317 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream
& aStream
) {
7318 aStream
<< " (scrollframe " << mScrollFrame
<< " scrolledFrame "
7319 << mScrolledFrame
<< ")";
7322 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
7323 nsSubDocumentFrame
* aSubDocFrame
,
7324 nsDisplayList
* aList
, int32_t aAPD
,
7325 int32_t aParentAPD
, nsDisplayOwnLayerFlags aFlags
)
7326 : nsDisplaySubDocument(aBuilder
, aFrame
, aSubDocFrame
, aList
, aFlags
),
7328 mParentAPD(aParentAPD
) {
7329 MOZ_COUNT_CTOR(nsDisplayZoom
);
7332 nsRect
nsDisplayZoom::GetBounds(nsDisplayListBuilder
* aBuilder
,
7333 bool* aSnap
) const {
7334 nsRect bounds
= nsDisplaySubDocument::GetBounds(aBuilder
, aSnap
);
7336 return bounds
.ScaleToOtherAppUnitsRoundOut(mAPD
, mParentAPD
);
7339 void nsDisplayZoom::HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
7340 HitTestState
* aState
,
7341 nsTArray
<nsIFrame
*>* aOutFrames
) {
7343 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
7344 // rect as well instead of possibly rounding the width or height to zero.
7345 if (aRect
.width
== 1 && aRect
.height
== 1) {
7346 rect
.MoveTo(aRect
.TopLeft().ScaleToOtherAppUnits(mParentAPD
, mAPD
));
7347 rect
.width
= rect
.height
= 1;
7349 rect
= aRect
.ScaleToOtherAppUnitsRoundOut(mParentAPD
, mAPD
);
7351 mList
.HitTest(aBuilder
, rect
, aState
, aOutFrames
);
7354 bool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
7355 nsRegion
* aVisibleRegion
) {
7356 // Convert the passed in visible region to our appunits.
7357 nsRegion visibleRegion
;
7358 // mVisibleRect has been clipped to GetClippedBounds
7359 visibleRegion
.And(*aVisibleRegion
, GetPaintRect());
7360 visibleRegion
= visibleRegion
.ScaleToOtherAppUnitsRoundOut(mParentAPD
, mAPD
);
7361 nsRegion originalVisibleRegion
= visibleRegion
;
7363 nsRect transformedVisibleRect
=
7364 GetPaintRect().ScaleToOtherAppUnitsRoundOut(mParentAPD
, mAPD
);
7366 // If we are to generate a scrollable layer we call
7367 // nsDisplaySubDocument::ComputeVisibility to make the necessary adjustments
7368 // for ComputeVisibility, it does all it's calculations in the child APD.
7369 bool usingDisplayPort
= UseDisplayPortForViewport(aBuilder
, mFrame
);
7370 if (!(mFlags
& nsDisplayOwnLayerFlags::GenerateScrollableLayer
) ||
7371 !usingDisplayPort
) {
7372 retval
= mList
.ComputeVisibilityForSublist(aBuilder
, &visibleRegion
,
7373 transformedVisibleRect
);
7375 retval
= nsDisplaySubDocument::ComputeVisibility(aBuilder
, &visibleRegion
);
7379 // removed = originalVisibleRegion - visibleRegion
7380 removed
.Sub(originalVisibleRegion
, visibleRegion
);
7381 // Convert removed region to parent appunits.
7382 removed
= removed
.ScaleToOtherAppUnitsRoundIn(mAPD
, mParentAPD
);
7383 // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications
7384 // SubtractFromVisibleRegion does)
7385 aBuilder
->SubtractFromVisibleRegion(aVisibleRegion
, removed
);
7390 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
7391 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
7392 const ActiveScrolledRoot
* aActiveScrolledRoot
,
7393 mozilla::layers::FrameMetrics::ViewID aViewID
)
7394 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
),
7396 MOZ_COUNT_CTOR(nsDisplayAsyncZoom
);
7399 #ifdef NS_BUILD_REFCNT_LOGGING
7400 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
7401 MOZ_COUNT_DTOR(nsDisplayAsyncZoom
);
7405 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder
* aBuilder
,
7406 const nsRect
& aRect
, HitTestState
* aState
,
7407 nsTArray
<nsIFrame
*>* aOutFrames
) {
7409 nsIScrollableFrame
* scrollFrame
= do_QueryFrame(mFrame
);
7410 MOZ_ASSERT(scrollFrame
&& ViewportUtils::IsZoomedContentRoot(
7411 scrollFrame
->GetScrolledFrame()));
7413 nsRect rect
= ViewportUtils::VisualToLayout(aRect
, mFrame
->PresShell());
7414 mList
.HitTest(aBuilder
, rect
, aState
, aOutFrames
);
7417 already_AddRefed
<Layer
> nsDisplayAsyncZoom::BuildLayer(
7418 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
7419 const ContainerLayerParameters
& aContainerParameters
) {
7420 PresShell
* presShell
= mFrame
->PresShell();
7421 ContainerLayerParameters
containerParameters(
7422 presShell
->GetResolution(), presShell
->GetResolution(), nsIntPoint(),
7423 aContainerParameters
);
7425 RefPtr
<Layer
> layer
=
7426 nsDisplayOwnLayer::BuildLayer(aBuilder
, aManager
, containerParameters
);
7428 layer
->SetAsyncZoomContainerId(Some(mViewID
));
7430 layer
->SetPostScale(1.0f
/ presShell
->GetResolution(),
7431 1.0f
/ presShell
->GetResolution());
7432 layer
->AsContainerLayer()->SetScaleToResolution(presShell
->GetResolution());
7434 return layer
.forget();
7437 bool nsDisplayAsyncZoom::UpdateScrollData(
7438 mozilla::layers::WebRenderScrollData
* aData
,
7439 mozilla::layers::WebRenderLayerScrollData
* aLayerData
) {
7440 bool ret
= nsDisplayOwnLayer::UpdateScrollData(aData
, aLayerData
);
7443 aLayerData
->SetAsyncZoomContainerId(mViewID
);
7448 ///////////////////////////////////////////////////
7449 // nsDisplayTransform Implementation
7453 static_assert(sizeof(nsDisplayTransform
) <= 512,
7454 "nsDisplayTransform has grown");
7457 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder
* aBuilder
,
7458 nsIFrame
* aFrame
, nsDisplayList
* aList
,
7459 const nsRect
& aChildrenBuildingRect
)
7460 : nsPaintedDisplayItem(aBuilder
, aFrame
),
7461 mTransform(Some(Matrix4x4())),
7462 mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot
),
7463 mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot
),
7464 mChildrenBuildingRect(aChildrenBuildingRect
),
7465 mPrerenderDecision(PrerenderDecision::No
),
7466 mIsTransformSeparator(true),
7467 mHasTransformGetter(false) {
7468 MOZ_COUNT_CTOR(nsDisplayTransform
);
7469 MOZ_ASSERT(aFrame
, "Must have a frame!");
7470 Init(aBuilder
, aList
);
7473 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder
* aBuilder
,
7474 nsIFrame
* aFrame
, nsDisplayList
* aList
,
7475 const nsRect
& aChildrenBuildingRect
,
7476 PrerenderDecision aPrerenderDecision
)
7477 : nsPaintedDisplayItem(aBuilder
, aFrame
),
7478 mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot
),
7479 mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot
),
7480 mChildrenBuildingRect(aChildrenBuildingRect
),
7481 mPrerenderDecision(aPrerenderDecision
),
7482 mIsTransformSeparator(false),
7483 mHasTransformGetter(false) {
7484 MOZ_COUNT_CTOR(nsDisplayTransform
);
7485 MOZ_ASSERT(aFrame
, "Must have a frame!");
7486 SetReferenceFrameToAncestor(aBuilder
);
7487 Init(aBuilder
, aList
);
7490 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder
* aBuilder
,
7491 nsIFrame
* aFrame
, nsDisplayList
* aList
,
7492 const nsRect
& aChildrenBuildingRect
,
7493 decltype(WithTransformGetter
))
7494 : nsPaintedDisplayItem(aBuilder
, aFrame
),
7495 mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot
),
7496 mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot
),
7497 mChildrenBuildingRect(aChildrenBuildingRect
),
7498 mPrerenderDecision(PrerenderDecision::No
),
7499 mIsTransformSeparator(false),
7500 mHasTransformGetter(true) {
7501 MOZ_COUNT_CTOR(nsDisplayTransform
);
7502 MOZ_ASSERT(aFrame
, "Must have a frame!");
7503 MOZ_ASSERT(aFrame
->GetTransformGetter());
7504 Init(aBuilder
, aList
);
7507 void nsDisplayTransform::SetReferenceFrameToAncestor(
7508 nsDisplayListBuilder
* aBuilder
) {
7509 if (mFrame
== aBuilder
->RootReferenceFrame()) {
7512 // We manually recompute mToReferenceFrame without going through the
7513 // builder, since this won't apply the 'additional offset'. Our
7514 // children will already be painting with that applied, and we don't
7515 // want to include it a second time in our transform. We don't recompute
7516 // our visible/building rects, since those should still include the additional
7518 // TODO: Are there are things computed using our ToReferenceFrame that should
7519 // have the additional offset applied? Should we instead just manually remove
7520 // the offset from our transform instead of this more general value?
7521 // Can we instead apply the additional offset to us and not our children, like
7522 // we do for all other offsets (and how reference frames are supposed to
7525 nsIFrame
* outerFrame
= nsLayoutUtils::GetCrossDocParentFrame(mFrame
);
7526 MOZ_ASSERT(mReferenceFrame
== aBuilder
->FindReferenceFrameFor(outerFrame
));
7528 mToReferenceFrame
= mFrame
->GetOffsetToCrossDoc(mReferenceFrame
);
7530 if (DisplayPortUtils::IsFixedPosFrameInDisplayPort(mFrame
)) {
7531 // This is an odd special case. If we are both IsFixedPosFrameInDisplayPort
7532 // and transformed that we are our own AGR parent.
7533 // We want our frame to be our AGR because FrameLayerBuilder uses our AGR to
7534 // determine if we are inside a fixed pos subtree. If we use the outer AGR
7535 // from outside the fixed pos subtree FLB can't tell that we are fixed pos.
7536 mAnimatedGeometryRoot
= mAnimatedGeometryRootForChildren
;
7537 } else if (mFrame
->StyleDisplay()->mPosition
==
7538 StylePositionProperty::Sticky
&&
7539 IsStickyFrameActive(aBuilder
, mFrame
, nullptr)) {
7540 // Similar to the IsFixedPosFrameInDisplayPort case we are our own AGR.
7541 // We are inside the sticky position, so our AGR is the sticky positioned
7542 // frame, which is our AGR, not the parent AGR.
7543 mAnimatedGeometryRoot
= mAnimatedGeometryRootForChildren
;
7544 } else if (mAnimatedGeometryRoot
->mParentAGR
) {
7545 mAnimatedGeometryRootForScrollMetadata
= mAnimatedGeometryRoot
->mParentAGR
;
7546 if (!MayBeAnimated(aBuilder
)) {
7547 // If we're an animated transform then we want the same AGR as our
7548 // children so that FrameLayerBuilder knows that this layer moves with the
7549 // transform and won't compute occlusions. If we're not animated then use
7550 // our parent AGR so that inactive transform layers can go in the same
7551 // PaintedLayer as surrounding content.
7552 mAnimatedGeometryRoot
= mAnimatedGeometryRoot
->mParentAGR
;
7557 void nsDisplayTransform::Init(nsDisplayListBuilder
* aBuilder
,
7558 nsDisplayList
* aChildren
) {
7559 mShouldFlatten
= false;
7560 mChildren
.AppendToTop(aChildren
);
7561 UpdateBounds(aBuilder
);
7564 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) {
7568 /* Returns the delta specified by the transform-origin property.
7569 * This is a positive delta, meaning that it indicates the direction to move
7570 * to get from (0, 0) of the frame to the transform origin. This function is
7571 * called off the main thread.
7574 Point3D
nsDisplayTransform::GetDeltaToTransformOrigin(
7575 const nsIFrame
* aFrame
, TransformReferenceBox
& aRefBox
,
7576 float aAppUnitsPerPixel
) {
7577 MOZ_ASSERT(aFrame
, "Can't get delta for a null frame!");
7578 MOZ_ASSERT(aFrame
->IsTransformed() || aFrame
->BackfaceIsHidden() ||
7579 aFrame
->Combines3DTransformWithAncestors(),
7580 "Shouldn't get a delta for an untransformed frame!");
7582 if (!aFrame
->IsTransformed()) {
7586 /* For both of the coordinates, if the value of transform is a
7587 * percentage, it's relative to the size of the frame. Otherwise, if it's
7588 * a distance, it's already computed for us!
7590 const nsStyleDisplay
* display
= aFrame
->StyleDisplay();
7592 const StyleTransformOrigin
& transformOrigin
= display
->mTransformOrigin
;
7593 CSSPoint origin
= nsStyleTransformMatrix::Convert2DPosition(
7594 transformOrigin
.horizontal
, transformOrigin
.vertical
, aRefBox
);
7596 if (aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
7597 // SVG frames (unlike other frames) have a reference box that can be (and
7598 // typically is) offset from the TopLeft() of the frame. We need to account
7600 origin
.x
+= CSSPixel::FromAppUnits(aRefBox
.X());
7601 origin
.y
+= CSSPixel::FromAppUnits(aRefBox
.Y());
7604 float scale
= mozilla::AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel
);
7605 float z
= transformOrigin
.depth
._0
;
7606 return Point3D(origin
.x
* scale
, origin
.y
* scale
, z
* scale
);
7610 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame
* aFrame
,
7611 float aAppUnitsPerPixel
,
7612 Matrix4x4
& aOutMatrix
) {
7613 MOZ_ASSERT(aFrame
, "Can't get delta for a null frame!");
7614 MOZ_ASSERT(aFrame
->IsTransformed() || aFrame
->BackfaceIsHidden() ||
7615 aFrame
->Combines3DTransformWithAncestors(),
7616 "Shouldn't get a delta for an untransformed frame!");
7617 MOZ_ASSERT(aOutMatrix
.IsIdentity(), "Must have a blank output matrix");
7619 if (!aFrame
->IsTransformed()) {
7623 /* Find our containing block, which is the element that provides the
7624 * value for perspective we need to use
7627 // TODO: Is it possible that the cbFrame's bounds haven't been set correctly
7629 // (similar to the aBoundsOverride case for GetResultingTransformMatrix)?
7630 nsIFrame
* cbFrame
= aFrame
->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME
);
7635 /* Grab the values for perspective and perspective-origin (if present) */
7636 const nsStyleDisplay
* cbDisplay
= cbFrame
->StyleDisplay();
7637 if (cbDisplay
->mChildPerspective
.IsNone()) {
7641 MOZ_ASSERT(cbDisplay
->mChildPerspective
.IsLength());
7642 // TODO(emilio): Seems quite silly to go through app units just to convert to
7643 // float pixels below.
7644 nscoord perspective
= cbDisplay
->mChildPerspective
.length
._0
.ToAppUnits();
7645 if (perspective
< std::numeric_limits
<Float
>::epsilon()) {
7649 TransformReferenceBox
refBox(cbFrame
);
7651 Point perspectiveOrigin
= nsStyleTransformMatrix::Convert2DPosition(
7652 cbDisplay
->mPerspectiveOrigin
.horizontal
,
7653 cbDisplay
->mPerspectiveOrigin
.vertical
, refBox
, aAppUnitsPerPixel
);
7655 /* GetOffsetTo computes the offset required to move from 0,0 in cbFrame to 0,0
7656 * in aFrame. Although we actually want the inverse of this, it's faster to
7659 nsPoint frameToCbOffset
= -aFrame
->GetOffsetTo(cbFrame
);
7660 Point
frameToCbGfxOffset(
7661 NSAppUnitsToFloatPixels(frameToCbOffset
.x
, aAppUnitsPerPixel
),
7662 NSAppUnitsToFloatPixels(frameToCbOffset
.y
, aAppUnitsPerPixel
));
7664 /* Move the perspective origin to be relative to aFrame, instead of relative
7665 * to the containing block which is how it was specified in the style system.
7667 perspectiveOrigin
+= frameToCbGfxOffset
;
7670 -1.0 / NSAppUnitsToFloatPixels(perspective
, aAppUnitsPerPixel
);
7672 aOutMatrix
.ChangeBasis(Point3D(perspectiveOrigin
.x
, perspectiveOrigin
.y
, 0));
7676 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
7677 const nsIFrame
* aFrame
, TransformReferenceBox
& aRefBox
,
7678 float aAppUnitsPerPixel
)
7680 mTranslate(aFrame
->StyleDisplay()->mTranslate
),
7681 mRotate(aFrame
->StyleDisplay()->mRotate
),
7682 mScale(aFrame
->StyleDisplay()->mScale
),
7683 mTransform(aFrame
->StyleDisplay()->mTransform
),
7684 mMotion(MotionPathUtils::ResolveMotionPath(aFrame
, aRefBox
)),
7686 GetDeltaToTransformOrigin(aFrame
, aRefBox
, aAppUnitsPerPixel
)) {}
7688 /* Wraps up the transform matrix in a change-of-basis matrix pair that
7689 * translates from local coordinate space to transform coordinate space, then
7692 Matrix4x4
nsDisplayTransform::GetResultingTransformMatrix(
7693 const FrameTransformProperties
& aProperties
, TransformReferenceBox
& aRefBox
,
7694 float aAppUnitsPerPixel
) {
7695 return GetResultingTransformMatrixInternal(aProperties
, aRefBox
, nsPoint(),
7696 aAppUnitsPerPixel
, 0);
7699 Matrix4x4
nsDisplayTransform::GetResultingTransformMatrix(
7700 const nsIFrame
* aFrame
, const nsPoint
& aOrigin
, float aAppUnitsPerPixel
,
7702 TransformReferenceBox
refBox(aFrame
);
7703 FrameTransformProperties
props(aFrame
, refBox
, aAppUnitsPerPixel
);
7704 return GetResultingTransformMatrixInternal(props
, refBox
, aOrigin
,
7705 aAppUnitsPerPixel
, aFlags
);
7708 Matrix4x4
nsDisplayTransform::GetResultingTransformMatrixInternal(
7709 const FrameTransformProperties
& aProperties
, TransformReferenceBox
& aRefBox
,
7710 const nsPoint
& aOrigin
, float aAppUnitsPerPixel
, uint32_t aFlags
) {
7711 const nsIFrame
* frame
= aProperties
.mFrame
;
7712 NS_ASSERTION(frame
|| !(aFlags
& INCLUDE_PERSPECTIVE
),
7713 "Must have a frame to compute perspective!");
7715 // Get the underlying transform matrix:
7717 /* Get the matrix, then change its basis to factor in the origin. */
7719 // Call IsSVGTransformed() regardless of the value of
7720 // aProperties.HasTransform(), since we still need any
7721 // potential parentsChildrenOnlyTransform.
7722 Matrix svgTransform
, parentsChildrenOnlyTransform
;
7723 const bool hasSVGTransforms
=
7724 frame
&& frame
->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED
) &&
7725 frame
->IsSVGTransformed(&svgTransform
, &parentsChildrenOnlyTransform
);
7726 bool shouldRound
= nsLayoutUtils::ShouldSnapToGrid(frame
);
7728 /* Transformed frames always have a transform, or are preserving 3d (and might
7729 * still have perspective!) */
7730 if (aProperties
.HasTransform()) {
7731 result
= nsStyleTransformMatrix::ReadTransforms(
7732 aProperties
.mTranslate
, aProperties
.mRotate
, aProperties
.mScale
,
7733 aProperties
.mMotion
, aProperties
.mTransform
, aRefBox
,
7735 } else if (hasSVGTransforms
) {
7736 // Correct the translation components for zoom:
7737 float pixelsPerCSSPx
= AppUnitsPerCSSPixel() / aAppUnitsPerPixel
;
7738 svgTransform
._31
*= pixelsPerCSSPx
;
7739 svgTransform
._32
*= pixelsPerCSSPx
;
7740 result
= Matrix4x4::From2D(svgTransform
);
7743 // Apply any translation due to 'transform-origin' and/or 'transform-box':
7744 result
.ChangeBasis(aProperties
.mToTransformOrigin
);
7746 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
7747 // an explanation of what children-only transforms are.
7748 const bool parentHasChildrenOnlyTransform
=
7749 hasSVGTransforms
&& !parentsChildrenOnlyTransform
.IsIdentity();
7751 if (parentHasChildrenOnlyTransform
) {
7752 float pixelsPerCSSPx
= AppUnitsPerCSSPixel() / aAppUnitsPerPixel
;
7753 parentsChildrenOnlyTransform
._31
*= pixelsPerCSSPx
;
7754 parentsChildrenOnlyTransform
._32
*= pixelsPerCSSPx
;
7756 Point3D
frameOffset(
7757 NSAppUnitsToFloatPixels(-frame
->GetPosition().x
, aAppUnitsPerPixel
),
7758 NSAppUnitsToFloatPixels(-frame
->GetPosition().y
, aAppUnitsPerPixel
), 0);
7759 Matrix4x4 parentsChildrenOnlyTransform3D
=
7760 Matrix4x4::From2D(parentsChildrenOnlyTransform
)
7761 .ChangeBasis(frameOffset
);
7763 result
*= parentsChildrenOnlyTransform3D
;
7766 Matrix4x4 perspectiveMatrix
;
7767 bool hasPerspective
= aFlags
& INCLUDE_PERSPECTIVE
;
7768 if (hasPerspective
) {
7769 if (ComputePerspectiveMatrix(frame
, aAppUnitsPerPixel
, perspectiveMatrix
)) {
7770 result
*= perspectiveMatrix
;
7774 if ((aFlags
& INCLUDE_PRESERVE3D_ANCESTORS
) && frame
&&
7775 frame
->Combines3DTransformWithAncestors()) {
7776 // Include the transform set on our parent
7777 nsIFrame
* parentFrame
=
7778 frame
->GetClosestFlattenedTreeAncestorPrimaryFrame();
7779 NS_ASSERTION(parentFrame
&& parentFrame
->IsTransformed() &&
7780 parentFrame
->Extend3DContext(),
7781 "Preserve3D mismatch!");
7782 TransformReferenceBox
refBox(parentFrame
);
7783 FrameTransformProperties
props(parentFrame
, refBox
, aAppUnitsPerPixel
);
7786 aFlags
& (INCLUDE_PRESERVE3D_ANCESTORS
| INCLUDE_PERSPECTIVE
);
7788 // If this frame isn't transformed (but we exist for backface-visibility),
7789 // then we're not a reference frame so no offset to origin will be added.
7790 // Otherwise we need to manually translate into our parent's coordinate
7792 if (frame
->IsTransformed()) {
7793 nsLayoutUtils::PostTranslate(result
, frame
->GetPosition(),
7794 aAppUnitsPerPixel
, shouldRound
);
7796 Matrix4x4 parent
= GetResultingTransformMatrixInternal(
7797 props
, refBox
, nsPoint(0, 0), aAppUnitsPerPixel
, flags
);
7798 result
= result
* parent
;
7801 if (aFlags
& OFFSET_BY_ORIGIN
) {
7802 nsLayoutUtils::PostTranslate(result
, aOrigin
, aAppUnitsPerPixel
,
7809 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder
* aBuilder
) {
7810 static constexpr nsCSSPropertyIDSet opacitySet
=
7811 nsCSSPropertyIDSet::OpacityProperties();
7812 if (ActiveLayerTracker::IsStyleAnimated(aBuilder
, mFrame
, opacitySet
)) {
7816 EffectCompositor::SetPerformanceWarning(
7818 AnimationPerformanceWarning(
7819 AnimationPerformanceWarning::Type::OpacityFrameInactive
));
7824 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder
* aBuilder
) {
7825 return mPrerenderDecision
!= PrerenderDecision::No
;
7828 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
7829 nsDisplayListBuilder
* aBuilder
) {
7830 return StaticPrefs::gfx_omta_background_color();
7833 static bool IsInStickyPositionedSubtree(const nsIFrame
* aFrame
) {
7834 for (const nsIFrame
* frame
= aFrame
; frame
;
7835 frame
= nsLayoutUtils::GetCrossDocParentFrame(frame
)) {
7836 if (frame
->IsStickyPositioned()) {
7843 static bool ShouldUsePartialPrerender(const nsIFrame
* aFrame
) {
7844 return StaticPrefs::layout_animation_prerender_partial() &&
7845 // Bug 1642547: Support partial prerender for position:sticky elements.
7846 !IsInStickyPositionedSubtree(aFrame
);
7850 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
7851 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsRect
* aDirtyRect
)
7853 PrerenderInfo result
;
7854 // If we are in a preserve-3d tree, and we've disallowed async animations, we
7855 // return No prerender decision directly.
7856 if ((aFrame
->Extend3DContext() ||
7857 aFrame
->Combines3DTransformWithAncestors()) &&
7858 !aBuilder
->GetPreserves3DAllowAsyncAnimation()) {
7862 // Elements whose transform has been modified recently, or which
7863 // have a compositor-animated transform, can be prerendered. An element
7864 // might have only just had its transform animated in which case
7865 // the ActiveLayerManager may not have been notified yet.
7866 static constexpr nsCSSPropertyIDSet transformSet
=
7867 nsCSSPropertyIDSet::TransformLikeProperties();
7868 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame
) &&
7869 !EffectCompositor::HasAnimationsForCompositor(
7870 aFrame
, DisplayItemType::TYPE_TRANSFORM
)) {
7871 EffectCompositor::SetPerformanceWarning(
7872 aFrame
, transformSet
,
7873 AnimationPerformanceWarning(
7874 AnimationPerformanceWarning::Type::TransformFrameInactive
));
7876 // This case happens when we're sure that the frame is not animated and its
7877 // preserve-3d ancestors are not, either. So we don't need to pre-render.
7878 // However, this decision shouldn't affect the decisions for other frames in
7879 // the preserve-3d context. We need this flag to determine whether we should
7880 // block async animations on other frames in the current preserve-3d tree.
7881 result
.mHasAnimations
= false;
7885 // We should not allow prerender if any ancestor container element has
7886 // mask/clip-path effects.
7888 // With prerender and async transform animation, we do not need to restyle an
7889 // animated element to respect position changes, since that transform is done
7890 // by layer animation. As a result, the container element is not aware of
7891 // position change of that containing element and loses the chance to update
7892 // the content of mask/clip-path.
7894 // Why do we need to update a mask? This is relative to how we generate a
7895 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
7896 // mask layer, to reduce memory usage, we did not choose the size of the
7897 // masked element as mask size. Instead, we read the union of bounds of all
7898 // children display items by nsDisplayWrapList::GetBounds, which is smaller
7899 // than or equal to the masked element's boundary, and use it as the position
7900 // size of the mask layer. That union bounds is actually affected by the
7901 // geometry of the animated element. To keep the content of mask up to date,
7902 // forbidding of prerender is required.
7903 for (nsIFrame
* container
= nsLayoutUtils::GetCrossDocParentFrame(aFrame
);
7905 container
= nsLayoutUtils::GetCrossDocParentFrame(container
)) {
7906 const nsStyleSVGReset
* svgReset
= container
->StyleSVGReset();
7907 if (svgReset
->HasMask() || svgReset
->HasClipPath()) {
7912 // If the incoming dirty rect already contains the entire overflow area,
7913 // we are already rendering the entire content.
7914 nsRect overflow
= aFrame
->InkOverflowRectRelativeToSelf();
7915 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
7916 // in cases of non-invertible transforms, so we set `untransformedRect` to
7917 // `aDirtyRect` as an initial value for such cases.
7918 nsRect untransformedDirtyRect
= *aDirtyRect
;
7919 UntransformRect(*aDirtyRect
, overflow
, aFrame
, &untransformedDirtyRect
);
7920 if (untransformedDirtyRect
.Contains(overflow
)) {
7921 *aDirtyRect
= untransformedDirtyRect
;
7922 result
.mDecision
= PrerenderDecision::Full
;
7926 float viewportRatio
=
7927 StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
7928 uint32_t absoluteLimitX
=
7929 StaticPrefs::layout_animation_prerender_absolute_limit_x();
7930 uint32_t absoluteLimitY
=
7931 StaticPrefs::layout_animation_prerender_absolute_limit_y();
7932 nsSize refSize
= aBuilder
->RootReferenceFrame()->GetSize();
7934 float resolution
= aFrame
->PresShell()->GetCumulativeResolution();
7935 if (resolution
< 1.0f
) {
7937 NSCoordSaturatingNonnegativeMultiply(refSize
.width
, 1.0f
/ resolution
),
7938 NSCoordSaturatingNonnegativeMultiply(refSize
.height
,
7939 1.0f
/ resolution
));
7942 // Only prerender if the transformed frame's size is <= a multiple of the
7943 // reference frame size (~viewport), and less than an absolute limit.
7944 // Both the ratio and the absolute limit are configurable.
7945 nscoord maxLength
= std::max(nscoord(refSize
.width
* viewportRatio
),
7946 nscoord(refSize
.height
* viewportRatio
));
7947 nsSize
relativeLimit(maxLength
, maxLength
);
7948 nsSize
absoluteLimit(
7949 aFrame
->PresContext()->DevPixelsToAppUnits(absoluteLimitX
),
7950 aFrame
->PresContext()->DevPixelsToAppUnits(absoluteLimitY
));
7951 nsSize maxSize
= Min(relativeLimit
, absoluteLimit
);
7953 const auto transform
= nsLayoutUtils::GetTransformToAncestor(
7955 RelativeTo
{nsLayoutUtils::GetDisplayRootFrame(aFrame
)});
7956 const gfxRect transformedBounds
= transform
.TransformAndClipBounds(
7957 gfxRect(overflow
.x
, overflow
.y
, overflow
.width
, overflow
.height
),
7958 gfxRect::MaxIntRect());
7959 const nsSize frameSize
=
7960 nsSize(transformedBounds
.width
, transformedBounds
.height
);
7962 uint64_t maxLimitArea
= uint64_t(maxSize
.width
) * maxSize
.height
;
7963 uint64_t frameArea
= uint64_t(frameSize
.width
) * frameSize
.height
;
7964 if (frameArea
<= maxLimitArea
&& frameSize
<= absoluteLimit
) {
7965 *aDirtyRect
= overflow
;
7966 result
.mDecision
= PrerenderDecision::Full
;
7970 if (ShouldUsePartialPrerender(aFrame
)) {
7971 *aDirtyRect
= nsLayoutUtils::ComputePartialPrerenderArea(
7972 aFrame
, untransformedDirtyRect
, overflow
, maxSize
);
7973 result
.mDecision
= PrerenderDecision::Partial
;
7977 if (frameArea
> maxLimitArea
) {
7978 uint64_t appUnitsPerPixel
= AppUnitsPerCSSPixel();
7979 EffectCompositor::SetPerformanceWarning(
7980 aFrame
, transformSet
,
7981 AnimationPerformanceWarning(
7982 AnimationPerformanceWarning::Type::ContentTooLargeArea
,
7984 int(frameArea
/ (appUnitsPerPixel
* appUnitsPerPixel
)),
7985 int(maxLimitArea
/ (appUnitsPerPixel
* appUnitsPerPixel
)),
7988 EffectCompositor::SetPerformanceWarning(
7989 aFrame
, transformSet
,
7990 AnimationPerformanceWarning(
7991 AnimationPerformanceWarning::Type::ContentTooLarge
,
7993 nsPresContext::AppUnitsToIntCSSPixels(frameSize
.width
),
7994 nsPresContext::AppUnitsToIntCSSPixels(frameSize
.height
),
7995 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit
.width
),
7996 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit
.height
),
7997 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit
.width
),
7998 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit
.height
),
8005 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
8006 * visible or hit. */
8007 static bool IsFrameVisible(nsIFrame
* aFrame
, const Matrix4x4
& aMatrix
) {
8008 if (aMatrix
.IsSingular()) {
8011 if (aFrame
->BackfaceIsHidden() && aMatrix
.IsBackfaceVisible()) {
8017 const Matrix4x4Flagged
& nsDisplayTransform::GetTransform() const {
8022 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8024 if (mHasTransformGetter
) {
8025 mTransform
.emplace((mFrame
->GetTransformGetter())(mFrame
, scale
));
8027 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame
.x
, scale
),
8028 NSAppUnitsToFloatPixels(mToReferenceFrame
.y
, scale
), 0.0f
);
8029 mTransform
->ChangeBasis(newOrigin
.x
, newOrigin
.y
, newOrigin
.z
);
8030 } else if (!mIsTransformSeparator
) {
8031 DebugOnly
<bool> isReference
= mFrame
->IsTransformed() ||
8032 mFrame
->Combines3DTransformWithAncestors() ||
8033 mFrame
->Extend3DContext();
8034 MOZ_ASSERT(isReference
);
8036 GetResultingTransformMatrix(mFrame
, ToReferenceFrame(), scale
,
8037 INCLUDE_PERSPECTIVE
| OFFSET_BY_ORIGIN
));
8039 // Use identity matrix
8040 mTransform
.emplace();
8046 const Matrix4x4Flagged
& nsDisplayTransform::GetInverseTransform() const {
8047 if (mInverseTransform
) {
8048 return *mInverseTransform
;
8051 MOZ_ASSERT(!GetTransform().IsSingular());
8053 mInverseTransform
.emplace(GetTransform().Inverse());
8055 return *mInverseTransform
;
8058 Matrix4x4
nsDisplayTransform::GetTransformForRendering(
8059 LayoutDevicePoint
* aOutOrigin
) const {
8060 if (!mFrame
->HasPerspective() || mHasTransformGetter
||
8061 mIsTransformSeparator
) {
8062 if (!mHasTransformGetter
&& !mIsTransformSeparator
&& aOutOrigin
) {
8063 // If aOutOrigin is provided, put the offset to origin into it, because
8064 // we need to keep it separate for webrender. The combination of
8065 // *aOutOrigin and the returned matrix here should always be equivalent
8066 // to what GetTransform() would have returned.
8067 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8068 *aOutOrigin
= LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale
);
8070 // The rounding behavior should also be the same as GetTransform().
8071 if (nsLayoutUtils::ShouldSnapToGrid(mFrame
)) {
8072 aOutOrigin
->Round();
8074 return GetResultingTransformMatrix(mFrame
, nsPoint(0, 0), scale
,
8075 INCLUDE_PERSPECTIVE
);
8077 return GetTransform().GetMatrix();
8079 MOZ_ASSERT(!mHasTransformGetter
);
8081 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8082 // Don't include perspective transform, or the offset to origin, since
8083 // nsDisplayPerspective will handle both of those.
8084 return GetResultingTransformMatrix(mFrame
, ToReferenceFrame(), scale
, 0);
8087 const Matrix4x4
& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
8088 nsDisplayListBuilder
* aBuilder
) {
8089 MOZ_ASSERT(!mFrame
->Extend3DContext() || IsLeafOf3DContext());
8091 if (!IsLeafOf3DContext()) {
8092 return GetTransform().GetMatrix();
8095 if (!mTransformPreserves3D
) {
8096 const nsIFrame
* establisher
; // Establisher of the 3D rendering context.
8097 for (establisher
= mFrame
;
8098 establisher
&& establisher
->Combines3DTransformWithAncestors();
8100 establisher
->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
8102 const nsIFrame
* establisherReference
= aBuilder
->FindReferenceFrameFor(
8103 nsLayoutUtils::GetCrossDocParentFrame(establisher
));
8105 nsPoint offset
= establisher
->GetOffsetToCrossDoc(establisherReference
);
8106 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8108 INCLUDE_PRESERVE3D_ANCESTORS
| INCLUDE_PERSPECTIVE
| OFFSET_BY_ORIGIN
;
8109 mTransformPreserves3D
= MakeUnique
<Matrix4x4
>(
8110 GetResultingTransformMatrix(mFrame
, offset
, scale
, flags
));
8113 return *mTransformPreserves3D
;
8116 bool nsDisplayTransform::CreateWebRenderCommands(
8117 mozilla::wr::DisplayListBuilder
& aBuilder
,
8118 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
8119 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8120 nsDisplayListBuilder
* aDisplayListBuilder
) {
8121 // We want to make sure we don't pollute the transform property in the WR
8122 // stacking context by including the position of this frame (relative to the
8123 // parent reference frame). We need to keep those separate; the position of
8124 // this frame goes into the stacking context bounds while the transform goes
8125 // into the transform.
8126 LayoutDevicePoint position
;
8127 Matrix4x4 newTransformMatrix
= GetTransformForRendering(&position
);
8129 gfx::Matrix4x4
* transformForSC
= &newTransformMatrix
;
8130 if (newTransformMatrix
.IsIdentity()) {
8131 // If the transform is an identity transform, strip it out so that WR
8132 // doesn't turn this stacking context into a reference frame, as it
8133 // affects positioning. Bug 1345577 tracks a better fix.
8134 transformForSC
= nullptr;
8136 // In ChooseScaleAndSetTransform, we round the offset from the reference
8137 // frame used to adjust the transform, if there is no transform, or it
8138 // is just a translation. We need to do the same here.
8139 if (nsLayoutUtils::ShouldSnapToGrid(mFrame
)) {
8144 // We don't send animations for transform separator display items.
8145 uint64_t animationsId
=
8146 mIsTransformSeparator
8148 : AddAnimationsForWebRender(
8149 this, aManager
, aDisplayListBuilder
,
8150 IsPartialPrerender() ? Some(position
) : Nothing());
8151 wr::WrAnimationProperty prop
{
8152 wr::WrAnimationType::Transform
,
8156 Maybe
<nsDisplayTransform
*> deferredTransformItem
;
8157 if (!mFrame
->ChildrenHavePerspective()) {
8158 // If it has perspective, we create a new scroll data via the
8159 // UpdateScrollData call because that scenario is more complex. Otherwise
8160 // we can just stash the transform on the StackingContextHelper and
8161 // apply it to any scroll data that are created inside this
8162 // nsDisplayTransform.
8163 deferredTransformItem
= Some(this);
8166 // Determine if we're possibly animated (= would need an active layer in FLB).
8167 bool animated
= !mIsTransformSeparator
&&
8168 ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
8170 wr::StackingContextParams params
;
8171 params
.mBoundTransform
= &newTransformMatrix
;
8172 params
.animation
= animationsId
? &prop
: nullptr;
8173 params
.mTransformPtr
= transformForSC
;
8174 params
.prim_flags
= !BackfaceIsHidden()
8175 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
8176 : wr::PrimitiveFlags
{0};
8177 params
.mDeferredTransformItem
= deferredTransformItem
;
8178 params
.mAnimated
= animated
;
8179 // Determine if we would have to rasterize any items in local raster space
8180 // (i.e. disable subpixel AA). We don't always need to rasterize locally even
8181 // if the stacking context is possibly animated (at the cost of potentially
8182 // some false negatives with respect to will-change handling), so we pass in
8183 // this determination separately to accurately match with when FLB would
8184 // normally disable subpixel AA.
8185 params
.mRasterizeLocally
= animated
&& Frame()->HasAnimationOfTransform();
8186 params
.SetPreserve3D(mFrame
->Extend3DContext() && !mIsTransformSeparator
);
8188 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
8190 LayoutDeviceSize boundsSize
= LayoutDeviceSize::FromAppUnits(
8191 mChildBounds
.Size(), mFrame
->PresContext()->AppUnitsPerDevPixel());
8193 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
8194 params
, LayoutDeviceRect(position
, boundsSize
));
8196 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
8197 GetChildren(), this, aDisplayListBuilder
, sc
, aBuilder
, aResources
);
8201 bool nsDisplayTransform::UpdateScrollData(
8202 mozilla::layers::WebRenderScrollData
* aData
,
8203 mozilla::layers::WebRenderLayerScrollData
* aLayerData
) {
8204 if (!mFrame
->ChildrenHavePerspective()) {
8205 // This case is handled in CreateWebRenderCommands by stashing the transform
8206 // on the stacking context.
8210 aLayerData
->SetTransform(GetTransform().GetMatrix());
8211 aLayerData
->SetTransformIsPerspective(true);
8216 bool nsDisplayTransform::ShouldSkipTransform(
8217 nsDisplayListBuilder
* aBuilder
) const {
8218 return (aBuilder
->RootReferenceFrame() == mFrame
) &&
8219 aBuilder
->IsForGenerateGlyphMask();
8222 already_AddRefed
<Layer
> nsDisplayTransform::BuildLayer(
8223 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
8224 const ContainerLayerParameters
& aContainerParameters
) {
8225 // While generating a glyph mask, the transform vector of the root frame had
8226 // been applied into the target context, so stop applying it again here.
8227 const bool shouldSkipTransform
= ShouldSkipTransform(aBuilder
);
8229 /* For frames without transform, it would not be removed for
8230 * backface hidden here. But, it would be removed by the init
8231 * function of nsDisplayTransform.
8233 const Matrix4x4 newTransformMatrix
=
8234 shouldSkipTransform
? Matrix4x4() : GetTransformForRendering();
8236 uint32_t flags
= FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR
;
8237 RefPtr
<ContainerLayer
> container
=
8238 aManager
->GetLayerBuilder()->BuildContainerLayerFor(
8239 aBuilder
, aManager
, mFrame
, this, GetChildren(), aContainerParameters
,
8240 &newTransformMatrix
, flags
);
8246 // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all
8247 // flags, so we never need to explicitly unset this flag.
8248 if (mFrame
->Extend3DContext() && !mIsTransformSeparator
) {
8249 container
->SetContentFlags(container
->GetContentFlags() |
8250 Layer::CONTENT_EXTEND_3D_CONTEXT
);
8252 container
->SetContentFlags(container
->GetContentFlags() &
8253 ~Layer::CONTENT_EXTEND_3D_CONTEXT
);
8256 if (CanUseAsyncAnimations(aBuilder
)) {
8257 mFrame
->SetProperty(nsIFrame::RefusedAsyncAnimationProperty(), false);
8260 // We don't send animations for transform separator display items.
8261 if (!mIsTransformSeparator
) {
8262 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
8263 container
, aBuilder
, this, mFrame
, GetType());
8266 if (CanUseAsyncAnimations(aBuilder
) && MayBeAnimated(aBuilder
)) {
8267 // Only allow async updates to the transform if we're an animated layer,
8268 // since that's what triggers us to set the correct AGR in the constructor
8269 // and makes sure FrameLayerBuilder won't compute occlusions for this layer.
8270 container
->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
8271 /*the value is irrelevant*/ nullptr);
8272 container
->SetContentFlags(container
->GetContentFlags() |
8273 Layer::CONTENT_MAY_CHANGE_TRANSFORM
);
8275 container
->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
8276 container
->SetContentFlags(container
->GetContentFlags() &
8277 ~Layer::CONTENT_MAY_CHANGE_TRANSFORM
);
8279 return container
.forget();
8282 void nsDisplayTransform::Collect3DTransformLeaves(
8283 nsTArray
<nsDisplayTransform
*>& aLeaves
) {
8284 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
8285 aLeaves
.AppendElement(this);
8289 for (nsDisplayItem
* item
: mChildren
) {
8290 if (item
->GetType() == DisplayItemType::TYPE_PERSPECTIVE
) {
8291 auto* perspective
= static_cast<nsDisplayPerspective
*>(item
);
8292 if (!perspective
->GetChildren()->GetTop()) {
8295 item
= perspective
->GetChildren()->GetTop();
8297 MOZ_RELEASE_ASSERT(item
->GetType() == DisplayItemType::TYPE_TRANSFORM
);
8298 static_cast<nsDisplayTransform
*>(item
)->Collect3DTransformLeaves(aLeaves
);
8302 static RefPtr
<gfx::Path
> BuildPathFromPolygon(const RefPtr
<DrawTarget
>& aDT
,
8303 const gfx::Polygon
& aPolygon
) {
8304 MOZ_ASSERT(!aPolygon
.IsEmpty());
8306 RefPtr
<PathBuilder
> pathBuilder
= aDT
->CreatePathBuilder();
8307 const nsTArray
<Point4D
>& points
= aPolygon
.GetPoints();
8309 pathBuilder
->MoveTo(points
[0].As2DPoint());
8311 for (size_t i
= 1; i
< points
.Length(); ++i
) {
8312 pathBuilder
->LineTo(points
[i
].As2DPoint());
8315 pathBuilder
->Close();
8316 return pathBuilder
->Finish();
8319 void nsDisplayTransform::CollectSorted3DTransformLeaves(
8320 nsDisplayListBuilder
* aBuilder
, nsTArray
<TransformPolygon
>& aLeaves
) {
8321 std::list
<TransformPolygon
> inputLayers
;
8323 nsTArray
<nsDisplayTransform
*> leaves
;
8324 Collect3DTransformLeaves(leaves
);
8325 for (nsDisplayTransform
* item
: leaves
) {
8326 auto bounds
= LayoutDeviceRect::FromAppUnits(
8327 item
->mChildBounds
, item
->mFrame
->PresContext()->AppUnitsPerDevPixel());
8328 Matrix4x4 transform
= item
->GetAccumulatedPreserved3DTransform(aBuilder
);
8330 if (!IsFrameVisible(item
->mFrame
, transform
)) {
8333 gfx::Polygon polygon
=
8334 gfx::Polygon::FromRect(gfx::Rect(bounds
.ToUnknownRect()));
8336 polygon
.TransformToScreenSpace(transform
);
8338 if (polygon
.GetPoints().Length() >= 3) {
8339 inputLayers
.push_back(TransformPolygon(item
, std::move(polygon
)));
8343 if (inputLayers
.empty()) {
8347 BSPTree
<nsDisplayTransform
> tree(inputLayers
);
8348 nsTArray
<TransformPolygon
> orderedLayers(tree
.GetDrawOrder());
8350 for (TransformPolygon
& polygon
: orderedLayers
) {
8352 polygon
.data
->GetAccumulatedPreserved3DTransform(aBuilder
).Inverse();
8354 MOZ_ASSERT(polygon
.geometry
);
8355 polygon
.geometry
->TransformToLayerSpace(inverse
);
8358 aLeaves
= std::move(orderedLayers
);
8361 void nsDisplayTransform::Paint(nsDisplayListBuilder
* aBuilder
,
8363 Paint(aBuilder
, aCtx
, Nothing());
8366 void nsDisplayTransform::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
8367 const Maybe
<gfx::Polygon
>& aPolygon
) {
8368 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
8369 MOZ_ASSERT(!aPolygon
);
8370 nsTArray
<TransformPolygon
> leaves
;
8371 CollectSorted3DTransformLeaves(aBuilder
, leaves
);
8372 for (TransformPolygon
& item
: leaves
) {
8373 item
.data
->Paint(aBuilder
, aCtx
, item
.geometry
);
8378 gfxContextMatrixAutoSaveRestore
saveMatrix(aCtx
);
8379 Matrix4x4 trans
= GetAccumulatedPreserved3DTransform(aBuilder
);
8380 if (!IsFrameVisible(mFrame
, trans
)) {
8385 if (trans
.CanDraw2D(&trans2d
)) {
8386 aCtx
->Multiply(ThebesMatrix(trans2d
));
8389 RefPtr
<gfx::Path
> path
=
8390 BuildPathFromPolygon(aCtx
->GetDrawTarget(), *aPolygon
);
8391 aCtx
->GetDrawTarget()->PushClip(path
);
8394 GetChildren()->Paint(aBuilder
, aCtx
,
8395 mFrame
->PresContext()->AppUnitsPerDevPixel());
8398 aCtx
->GetDrawTarget()->PopClip();
8403 // TODO: Implement 3d transform handling, including plane splitting and
8404 // sorting. See BasicCompositor.
8405 auto pixelBounds
= LayoutDeviceRect::FromAppUnitsToOutside(
8406 mChildBounds
, mFrame
->PresContext()->AppUnitsPerDevPixel());
8407 RefPtr
<DrawTarget
> untransformedDT
=
8408 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
8409 IntSize(pixelBounds
.Width(), pixelBounds
.Height()),
8410 SurfaceFormat::B8G8R8A8
, true);
8411 if (!untransformedDT
|| !untransformedDT
->IsValid()) {
8414 untransformedDT
->SetTransform(
8415 Matrix::Translation(-Point(pixelBounds
.X(), pixelBounds
.Y())));
8417 RefPtr
<gfxContext
> groupTarget
=
8418 gfxContext::CreatePreservingTransformOrNull(untransformedDT
);
8421 RefPtr
<gfx::Path
> path
=
8422 BuildPathFromPolygon(aCtx
->GetDrawTarget(), *aPolygon
);
8423 aCtx
->GetDrawTarget()->PushClip(path
);
8426 GetChildren()->Paint(aBuilder
, groupTarget
,
8427 mFrame
->PresContext()->AppUnitsPerDevPixel());
8430 aCtx
->GetDrawTarget()->PopClip();
8433 RefPtr
<SourceSurface
> untransformedSurf
= untransformedDT
->Snapshot();
8435 trans
.PreTranslate(pixelBounds
.X(), pixelBounds
.Y(), 0);
8436 aCtx
->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf
, trans
);
8439 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder
* aBuilder
,
8440 bool aEnforceMinimumSize
) const {
8441 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
8442 // completely bypass the main thread for this animation, so it is always
8444 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
8445 // already involved so there is less to be gained.
8446 // Therefore we check that the *post-transform* bounds of this item are
8447 // big enough to justify an active layer.
8448 if (EffectCompositor::HasAnimationsForCompositor(
8449 mFrame
, DisplayItemType::TYPE_TRANSFORM
) ||
8450 (ActiveLayerTracker::IsTransformAnimated(aBuilder
, mFrame
) &&
8451 !(aEnforceMinimumSize
&& IsItemTooSmallForActiveLayer(mFrame
)))) {
8457 nsDisplayItem::LayerState
nsDisplayTransform::GetLayerState(
8458 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
8459 const ContainerLayerParameters
& aParameters
) {
8460 // If the transform is 3d, the layer takes part in preserve-3d
8461 // sorting, or the layer is a separator then we *always* want this
8462 // to be an active layer.
8463 // Checking HasPerspective() is needed to handle perspective value 0 when
8464 // the transform is 2D.
8465 if (!GetTransform().Is2D() || Combines3DTransformWithAncestors() ||
8466 mIsTransformSeparator
|| mFrame
->HasPerspective()) {
8467 return LayerState::LAYER_ACTIVE_FORCE
;
8470 if (MayBeAnimated(aBuilder
)) {
8471 // Returns LayerState::LAYER_ACTIVE_FORCE to avoid flatterning the layer for
8472 // async animations.
8473 return LayerState::LAYER_ACTIVE_FORCE
;
8476 // Expect the child display items to have this frame as their animated
8477 // geometry root (since it will be their reference frame). If they have a
8478 // different animated geometry root, we'll make this an active layer so the
8479 // animation can be accelerated.
8480 return RequiredLayerStateForChildren(
8481 aBuilder
, aManager
, aParameters
, *GetChildren(),
8482 mAnimatedGeometryRootForChildren
, GetActiveScrolledRoot());
8485 bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
8486 nsRegion
* aVisibleRegion
) {
8487 // nsDisplayTransform::GetBounds() returns an empty rect in nested 3d context.
8488 // Calling mStoredList.RecomputeVisibility below for such transform causes the
8489 // child display items to end up with empty visible rect.
8490 // We avoid this by bailing out always if we are dealing with a 3d context.
8491 if (mFrame
->Extend3DContext() || Combines3DTransformWithAncestors()) {
8495 /* As we do this, we need to be sure to
8496 * untransform the visible rect, since we want everything that's painting to
8497 * think that it's painting in its original rectangular coordinate space.
8498 * If we can't untransform, take the entire overflow rect */
8499 nsRect untransformedVisibleRect
;
8500 if (!UntransformPaintRect(aBuilder
, &untransformedVisibleRect
)) {
8501 untransformedVisibleRect
= mFrame
->InkOverflowRectRelativeToSelf();
8505 const nsRect bounds
= GetUntransformedBounds(aBuilder
, &snap
);
8506 nsRegion visibleRegion
;
8507 visibleRegion
.And(bounds
, untransformedVisibleRect
);
8508 GetChildren()->ComputeVisibilityForSublist(aBuilder
, &visibleRegion
,
8509 visibleRegion
.GetBounds());
8514 nsRect
nsDisplayTransform::TransformUntransformedBounds(
8515 nsDisplayListBuilder
* aBuilder
, const Matrix4x4Flagged
& aMatrix
) const {
8517 const nsRect untransformedBounds
= GetUntransformedBounds(aBuilder
, &snap
);
8518 // GetTransform always operates in dev pixels.
8519 const float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8520 return nsLayoutUtils::MatrixTransformRect(untransformedBounds
, aMatrix
,
8525 * Returns the bounds for this transform. The bounds are calculated during
8526 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
8528 nsRect
nsDisplayTransform::GetBounds(nsDisplayListBuilder
* aBuilder
,
8529 bool* aSnap
) const {
8534 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder
* aBuilder
) {
8535 MOZ_ASSERT(mFrame
->Extend3DContext() || IsLeafOf3DContext());
8537 /* Some transforms can get empty bounds in 2D, but might get transformed again
8538 * and get non-empty bounds. A simple example of this would be a 180 degree
8539 * rotation getting applied twice.
8540 * We should not depend on transforming bounds level by level.
8542 * This function collects the bounds of this transform and stores it in
8543 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
8544 * down and include the bounds of the child transforms.
8545 * The bounds are transformed with the accumulated transformation matrix up to
8546 * the 3D context root coordinate space.
8548 nsDisplayListBuilder::AutoAccumulateTransform
accTransform(aBuilder
);
8549 accTransform
.Accumulate(GetTransform().GetMatrix());
8551 // Do not dive into another 3D context.
8552 if (!IsLeafOf3DContext()) {
8553 for (nsDisplayItem
* i
: *GetChildren()) {
8554 i
->DoUpdateBoundsPreserves3D(aBuilder
);
8558 /* The child transforms that extend 3D context further will have empty bounds,
8559 * so the untransformed bounds here is the bounds of all the non-preserve-3d
8560 * content under this transform.
8562 const nsRect rect
= TransformUntransformedBounds(
8563 aBuilder
, accTransform
.GetCurrentTransform());
8564 aBuilder
->AccumulateRect(rect
);
8567 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
8568 nsDisplayListBuilder
* aBuilder
) {
8569 MOZ_ASSERT(mFrame
->Combines3DTransformWithAncestors() ||
8570 IsTransformSeparator());
8571 // Updating is not going through to child 3D context.
8572 ComputeBounds(aBuilder
);
8575 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder
* aBuilder
) {
8576 UpdateUntransformedBounds(aBuilder
);
8578 if (IsTransformSeparator()) {
8579 MOZ_ASSERT(GetTransform().IsIdentity());
8580 mBounds
= mChildBounds
;
8584 if (mFrame
->Extend3DContext()) {
8585 if (!Combines3DTransformWithAncestors()) {
8586 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
8587 // collect the bounds from the child transforms.
8588 UpdateBoundsFor3D(aBuilder
);
8590 // With nested 3D transforms, the 2D bounds might not be useful.
8597 MOZ_ASSERT(!mFrame
->Extend3DContext());
8599 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
8600 // but mix-blend-mode relies on having bounds set. See bug 1556956.
8602 // A stand-alone transform.
8603 mBounds
= TransformUntransformedBounds(aBuilder
, GetTransform());
8606 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder
* aBuilder
) {
8607 MOZ_ASSERT(mFrame
->Extend3DContext() &&
8608 !mFrame
->Combines3DTransformWithAncestors() &&
8609 !IsTransformSeparator());
8611 // Always start updating from an establisher of a 3D rendering context.
8612 nsDisplayListBuilder::AutoAccumulateRect
accRect(aBuilder
);
8613 nsDisplayListBuilder::AutoAccumulateTransform
accTransform(aBuilder
);
8614 accTransform
.StartRoot();
8615 ComputeBounds(aBuilder
);
8616 mBounds
= aBuilder
->GetAccumulatedRect();
8619 void nsDisplayTransform::UpdateUntransformedBounds(
8620 nsDisplayListBuilder
* aBuilder
) {
8621 mChildBounds
= GetChildren()->GetClippedBoundsWithRespectToASR(
8622 aBuilder
, mActiveScrolledRoot
);
8629 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
8630 void nsDisplayTransform::HitTest(nsDisplayListBuilder
* aBuilder
,
8631 const nsRect
& aRect
, HitTestState
* aState
,
8632 nsTArray
<nsIFrame
*>* aOutFrames
) {
8633 if (aState
->mInPreserves3D
) {
8634 GetChildren()->HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
8638 /* Here's how this works:
8639 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
8641 * 2. Invert the matrix.
8642 * 3. Use it to transform the rect into the correct space.
8643 * 4. Pass that rect down through to the list's version of HitTest.
8645 // GetTransform always operates in dev pixels.
8646 float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8647 Matrix4x4 matrix
= GetAccumulatedPreserved3DTransform(aBuilder
);
8649 if (!IsFrameVisible(mFrame
, matrix
)) {
8653 /* We want to go from transformed-space to regular space.
8654 * Thus we have to invert the matrix, which normally does
8655 * the reverse operation (e.g. regular->transformed)
8658 /* Now, apply the transform and pass it down the channel. */
8660 nsRect resultingRect
;
8661 if (aRect
.width
== 1 && aRect
.height
== 1) {
8662 // Magic width/height indicating we're hit testing a point, not a rect
8664 matrix
.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect
.x
, factor
),
8665 NSAppUnitsToFloatPixels(aRect
.y
, factor
)));
8666 if (!point
.HasPositiveWCoord()) {
8670 Point point2d
= point
.As2DPoint();
8673 nsRect(NSFloatPixelsToAppUnits(float(point2d
.x
), factor
),
8674 NSFloatPixelsToAppUnits(float(point2d
.y
), factor
), 1, 1);
8677 Rect
originalRect(NSAppUnitsToFloatPixels(aRect
.x
, factor
),
8678 NSAppUnitsToFloatPixels(aRect
.y
, factor
),
8679 NSAppUnitsToFloatPixels(aRect
.width
, factor
),
8680 NSAppUnitsToFloatPixels(aRect
.height
, factor
));
8683 nsRect childBounds
= GetUntransformedBounds(aBuilder
, &snap
);
8684 Rect
childGfxBounds(NSAppUnitsToFloatPixels(childBounds
.x
, factor
),
8685 NSAppUnitsToFloatPixels(childBounds
.y
, factor
),
8686 NSAppUnitsToFloatPixels(childBounds
.width
, factor
),
8687 NSAppUnitsToFloatPixels(childBounds
.height
, factor
));
8689 Rect rect
= matrix
.ProjectRectBounds(originalRect
, childGfxBounds
);
8692 nsRect(NSFloatPixelsToAppUnits(float(rect
.X()), factor
),
8693 NSFloatPixelsToAppUnits(float(rect
.Y()), factor
),
8694 NSFloatPixelsToAppUnits(float(rect
.Width()), factor
),
8695 NSFloatPixelsToAppUnits(float(rect
.Height()), factor
));
8698 if (resultingRect
.IsEmpty()) {
8703 printf("Frame: %p\n", dynamic_cast<void*>(mFrame
));
8704 printf(" Untransformed point: (%f, %f)\n", resultingRect
.X(),
8706 uint32_t originalFrameCount
= aOutFrames
.Length();
8709 GetChildren()->HitTest(aBuilder
, resultingRect
, aState
, aOutFrames
);
8712 if (originalFrameCount
!= aOutFrames
.Length())
8713 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
8714 dynamic_cast<void*>(aOutFrames
.ElementAt(0)));
8715 printf("=== end of hit test ===\n");
8719 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder
* aBuilder
,
8720 const nsPoint
& aPoint
) {
8721 // GetTransform always operates in dev pixels.
8722 float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8723 Matrix4x4 matrix
= GetAccumulatedPreserved3DTransform(aBuilder
);
8725 NS_ASSERTION(IsFrameVisible(mFrame
, matrix
),
8726 "We can't have hit a frame that isn't visible!");
8728 Matrix4x4 inverse
= matrix
;
8731 inverse
.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint
.x
, factor
),
8732 NSAppUnitsToFloatPixels(aPoint
.y
, factor
)));
8734 Point point2d
= point
.As2DPoint();
8736 Point3D transformed
= matrix
.TransformPoint(Point3D(point2d
.x
, point2d
.y
, 0));
8737 return transformed
.z
;
8740 /* The transform is opaque iff the transform consists solely of scales and
8741 * translations and if the underlying content is opaque. Thus if the transform
8748 * We need b and c to be zero.
8750 * We also need to check whether the underlying opaque content completely fills
8751 * our visible rect. We use UntransformRect which expands to the axis-aligned
8752 * bounding rect, but that's OK since if
8753 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
8754 * certainly contains the actual (non-axis-aligned) untransformed rect.
8756 nsRegion
nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
8757 bool* aSnap
) const {
8760 nsRect untransformedVisible
;
8761 if (!UntransformBuildingRect(aBuilder
, &untransformedVisible
)) {
8765 const Matrix4x4Flagged
& matrix
= GetTransform();
8767 if (!matrix
.Is2D(&matrix2d
) || !matrix2d
.PreservesAxisAlignedRectangles()) {
8774 const nsRect bounds
= GetUntransformedBounds(aBuilder
, &tmpSnap
);
8775 const nsRegion opaque
= ::GetOpaqueRegion(aBuilder
, GetChildren(), bounds
);
8777 if (opaque
.Contains(untransformedVisible
)) {
8778 result
= GetBuildingRect().Intersect(GetBounds(aBuilder
, &tmpSnap
));
8783 nsRect
nsDisplayTransform::GetComponentAlphaBounds(
8784 nsDisplayListBuilder
* aBuilder
) const {
8785 if (GetChildren()->GetComponentAlphaBounds(aBuilder
).IsEmpty()) {
8790 return GetBounds(aBuilder
, &snap
);
8793 /* TransformRect takes in as parameters a rectangle (in app space) and returns
8794 * the smallest rectangle (in app space) containing the transformed image of
8795 * that rectangle. That is, it takes the four corners of the rectangle,
8796 * transforms them according to the matrix associated with the specified frame,
8797 * then returns the smallest rectangle containing the four transformed points.
8799 * @param aUntransformedBounds The rectangle (in app units) to transform.
8800 * @param aFrame The frame whose transformation should be applied.
8801 * @param aOrigin The delta from the frame origin to the coordinate space origin
8802 * @return The smallest rectangle containing the image of the transformed
8805 nsRect
nsDisplayTransform::TransformRect(const nsRect
& aUntransformedBounds
,
8806 const nsIFrame
* aFrame
,
8807 TransformReferenceBox
& aRefBox
) {
8808 MOZ_ASSERT(aFrame
, "Can't take the transform based on a null frame!");
8810 float factor
= aFrame
->PresContext()->AppUnitsPerDevPixel();
8813 INCLUDE_PERSPECTIVE
| OFFSET_BY_ORIGIN
| INCLUDE_PRESERVE3D_ANCESTORS
;
8814 FrameTransformProperties
props(aFrame
, aRefBox
, factor
);
8815 return nsLayoutUtils::MatrixTransformRect(
8816 aUntransformedBounds
,
8817 GetResultingTransformMatrixInternal(props
, aRefBox
, nsPoint(0, 0), factor
,
8822 bool nsDisplayTransform::UntransformRect(const nsRect
& aTransformedBounds
,
8823 const nsRect
& aChildBounds
,
8824 const nsIFrame
* aFrame
,
8826 MOZ_ASSERT(aFrame
, "Can't take the transform based on a null frame!");
8828 float factor
= aFrame
->PresContext()->AppUnitsPerDevPixel();
8831 INCLUDE_PERSPECTIVE
| OFFSET_BY_ORIGIN
| INCLUDE_PRESERVE3D_ANCESTORS
;
8833 Matrix4x4 transform
=
8834 GetResultingTransformMatrix(aFrame
, nsPoint(0, 0), factor
, flags
);
8835 if (transform
.IsSingular()) {
8839 RectDouble
result(NSAppUnitsToFloatPixels(aTransformedBounds
.x
, factor
),
8840 NSAppUnitsToFloatPixels(aTransformedBounds
.y
, factor
),
8841 NSAppUnitsToFloatPixels(aTransformedBounds
.width
, factor
),
8842 NSAppUnitsToFloatPixels(aTransformedBounds
.height
, factor
));
8844 RectDouble
childGfxBounds(
8845 NSAppUnitsToFloatPixels(aChildBounds
.x
, factor
),
8846 NSAppUnitsToFloatPixels(aChildBounds
.y
, factor
),
8847 NSAppUnitsToFloatPixels(aChildBounds
.width
, factor
),
8848 NSAppUnitsToFloatPixels(aChildBounds
.height
, factor
));
8850 result
= transform
.Inverse().ProjectRectBounds(result
, childGfxBounds
);
8851 *aOutRect
= nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result
), factor
);
8855 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder
* aBuilder
,
8856 const nsRect
& aRect
,
8857 nsRect
* aOutRect
) const {
8858 if (GetTransform().IsSingular()) {
8862 // GetTransform always operates in dev pixels.
8863 float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8864 RectDouble
result(NSAppUnitsToFloatPixels(aRect
.x
, factor
),
8865 NSAppUnitsToFloatPixels(aRect
.y
, factor
),
8866 NSAppUnitsToFloatPixels(aRect
.width
, factor
),
8867 NSAppUnitsToFloatPixels(aRect
.height
, factor
));
8870 nsRect childBounds
= GetUntransformedBounds(aBuilder
, &snap
);
8871 RectDouble
childGfxBounds(
8872 NSAppUnitsToFloatPixels(childBounds
.x
, factor
),
8873 NSAppUnitsToFloatPixels(childBounds
.y
, factor
),
8874 NSAppUnitsToFloatPixels(childBounds
.width
, factor
),
8875 NSAppUnitsToFloatPixels(childBounds
.height
, factor
));
8877 /* We want to untransform the matrix, so invert the transformation first! */
8878 result
= GetInverseTransform().ProjectRectBounds(result
, childGfxBounds
);
8880 *aOutRect
= nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result
), factor
);
8885 void nsDisplayTransform::WriteDebugInfo(std::stringstream
& aStream
) {
8886 aStream
<< GetTransform().GetMatrix();
8887 if (IsTransformSeparator()) {
8888 aStream
<< " transform-separator";
8890 if (IsLeafOf3DContext()) {
8891 aStream
<< " 3d-context-leaf";
8893 if (mFrame
->Extend3DContext()) {
8894 aStream
<< " extends-3d-context";
8896 if (mFrame
->Combines3DTransformWithAncestors()) {
8897 aStream
<< " combines-3d-with-ancestors";
8900 aStream
<< " prerender(";
8901 switch (mPrerenderDecision
) {
8902 case PrerenderDecision::No
:
8905 case PrerenderDecision::Partial
:
8906 aStream
<< "partial";
8908 case PrerenderDecision::Full
:
8913 aStream
<< " childrenBuildingRect" << mChildrenBuildingRect
;
8916 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder
* aBuilder
,
8918 nsDisplayList
* aList
)
8919 : nsPaintedDisplayItem(aBuilder
, aFrame
) {
8920 mList
.AppendToTop(aList
);
8921 MOZ_ASSERT(mList
.Count() == 1);
8922 MOZ_ASSERT(mList
.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM
);
8923 mAnimatedGeometryRoot
= aBuilder
->FindAnimatedGeometryRootFor(
8924 mFrame
->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME
));
8927 already_AddRefed
<Layer
> nsDisplayPerspective::BuildLayer(
8928 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
8929 const ContainerLayerParameters
& aContainerParameters
) {
8930 float appUnitsPerPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8932 Matrix4x4 perspectiveMatrix
;
8933 DebugOnly
<bool> hasPerspective
= nsDisplayTransform::ComputePerspectiveMatrix(
8934 mFrame
, appUnitsPerPixel
, perspectiveMatrix
);
8935 MOZ_ASSERT(hasPerspective
, "Why did we create nsDisplayPerspective?");
8938 * ClipListToRange can remove our child after we were created.
8940 if (!GetChildren()->GetTop()) {
8945 * The resulting matrix is still in the coordinate space of the transformed
8946 * frame. Append a translation to the reference frame coordinates.
8948 nsDisplayTransform
* transform
=
8949 static_cast<nsDisplayTransform
*>(GetChildren()->GetTop());
8952 Point3D(NSAppUnitsToFloatPixels(transform
->ToReferenceFrame().x
,
8954 NSAppUnitsToFloatPixels(transform
->ToReferenceFrame().y
,
8957 Point3D
roundedOrigin(NS_round(newOrigin
.x
), NS_round(newOrigin
.y
), 0);
8959 perspectiveMatrix
.PostTranslate(roundedOrigin
);
8961 RefPtr
<ContainerLayer
> container
=
8962 aManager
->GetLayerBuilder()->BuildContainerLayerFor(
8963 aBuilder
, aManager
, mFrame
, this, GetChildren(), aContainerParameters
,
8964 &perspectiveMatrix
, 0);
8970 // Sort of a lie, but we want to pretend that the perspective layer extends a
8971 // 3d context so that it gets its transform combined with children. Might need
8972 // a better name that reflects this use case and isn't specific to
8974 container
->SetContentFlags(container
->GetContentFlags() |
8975 Layer::CONTENT_EXTEND_3D_CONTEXT
);
8976 container
->SetTransformIsPerspective(true);
8978 return container
.forget();
8981 void nsDisplayPerspective::Paint(nsDisplayListBuilder
* aBuilder
,
8983 // Just directly recurse into children, since we'll include the persepctive
8984 // value in any nsDisplayTransform children.
8985 GetChildren()->Paint(aBuilder
, aCtx
,
8986 mFrame
->PresContext()->AppUnitsPerDevPixel());
8989 LayerState
nsDisplayPerspective::GetLayerState(
8990 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
8991 const ContainerLayerParameters
& aParameters
) {
8992 return LayerState::LAYER_ACTIVE_FORCE
;
8995 nsRegion
nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
8996 bool* aSnap
) const {
8997 if (!GetChildren()->GetTop()) {
9002 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder
, aSnap
);
9005 bool nsDisplayPerspective::CreateWebRenderCommands(
9006 mozilla::wr::DisplayListBuilder
& aBuilder
,
9007 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
9008 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
9009 nsDisplayListBuilder
* aDisplayListBuilder
) {
9010 float appUnitsPerPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
9011 Matrix4x4 perspectiveMatrix
;
9012 DebugOnly
<bool> hasPerspective
= nsDisplayTransform::ComputePerspectiveMatrix(
9013 mFrame
, appUnitsPerPixel
, perspectiveMatrix
);
9014 MOZ_ASSERT(hasPerspective
, "Why did we create nsDisplayPerspective?");
9017 * ClipListToRange can remove our child after we were created.
9019 if (!GetChildren()->GetTop()) {
9024 * The resulting matrix is still in the coordinate space of the transformed
9025 * frame. Append a translation to the reference frame coordinates.
9027 nsDisplayTransform
* transform
=
9028 static_cast<nsDisplayTransform
*>(GetChildren()->GetTop());
9031 Point3D(NSAppUnitsToFloatPixels(transform
->ToReferenceFrame().x
,
9033 NSAppUnitsToFloatPixels(transform
->ToReferenceFrame().y
,
9036 Point3D
roundedOrigin(NS_round(newOrigin
.x
), NS_round(newOrigin
.y
), 0);
9038 perspectiveMatrix
.PostTranslate(roundedOrigin
);
9040 nsIFrame
* perspectiveFrame
=
9041 mFrame
->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME
);
9043 // Passing true here is always correct, since perspective always combines
9044 // transforms with the descendants. However that'd make WR do a lot of work
9045 // that it doesn't really need to do if there aren't other transforms forming
9046 // part of the 3D context.
9048 // WR knows how to treat perspective in that case, so the only thing we need
9049 // to do is to ensure we pass true when we're involved in a 3d context in any
9050 // other way via the transform-style property on either the transformed frame
9051 // or the perspective frame in order to not confuse WR's preserve-3d code in
9054 mFrame
->Extend3DContext() || perspectiveFrame
->Extend3DContext();
9056 wr::StackingContextParams params
;
9057 params
.mTransformPtr
= &perspectiveMatrix
;
9058 params
.reference_frame_kind
= wr::WrReferenceFrameKind::Perspective
;
9059 params
.prim_flags
= !BackfaceIsHidden()
9060 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
9061 : wr::PrimitiveFlags
{0};
9062 params
.SetPreserve3D(preserve3D
);
9064 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
9066 Maybe
<uint64_t> scrollingRelativeTo
;
9067 for (auto* asr
= GetActiveScrolledRoot(); asr
; asr
= asr
->mParent
) {
9068 // In OOP documents, the root scrollable frame of the in-process root
9069 // document is always active, so using IsAncestorFrameCrossDocInProcess
9070 // should be fine here.
9071 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
9072 asr
->mScrollableFrame
->GetScrolledFrame(), perspectiveFrame
)) {
9073 scrollingRelativeTo
.emplace(asr
->GetViewId());
9078 // We put the perspective reference frame wrapping the transformed frame,
9079 // even though there may be arbitrarily nested scroll frames in between.
9081 // We need to know how many ancestor scroll-frames are we nested in, in order
9082 // for the async scrolling code in WebRender to calculate the right
9083 // transformation for the perspective contents.
9084 params
.scrolling_relative_to
= scrollingRelativeTo
.ptrOr(nullptr);
9086 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
9089 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
9090 GetChildren(), this, aDisplayListBuilder
, sc
, aBuilder
, aResources
);
9095 bool nsDisplayPerspective::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
9096 nsRegion
* aVisibleRegion
) {
9097 return mList
.ComputeVisibilityForSublist(aBuilder
, aVisibleRegion
,
9101 nsDisplayText::nsDisplayText(nsDisplayListBuilder
* aBuilder
,
9102 nsTextFrame
* aFrame
)
9103 : nsPaintedDisplayItem(aBuilder
, aFrame
),
9107 MOZ_COUNT_CTOR(nsDisplayText
);
9108 mBounds
= mFrame
->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
9110 mBounds
.Inflate(mFrame
->PresContext()->AppUnitsPerDevPixel());
9113 bool nsDisplayText::CanApplyOpacity() const {
9114 auto* f
= static_cast<nsTextFrame
*>(mFrame
);
9116 if (f
->IsSelected()) {
9120 const nsStyleText
* textStyle
= f
->StyleText();
9121 if (textStyle
->HasTextShadow()) {
9125 nsTextFrame::TextDecorations decorations
;
9126 f
->GetTextDecorations(f
->PresContext(), nsTextFrame::eResolvedColors
,
9128 if (decorations
.HasDecorationLines()) {
9135 void nsDisplayText::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
9136 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS
);
9138 DrawTargetAutoDisableSubpixelAntialiasing
disable(aCtx
->GetDrawTarget(),
9139 IsSubpixelAADisabled());
9140 RenderToContext(aCtx
, aBuilder
);
9143 bool nsDisplayText::CreateWebRenderCommands(
9144 mozilla::wr::DisplayListBuilder
& aBuilder
,
9145 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
9146 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
9147 nsDisplayListBuilder
* aDisplayListBuilder
) {
9148 auto* f
= static_cast<nsTextFrame
*>(mFrame
);
9149 auto appUnitsPerDevPixel
= f
->PresContext()->AppUnitsPerDevPixel();
9151 nsRect bounds
= f
->WebRenderBounds() + ToReferenceFrame();
9153 bounds
.Inflate(appUnitsPerDevPixel
);
9155 if (bounds
.IsEmpty()) {
9159 // For large font sizes, punt to a blob image, to avoid the blurry rendering
9160 // that results from WR clamping the glyph size used for rasterization.
9162 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
9164 // This is not strictly accurate, as final used font sizes might not be the
9165 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
9166 // altering the used size of the font actually used).
9167 // It also fails to consider how transforms might affect the device-font-size
9168 // that webrender uses (and clamps).
9169 // But it should be near enough for practical purposes; the limitations just
9170 // mean we might sometimes end up with webrender still applying some bitmap
9171 // scaling, or bail out when we didn't really need to.
9172 constexpr float kWebRenderFontSizeLimit
= 320.0;
9173 f
->EnsureTextRun(nsTextFrame::eInflated
);
9174 gfxTextRun
* textRun
= f
->GetTextRun(nsTextFrame::eInflated
);
9176 textRun
->GetFontGroup()->GetStyle()->size
> kWebRenderFontSizeLimit
) {
9180 gfx::Point deviceOffset
=
9181 LayoutDevicePoint::FromAppUnits(bounds
.TopLeft(), appUnitsPerDevPixel
)
9184 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
9185 // frames) let's us early reject a bunch of things, but it can produce
9186 // incorrect results for shadows, because they can translate things back into
9187 // view. Also if we're selected we might have some shadows from the
9188 // ::selected and ::inctive-selected pseudo-selectors. So don't do this
9189 // optimization if we have shadows or a selection.
9190 if (!(f
->IsSelected() || f
->StyleText()->HasTextShadow())) {
9191 nsRect visible
= GetPaintRect();
9192 visible
.Inflate(3 * appUnitsPerDevPixel
);
9193 bounds
= bounds
.Intersect(visible
);
9196 RefPtr
<gfxContext
> textDrawer
= aBuilder
.GetTextContext(
9197 aResources
, aSc
, aManager
, this, bounds
, deviceOffset
);
9199 aBuilder
.StartGroup(this);
9201 RenderToContext(textDrawer
, aDisplayListBuilder
, true);
9202 const bool result
= textDrawer
->GetTextDrawer()->Finish();
9205 aBuilder
.FinishGroup();
9207 aBuilder
.CancelGroup(true);
9213 void nsDisplayText::RenderToContext(gfxContext
* aCtx
,
9214 nsDisplayListBuilder
* aBuilder
,
9215 bool aIsRecording
) {
9216 nsTextFrame
* f
= static_cast<nsTextFrame
*>(mFrame
);
9218 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
9219 // antialiased pixels beyond the measured text extents.
9220 // This is temporary until we do this in the actual calculation of text
9222 auto A2D
= mFrame
->PresContext()->AppUnitsPerDevPixel();
9223 LayoutDeviceRect extraVisible
=
9224 LayoutDeviceRect::FromAppUnits(GetPaintRect(), A2D
);
9225 extraVisible
.Inflate(1);
9227 gfxRect
pixelVisible(extraVisible
.x
, extraVisible
.y
, extraVisible
.width
,
9228 extraVisible
.height
);
9229 pixelVisible
.Inflate(2);
9230 pixelVisible
.RoundOut();
9232 bool willClip
= !aBuilder
->IsForGenerateGlyphMask() && !aIsRecording
;
9235 aCtx
->Rectangle(pixelVisible
);
9239 NS_ASSERTION(mVisIStartEdge
>= 0, "illegal start edge");
9240 NS_ASSERTION(mVisIEndEdge
>= 0, "illegal end edge");
9242 gfxContextMatrixAutoSaveRestore matrixSR
;
9244 nsPoint framePt
= ToReferenceFrame();
9245 if (f
->Style()->IsTextCombined()) {
9246 float scaleFactor
= nsTextFrame::GetTextCombineScaleFactor(f
);
9247 if (scaleFactor
!= 1.0f
) {
9248 if (auto* textDrawer
= aCtx
->GetTextDrawer()) {
9249 // WebRender doesn't support scaling text like this yet
9250 textDrawer
->FoundUnsupportedFeature();
9253 matrixSR
.SetContext(aCtx
);
9254 // Setup matrix to compress text for text-combine-upright if
9255 // necessary. This is done here because we want selection be
9256 // compressed at the same time as text.
9257 gfxPoint pt
= nsLayoutUtils::PointToGfxPoint(framePt
, A2D
);
9258 gfxMatrix mat
= aCtx
->CurrentMatrixDouble()
9260 .PreScale(scaleFactor
, 1.0)
9262 aCtx
->SetMatrixDouble(mat
);
9265 nsTextFrame::PaintTextParams
params(aCtx
);
9266 params
.framePt
= gfx::Point(framePt
.x
, framePt
.y
);
9267 params
.dirtyRect
= extraVisible
;
9269 if (aBuilder
->IsForGenerateGlyphMask()) {
9270 params
.state
= nsTextFrame::PaintTextParams::GenerateTextMask
;
9272 params
.state
= nsTextFrame::PaintTextParams::PaintText
;
9275 f
->PaintText(params
, mVisIStartEdge
, mVisIEndEdge
, ToReferenceFrame(),
9276 f
->IsSelected(), mOpacity
);
9283 class nsDisplayTextGeometry
: public nsDisplayItemGenericGeometry
{
9285 nsDisplayTextGeometry(nsDisplayText
* aItem
, nsDisplayListBuilder
* aBuilder
)
9286 : nsDisplayItemGenericGeometry(aItem
, aBuilder
),
9287 mOpacity(aItem
->Opacity()),
9288 mVisIStartEdge(aItem
->VisIStartEdge()),
9289 mVisIEndEdge(aItem
->VisIEndEdge()) {
9290 nsTextFrame
* f
= static_cast<nsTextFrame
*>(aItem
->Frame());
9291 f
->GetTextDecorations(f
->PresContext(), nsTextFrame::eResolvedColors
,
9296 * We store the computed text decorations here since they are
9297 * computed using style data from parent frames. Any changes to these
9298 * styles will only invalidate the parent frame and not this frame.
9300 nsTextFrame::TextDecorations mDecorations
;
9302 nscoord mVisIStartEdge
;
9303 nscoord mVisIEndEdge
;
9306 nsDisplayItemGeometry
* nsDisplayText::AllocateGeometry(
9307 nsDisplayListBuilder
* aBuilder
) {
9308 return new nsDisplayTextGeometry(this, aBuilder
);
9311 void nsDisplayText::ComputeInvalidationRegion(
9312 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
9313 nsRegion
* aInvalidRegion
) const {
9314 const nsDisplayTextGeometry
* geometry
=
9315 static_cast<const nsDisplayTextGeometry
*>(aGeometry
);
9316 nsTextFrame
* f
= static_cast<nsTextFrame
*>(mFrame
);
9318 nsTextFrame::TextDecorations decorations
;
9319 f
->GetTextDecorations(f
->PresContext(), nsTextFrame::eResolvedColors
,
9323 const nsRect
& newRect
= geometry
->mBounds
;
9324 nsRect oldRect
= GetBounds(aBuilder
, &snap
);
9325 if (decorations
!= geometry
->mDecorations
||
9326 mVisIStartEdge
!= geometry
->mVisIStartEdge
||
9327 mVisIEndEdge
!= geometry
->mVisIEndEdge
||
9328 !oldRect
.IsEqualInterior(newRect
) ||
9329 !geometry
->mBorderRect
.IsEqualInterior(GetBorderRect()) ||
9330 mOpacity
!= geometry
->mOpacity
) {
9331 aInvalidRegion
->Or(oldRect
, newRect
);
9335 void nsDisplayText::WriteDebugInfo(std::stringstream
& aStream
) {
9339 nsTextFrame
* f
= static_cast<nsTextFrame
*>(mFrame
);
9341 int32_t totalContentLength
;
9342 f
->ToCString(buf
, &totalContentLength
);
9344 aStream
<< buf
.get() << "\")";
9348 nsDisplayEffectsBase::nsDisplayEffectsBase(
9349 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
9350 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aClearClipChain
)
9351 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
,
9353 mHandleOpacity(false) {
9354 MOZ_COUNT_CTOR(nsDisplayEffectsBase
);
9357 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder
* aBuilder
,
9359 nsDisplayList
* aList
)
9360 : nsDisplayWrapList(aBuilder
, aFrame
, aList
), mHandleOpacity(false) {
9361 MOZ_COUNT_CTOR(nsDisplayEffectsBase
);
9364 nsRegion
nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
9365 bool* aSnap
) const {
9370 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder
* aBuilder
,
9371 const nsRect
& aRect
, HitTestState
* aState
,
9372 nsTArray
<nsIFrame
*>* aOutFrames
) {
9373 nsPoint
rectCenter(aRect
.x
+ aRect
.width
/ 2, aRect
.y
+ aRect
.height
/ 2);
9374 if (SVGIntegrationUtils::HitTestFrameForEffects(
9375 mFrame
, rectCenter
- ToReferenceFrame())) {
9376 mList
.HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
9380 gfxRect
nsDisplayEffectsBase::BBoxInUserSpace() const {
9381 return SVGUtils::GetBBox(mFrame
);
9384 gfxPoint
nsDisplayEffectsBase::UserSpaceOffset() const {
9385 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame
);
9388 void nsDisplayEffectsBase::ComputeInvalidationRegion(
9389 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
9390 nsRegion
* aInvalidRegion
) const {
9391 auto* geometry
= static_cast<const nsDisplaySVGEffectGeometry
*>(aGeometry
);
9393 nsRect bounds
= GetBounds(aBuilder
, &snap
);
9394 if (geometry
->mFrameOffsetToReferenceFrame
!= ToReferenceFrame() ||
9395 geometry
->mUserSpaceOffset
!= UserSpaceOffset() ||
9396 !geometry
->mBBox
.IsEqualInterior(BBoxInUserSpace()) ||
9397 geometry
->mOpacity
!= mFrame
->StyleEffects()->mOpacity
||
9398 geometry
->mHandleOpacity
!= ShouldHandleOpacity()) {
9399 // Filter and mask output can depend on the location of the frame's user
9400 // space and on the frame's BBox. We need to invalidate if either of these
9401 // change relative to the reference frame.
9402 // Invalidations from our inactive layer manager are not enough to catch
9403 // some of these cases because filters can produce output even if there's
9404 // nothing in the filter input.
9405 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
9409 bool nsDisplayEffectsBase::ValidateSVGFrame() {
9410 const nsIContent
* content
= mFrame
->GetContent();
9411 bool hasSVGLayout
= mFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
);
9413 ISVGDisplayableFrame
* svgFrame
= do_QueryFrame(mFrame
);
9414 if (!svgFrame
|| !mFrame
->GetContent()->IsSVGElement()) {
9415 NS_ASSERTION(false, "why?");
9418 if (!static_cast<const SVGElement
*>(content
)->HasValidDimensions()) {
9419 return false; // The SVG spec says not to draw filters for this
9426 typedef SVGIntegrationUtils::PaintFramesParams PaintFramesParams
;
9428 static void ComputeMaskGeometry(PaintFramesParams
& aParams
) {
9429 // Properties are added lazily and may have been removed by a restyle, so
9430 // make sure all applicable ones are set again.
9431 nsIFrame
* firstFrame
=
9432 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams
.frame
);
9434 const nsStyleSVGReset
* svgReset
= firstFrame
->StyleSVGReset();
9436 nsTArray
<SVGMaskFrame
*> maskFrames
;
9437 // XXX check return value?
9438 SVGObserverUtils::GetAndObserveMasks(firstFrame
, &maskFrames
);
9440 if (maskFrames
.Length() == 0) {
9444 gfxContext
& ctx
= aParams
.ctx
;
9445 nsIFrame
* frame
= aParams
.frame
;
9447 nsPoint offsetToUserSpace
=
9448 nsLayoutUtils::ComputeOffsetToUserSpace(aParams
.builder
, aParams
.frame
);
9450 gfxPoint devPixelOffsetToUserSpace
= nsLayoutUtils::PointToGfxPoint(
9451 offsetToUserSpace
, frame
->PresContext()->AppUnitsPerDevPixel());
9453 gfxContextMatrixAutoSaveRestore
matSR(&ctx
);
9454 ctx
.SetMatrixDouble(
9455 ctx
.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace
));
9457 // Convert boaderArea and dirtyRect to user space.
9458 int32_t appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
9459 nsRect userSpaceBorderArea
= aParams
.borderArea
- offsetToUserSpace
;
9460 nsRect userSpaceDirtyRect
= aParams
.dirtyRect
- offsetToUserSpace
;
9462 // Union all mask layer rectangles in user space.
9463 gfxRect maskInUserSpace
;
9464 for (size_t i
= 0; i
< maskFrames
.Length(); i
++) {
9465 SVGMaskFrame
* maskFrame
= maskFrames
[i
];
9466 gfxRect currentMaskSurfaceRect
;
9469 currentMaskSurfaceRect
= maskFrame
->GetMaskArea(aParams
.frame
);
9471 nsCSSRendering::ImageLayerClipState clipState
;
9472 nsCSSRendering::GetImageLayerClip(
9473 svgReset
->mMask
.mLayers
[i
], frame
, *frame
->StyleBorder(),
9474 userSpaceBorderArea
, userSpaceDirtyRect
, false, /* aWillPaintBorder */
9475 appUnitsPerDevPixel
, &clipState
);
9476 currentMaskSurfaceRect
= clipState
.mDirtyRectInDevPx
;
9479 maskInUserSpace
= maskInUserSpace
.Union(currentMaskSurfaceRect
);
9482 if (!maskInUserSpace
.IsEmpty()) {
9483 aParams
.maskRect
= Some(ToRect(maskInUserSpace
));
9485 aParams
.maskRect
= Nothing();
9489 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
9490 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
9491 const ActiveScrolledRoot
* aActiveScrolledRoot
)
9492 : nsDisplayEffectsBase(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
9493 mApplyOpacityWithSimpleClipPath(false) {
9494 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths
);
9496 nsPresContext
* presContext
= mFrame
->PresContext();
9498 aBuilder
->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE
;
9499 const nsStyleSVGReset
* svgReset
= aFrame
->StyleSVGReset();
9500 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i
, svgReset
->mMask
) {
9501 const auto& layer
= svgReset
->mMask
.mLayers
[i
];
9502 if (!layer
.mImage
.IsResolved()) {
9505 const nsRect
& borderArea
= mFrame
->GetRectRelativeToSelf();
9506 // NOTE(emilio): We only care about the dest rect so we don't bother
9507 // computing a clip.
9508 bool isTransformedFixed
= false;
9509 nsBackgroundLayerState state
= nsCSSRendering::PrepareImageLayer(
9510 presContext
, aFrame
, flags
, borderArea
, borderArea
, layer
,
9511 &isTransformedFixed
);
9512 mDestRects
.AppendElement(state
.mDestArea
);
9516 static bool CanMergeDisplayMaskFrame(nsIFrame
* aFrame
) {
9517 // Do not merge items for box-decoration-break:clone elements,
9518 // since each box should have its own mask in that case.
9519 if (aFrame
->StyleBorder()->mBoxDecorationBreak
==
9520 mozilla::StyleBoxDecorationBreak::Clone
) {
9524 // Do not merge if either frame has a mask. Continuation frames should apply
9525 // the mask independently (just like nsDisplayBackgroundImage).
9526 if (aFrame
->StyleSVGReset()->HasMask()) {
9533 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem
* aItem
) const {
9534 // Items for the same content element should be merged into a single
9535 // compositing group.
9536 if (!HasDifferentFrame(aItem
) || !HasSameTypeAndClip(aItem
) ||
9537 !HasSameContent(aItem
)) {
9541 return CanMergeDisplayMaskFrame(mFrame
) &&
9542 CanMergeDisplayMaskFrame(aItem
->Frame());
9545 bool nsDisplayMasksAndClipPaths::IsValidMask() {
9546 if (!ValidateSVGFrame()) {
9550 if (mFrame
->StyleEffects()->mOpacity
== 0.0f
&& mHandleOpacity
) {
9554 nsIFrame
* firstFrame
=
9555 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame
);
9557 if (SVGObserverUtils::GetAndObserveClipPath(firstFrame
, nullptr) ==
9558 SVGObserverUtils::eHasRefsSomeInvalid
||
9559 SVGObserverUtils::GetAndObserveMasks(firstFrame
, nullptr) ==
9560 SVGObserverUtils::eHasRefsSomeInvalid
) {
9567 already_AddRefed
<Layer
> nsDisplayMasksAndClipPaths::BuildLayer(
9568 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
9569 const ContainerLayerParameters
& aContainerParameters
) {
9570 if (!IsValidMask()) {
9574 RefPtr
<ContainerLayer
> container
=
9575 aManager
->GetLayerBuilder()->BuildContainerLayerFor(
9576 aBuilder
, aManager
, mFrame
, this, &mList
, aContainerParameters
,
9579 return container
.forget();
9582 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder
* aBuilder
,
9583 gfxContext
* aMaskContext
,
9584 bool* aMaskPainted
) {
9585 MOZ_ASSERT(aMaskContext
->GetDrawTarget()->GetFormat() == SurfaceFormat::A8
);
9587 imgDrawingParams
imgParams(aBuilder
->GetImageDecodeFlags());
9588 nsRect borderArea
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
9589 SVGIntegrationUtils::PaintFramesParams
params(*aMaskContext
, mFrame
, mBounds
,
9590 borderArea
, aBuilder
, nullptr,
9591 mHandleOpacity
, imgParams
);
9592 ComputeMaskGeometry(params
);
9593 bool maskIsComplete
= false;
9594 bool painted
= SVGIntegrationUtils::PaintMask(params
, maskIsComplete
);
9596 *aMaskPainted
= painted
;
9599 nsDisplayMasksAndClipPathsGeometry::UpdateDrawResult(this, imgParams
.result
);
9601 return maskIsComplete
&&
9602 (imgParams
.result
== ImgDrawResult::SUCCESS
||
9603 imgParams
.result
== ImgDrawResult::SUCCESS_NOT_COMPLETE
||
9604 imgParams
.result
== ImgDrawResult::WRONG_SIZE
);
9607 LayerState
nsDisplayMasksAndClipPaths::GetLayerState(
9608 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
9609 const ContainerLayerParameters
& aParameters
) {
9610 if (CanPaintOnMaskLayer(aManager
)) {
9611 LayerState result
= RequiredLayerStateForChildren(
9612 aBuilder
, aManager
, aParameters
, mList
, GetAnimatedGeometryRoot(),
9613 GetActiveScrolledRoot());
9614 // When we're not active, FrameLayerBuilder will call PaintAsLayer()
9615 // on us during painting. In that case we don't want a mask layer to
9616 // be created, because PaintAsLayer() takes care of applying the mask.
9617 // So we return LayerState::LAYER_SVG_EFFECTS instead of
9618 // LayerState::LAYER_INACTIVE so that FrameLayerBuilder doesn't set a mask
9619 // layer on our layer.
9620 return result
== LayerState::LAYER_INACTIVE
? LayerState::LAYER_SVG_EFFECTS
9624 return LayerState::LAYER_SVG_EFFECTS
;
9627 bool nsDisplayMasksAndClipPaths::CanPaintOnMaskLayer(LayerManager
* aManager
) {
9628 if (!aManager
->IsWidgetLayerManager()) {
9632 if (!SVGIntegrationUtils::IsMaskResourceReady(mFrame
)) {
9636 if (StaticPrefs::layers_draw_mask_debug()) {
9640 // We don't currently support this item creating a mask
9641 // for both the clip-path, and rounded rect clipping.
9642 if (GetClip().GetRoundedRectCount() != 0) {
9649 bool nsDisplayMasksAndClipPaths::ComputeVisibility(
9650 nsDisplayListBuilder
* aBuilder
, nsRegion
* aVisibleRegion
) {
9651 // Our children may be made translucent or arbitrarily deformed so we should
9652 // not allow them to subtract area from aVisibleRegion.
9653 nsRegion
childrenVisible(GetPaintRect());
9654 nsRect r
= GetPaintRect().Intersect(mList
.GetClippedBounds(aBuilder
));
9655 mList
.ComputeVisibilityForSublist(aBuilder
, &childrenVisible
, r
);
9659 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
9660 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
9661 nsRegion
* aInvalidRegion
) const {
9662 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder
, aGeometry
,
9666 static_cast<const nsDisplayMasksAndClipPathsGeometry
*>(aGeometry
);
9668 nsRect bounds
= GetBounds(aBuilder
, &snap
);
9670 if (mDestRects
.Length() != geometry
->mDestRects
.Length()) {
9671 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
9673 for (size_t i
= 0; i
< mDestRects
.Length(); i
++) {
9674 if (!mDestRects
[i
].IsEqualInterior(geometry
->mDestRects
[i
])) {
9675 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
9681 if (aBuilder
->ShouldSyncDecodeImages() &&
9682 geometry
->ShouldInvalidateToSyncDecodeImages()) {
9683 const nsStyleSVGReset
* svgReset
= mFrame
->StyleSVGReset();
9684 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i
, svgReset
->mMask
) {
9685 const auto& image
= svgReset
->mMask
.mLayers
[i
].mImage
;
9686 if (image
.IsImageRequestType()) {
9687 aInvalidRegion
->Or(*aInvalidRegion
, bounds
);
9694 void nsDisplayMasksAndClipPaths::PaintAsLayer(nsDisplayListBuilder
* aBuilder
,
9696 LayerManager
* aManager
) {
9697 // Clip the drawing target by mVisibleRect, which contains the visible
9698 // region of the target frame and its out-of-flow and inflow descendants.
9699 gfxContext
* context
= aCtx
;
9701 Rect bounds
= NSRectToRect(GetPaintRect(),
9702 mFrame
->PresContext()->AppUnitsPerDevPixel());
9704 context
->Clip(bounds
);
9706 imgDrawingParams
imgParams(aBuilder
->GetImageDecodeFlags());
9707 nsRect borderArea
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
9708 SVGIntegrationUtils::PaintFramesParams
params(*aCtx
, mFrame
, GetPaintRect(),
9709 borderArea
, aBuilder
, aManager
,
9710 mHandleOpacity
, imgParams
);
9712 ComputeMaskGeometry(params
);
9714 SVGIntegrationUtils::PaintMaskAndClipPath(params
);
9718 nsDisplayMasksAndClipPathsGeometry::UpdateDrawResult(this, imgParams
.result
);
9721 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
9722 nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
9723 const std::function
<void()>& aPaintChildren
) {
9724 // Clip the drawing target by mVisibleRect, which contains the visible
9725 // region of the target frame and its out-of-flow and inflow descendants.
9726 gfxContext
* context
= aCtx
;
9728 Rect bounds
= NSRectToRect(GetPaintRect(),
9729 mFrame
->PresContext()->AppUnitsPerDevPixel());
9731 context
->Clip(bounds
);
9733 imgDrawingParams
imgParams(aBuilder
->GetImageDecodeFlags());
9734 nsRect borderArea
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
9735 SVGIntegrationUtils::PaintFramesParams
params(*aCtx
, mFrame
, GetPaintRect(),
9736 borderArea
, aBuilder
, nullptr,
9737 mHandleOpacity
, imgParams
);
9739 ComputeMaskGeometry(params
);
9741 SVGIntegrationUtils::PaintMaskAndClipPath(params
, aPaintChildren
);
9745 nsDisplayMasksAndClipPathsGeometry::UpdateDrawResult(this, imgParams
.result
);
9748 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder
* aBuilder
,
9750 if (!IsValidMask()) {
9753 PaintWithContentsPaintCallback(aBuilder
, aCtx
, [&] {
9754 GetChildren()->Paint(aBuilder
, aCtx
,
9755 mFrame
->PresContext()->AppUnitsPerDevPixel());
9759 static Maybe
<wr::WrClipId
> CreateSimpleClipRegion(
9760 const nsDisplayMasksAndClipPaths
& aDisplayItem
,
9761 wr::DisplayListBuilder
& aBuilder
) {
9762 nsIFrame
* frame
= aDisplayItem
.Frame();
9763 auto* style
= frame
->StyleSVGReset();
9764 MOZ_ASSERT(style
->HasClipPath() || style
->HasMask());
9765 if (!SVGIntegrationUtils::UsingSimpleClipPathForFrame(frame
)) {
9769 const auto& clipPath
= style
->mClipPath
;
9770 const auto& shape
= *clipPath
.AsShape()._0
;
9772 auto appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
9773 const nsRect refBox
=
9774 nsLayoutUtils::ComputeGeometryBox(frame
, clipPath
.AsShape()._1
);
9776 AutoTArray
<wr::ComplexClipRegion
, 1> clipRegions
;
9778 wr::LayoutRect rect
;
9779 switch (shape
.tag
) {
9780 case StyleBasicShape::Tag::Inset
: {
9781 const nsRect insetRect
= ShapeUtils::ComputeInsetRect(shape
, refBox
) +
9782 aDisplayItem
.ToReferenceFrame();
9784 nscoord radii
[8] = {0};
9786 if (ShapeUtils::ComputeInsetRadii(shape
, refBox
, radii
)) {
9787 clipRegions
.AppendElement(
9788 wr::ToComplexClipRegion(insetRect
, radii
, appUnitsPerDevPixel
));
9791 rect
= wr::ToLayoutRect(
9792 LayoutDeviceRect::FromAppUnits(insetRect
, appUnitsPerDevPixel
));
9795 case StyleBasicShape::Tag::Ellipse
:
9796 case StyleBasicShape::Tag::Circle
: {
9797 nsPoint center
= ShapeUtils::ComputeCircleOrEllipseCenter(shape
, refBox
);
9800 if (shape
.IsEllipse()) {
9801 radii
= ShapeUtils::ComputeEllipseRadii(shape
, center
, refBox
);
9803 nscoord radius
= ShapeUtils::ComputeCircleRadius(shape
, center
, refBox
);
9804 radii
= {radius
, radius
};
9807 nsRect
ellipseRect(aDisplayItem
.ToReferenceFrame() + center
-
9808 nsPoint(radii
.width
, radii
.height
),
9811 nscoord ellipseRadii
[8];
9812 for (const auto corner
: mozilla::AllPhysicalHalfCorners()) {
9813 ellipseRadii
[corner
] =
9814 HalfCornerIsX(corner
) ? radii
.width
: radii
.height
;
9817 clipRegions
.AppendElement(wr::ToComplexClipRegion(
9818 ellipseRect
, ellipseRadii
, appUnitsPerDevPixel
));
9820 rect
= wr::ToLayoutRect(
9821 LayoutDeviceRect::FromAppUnits(ellipseRect
, appUnitsPerDevPixel
));
9825 // Please don't add more exceptions, try to find a way to define the clip
9826 // without using a mask image.
9828 // And if you _really really_ need to add an exception, add it to
9829 // SVGIntegrationUtils::UsingSimpleClipPathForFrame
9830 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
9833 wr::WrClipId clipId
= aBuilder
.DefineClip(Nothing(), rect
, &clipRegions
);
9834 return Some(clipId
);
9837 static void FillPolygonDataForDisplayItem(
9838 const nsDisplayMasksAndClipPaths
& aDisplayItem
,
9839 nsTArray
<wr::LayoutPoint
>& aPoints
, wr::FillRule
& aFillRule
) {
9840 nsIFrame
* frame
= aDisplayItem
.Frame();
9841 const auto* style
= frame
->StyleSVGReset();
9842 bool isPolygon
= style
->HasClipPath() && style
->mClipPath
.IsShape() &&
9843 style
->mClipPath
.AsShape()._0
->IsPolygon();
9848 const auto& clipPath
= style
->mClipPath
;
9849 const auto& shape
= *clipPath
.AsShape()._0
;
9850 const nsRect refBox
=
9851 nsLayoutUtils::ComputeGeometryBox(frame
, clipPath
.AsShape()._1
);
9853 // We only fill polygon data for polygons that are below a complexity
9855 nsTArray
<nsPoint
> vertices
=
9856 ShapeUtils::ComputePolygonVertices(shape
, refBox
);
9857 if (vertices
.Length() > wr::POLYGON_CLIP_VERTEX_MAX
) {
9861 auto appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
9863 for (size_t i
= 0; i
< vertices
.Length(); ++i
) {
9864 wr::LayoutPoint point
= wr::ToLayoutPoint(
9865 LayoutDevicePoint::FromAppUnits(vertices
[i
], appUnitsPerDevPixel
));
9866 aPoints
.AppendElement(point
);
9869 aFillRule
= (shape
.AsPolygon().fill
== StyleFillRule::Nonzero
)
9870 ? wr::FillRule::Nonzero
9871 : wr::FillRule::Evenodd
;
9874 static Maybe
<wr::WrClipId
> CreateWRClipPathAndMasks(
9875 nsDisplayMasksAndClipPaths
* aDisplayItem
, const LayoutDeviceRect
& aBounds
,
9876 wr::IpcResourceUpdateQueue
& aResources
, wr::DisplayListBuilder
& aBuilder
,
9877 const StackingContextHelper
& aSc
, layers::RenderRootStateManager
* aManager
,
9878 nsDisplayListBuilder
* aDisplayListBuilder
) {
9879 if (auto clip
= CreateSimpleClipRegion(*aDisplayItem
, aBuilder
)) {
9883 Maybe
<wr::ImageMask
> mask
= aManager
->CommandBuilder().BuildWrMaskImage(
9884 aDisplayItem
, aBuilder
, aResources
, aSc
, aDisplayListBuilder
, aBounds
);
9889 // We couldn't create a simple clip region, but before we create an image
9890 // mask clip, see if we can get a polygon clip to add to it.
9891 nsTArray
<wr::LayoutPoint
> points
;
9892 wr::FillRule fillRule
= wr::FillRule::Nonzero
;
9893 FillPolygonDataForDisplayItem(*aDisplayItem
, points
, fillRule
);
9895 wr::WrClipId clipId
=
9896 aBuilder
.DefineImageMaskClip(mask
.ref(), points
, fillRule
);
9898 return Some(clipId
);
9901 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
9902 mozilla::wr::DisplayListBuilder
& aBuilder
,
9903 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
9904 const StackingContextHelper
& aSc
,
9905 mozilla::layers::RenderRootStateManager
* aManager
,
9906 nsDisplayListBuilder
* aDisplayListBuilder
) {
9908 auto appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
9909 nsRect displayBounds
= GetBounds(aDisplayListBuilder
, &snap
);
9910 LayoutDeviceRect bounds
=
9911 LayoutDeviceRect::FromAppUnits(displayBounds
, appUnitsPerDevPixel
);
9913 Maybe
<wr::WrClipId
> clip
= CreateWRClipPathAndMasks(
9914 this, bounds
, aResources
, aBuilder
, aSc
, aManager
, aDisplayListBuilder
);
9916 Maybe
<StackingContextHelper
> layer
;
9917 const StackingContextHelper
* sc
= &aSc
;
9919 // Create a new stacking context to attach the mask to, ensuring the mask is
9920 // applied to the aggregate, and not the individual elements.
9922 // The stacking context shouldn't have any offset.
9923 bounds
.MoveTo(0, 0);
9925 Maybe
<float> opacity
= mApplyOpacityWithSimpleClipPath
9926 ? Some(mFrame
->StyleEffects()->mOpacity
)
9929 wr::StackingContextParams params
;
9930 params
.clip
= wr::WrStackingContextClip::ClipId(*clip
);
9931 params
.opacity
= opacity
.ptrOr(nullptr);
9932 layer
.emplace(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
, params
,
9937 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder
, aResources
, *sc
,
9938 aManager
, aDisplayListBuilder
);
9943 void nsDisplayMasksAndClipPaths::SelectOpacityOptimization(
9944 const bool aUsingLayers
) {
9946 !SVGIntegrationUtils::UsingSimpleClipPathForFrame(mFrame
)) {
9947 // Handle opacity in mask and clip-path drawing code.
9949 MOZ_ASSERT(!mApplyOpacityWithSimpleClipPath
);
9951 // Allow WebRender simple clip paths to also handle opacity.
9952 mApplyOpacityWithSimpleClipPath
= true;
9956 Maybe
<nsRect
> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
9957 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
) const {
9958 if (const DisplayItemClip
* clip
=
9959 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR
)) {
9960 return Some(clip
->GetClipRect());
9962 // This item does not have a clip with respect to |aASR|. However, we
9963 // might still have finite bounds with respect to |aASR|. Check our
9965 nsDisplayList
* childList
= GetSameCoordinateSystemChildren();
9967 return Some(childList
->GetClippedBoundsWithRespectToASR(aBuilder
, aASR
));
9970 MOZ_ASSERT(false, "item should have finite clip with respect to aASR");
9975 #ifdef MOZ_DUMP_PAINTING
9976 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString
& aTo
) {
9977 nsIFrame
* firstFrame
=
9978 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame
);
9980 aTo
+= " effects=(";
9981 if (mHandleOpacity
) {
9983 aTo
+= nsPrintfCString("opacity(%f)", mFrame
->StyleEffects()->mOpacity
);
9985 SVGClipPathFrame
* clipPathFrame
;
9986 // XXX Check return value?
9987 SVGObserverUtils::GetAndObserveClipPath(firstFrame
, &clipPathFrame
);
9988 if (clipPathFrame
) {
9992 aTo
+= nsPrintfCString(
9993 "clip(%s)", clipPathFrame
->IsTrivial() ? "trivial" : "non-trivial");
9995 } else if (mFrame
->StyleSVGReset()->HasClipPath()) {
9999 aTo
+= "clip(basic-shape)";
10003 nsTArray
<SVGMaskFrame
*> masks
;
10004 // XXX check return value?
10005 SVGObserverUtils::GetAndObserveMasks(firstFrame
, &masks
);
10006 if (!masks
.IsEmpty() && masks
[0]) {
10016 already_AddRefed
<Layer
> nsDisplayBackdropRootContainer::BuildLayer(
10017 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
10018 const ContainerLayerParameters
& aContainerParameters
) {
10019 RefPtr
<Layer
> container
= aManager
->GetLayerBuilder()->BuildContainerLayerFor(
10020 aBuilder
, aManager
, mFrame
, this, &mList
, aContainerParameters
, nullptr);
10025 return container
.forget();
10028 void nsDisplayBackdropRootContainer::Paint(nsDisplayListBuilder
* aBuilder
,
10029 gfxContext
* aCtx
) {
10030 aCtx
->GetDrawTarget()->PushLayer(false, 1.0, nullptr, mozilla::gfx::Matrix());
10031 GetChildren()->Paint(aBuilder
, aCtx
,
10032 mFrame
->PresContext()->AppUnitsPerDevPixel());
10033 aCtx
->GetDrawTarget()->PopLayer();
10036 LayerState
nsDisplayBackdropRootContainer::GetLayerState(
10037 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
10038 const ContainerLayerParameters
& aParameters
) {
10039 return RequiredLayerStateForChildren(aBuilder
, aManager
, aParameters
, mList
,
10040 GetAnimatedGeometryRoot(),
10041 GetActiveScrolledRoot());
10044 bool nsDisplayBackdropRootContainer::CreateWebRenderCommands(
10045 mozilla::wr::DisplayListBuilder
& aBuilder
,
10046 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
10047 const StackingContextHelper
& aSc
,
10048 mozilla::layers::RenderRootStateManager
* aManager
,
10049 nsDisplayListBuilder
* aDisplayListBuilder
) {
10050 wr::StackingContextParams params
;
10051 params
.flags
|= wr::StackingContextFlags::IS_BACKDROP_ROOT
;
10053 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
10054 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
10057 nsDisplayWrapList::CreateWebRenderCommands(aBuilder
, aResources
, sc
, aManager
,
10058 aDisplayListBuilder
);
10063 bool nsDisplayBackdropFilters::CanCreateWebRenderCommands(
10064 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
) {
10065 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(aFrame
);
10068 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
10069 mozilla::wr::DisplayListBuilder
& aBuilder
,
10070 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
10071 const StackingContextHelper
& aSc
,
10072 mozilla::layers::RenderRootStateManager
* aManager
,
10073 nsDisplayListBuilder
* aDisplayListBuilder
) {
10074 WrFiltersHolder wrFilters
;
10075 Maybe
<nsRect
> filterClip
;
10076 auto filterChain
= mFrame
->StyleEffects()->mBackdropFilters
.AsSpan();
10077 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain
, mFrame
,
10079 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame
, filterChain
,
10080 wrFilters
, filterClip
)) {
10084 nsCSSRendering::ImageLayerClipState clip
;
10085 nsCSSRendering::GetImageLayerClip(
10086 mFrame
->StyleBackground()->BottomLayer(), mFrame
, *mFrame
->StyleBorder(),
10087 mBackdropRect
, mBackdropRect
, false,
10088 mFrame
->PresContext()->AppUnitsPerDevPixel(), &clip
);
10090 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
10091 mBackdropRect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
10093 wr::ComplexClipRegion region
=
10094 wr::ToComplexClipRegion(clip
.mBGClipArea
, clip
.mRadii
,
10095 mFrame
->PresContext()->AppUnitsPerDevPixel());
10097 aBuilder
.PushBackdropFilter(wr::ToLayoutRect(bounds
), region
,
10098 wrFilters
.filters
, wrFilters
.filter_datas
,
10099 !BackfaceIsHidden());
10101 wr::StackingContextParams params
;
10103 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
10104 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
10107 nsDisplayWrapList::CreateWebRenderCommands(aBuilder
, aResources
, sc
, aManager
,
10108 aDisplayListBuilder
);
10112 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder
* aBuilder
,
10113 gfxContext
* aCtx
) {
10114 // TODO: Implement backdrop filters
10115 GetChildren()->Paint(aBuilder
, aCtx
,
10116 mFrame
->PresContext()->AppUnitsPerDevPixel());
10120 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder
* aBuilder
,
10121 nsIFrame
* aFrame
, nsDisplayList
* aList
)
10122 : nsDisplayEffectsBase(aBuilder
, aFrame
, aList
),
10123 mEffectsBounds(aFrame
->InkOverflowRectRelativeToSelf()) {
10124 MOZ_COUNT_CTOR(nsDisplayFilters
);
10127 already_AddRefed
<Layer
> nsDisplayFilters::BuildLayer(
10128 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
10129 const ContainerLayerParameters
& aContainerParameters
) {
10130 if (!ValidateSVGFrame()) {
10134 if (mFrame
->StyleEffects()->mOpacity
== 0.0f
&& mHandleOpacity
) {
10138 nsIFrame
* firstFrame
=
10139 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame
);
10141 // We may exist for a mix of CSS filter functions and/or references to SVG
10142 // filters. If we have invalid references to SVG filters then we paint
10143 // nothing, so no need for a layer.
10144 if (SVGObserverUtils::GetAndObserveFilters(firstFrame
, nullptr) ==
10145 SVGObserverUtils::eHasRefsSomeInvalid
) {
10149 ContainerLayerParameters newContainerParameters
= aContainerParameters
;
10150 newContainerParameters
.mDisableSubpixelAntialiasingInDescendants
= true;
10152 RefPtr
<ContainerLayer
> container
=
10153 aManager
->GetLayerBuilder()->BuildContainerLayerFor(
10154 aBuilder
, aManager
, mFrame
, this, &mList
, newContainerParameters
,
10156 return container
.forget();
10159 LayerState
nsDisplayFilters::GetLayerState(
10160 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
10161 const ContainerLayerParameters
& aParameters
) {
10162 return LayerState::LAYER_SVG_EFFECTS
;
10165 void nsDisplayFilters::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
10166 PaintWithContentsPaintCallback(aBuilder
, aCtx
, [&](gfxContext
* aContext
) {
10167 GetChildren()->Paint(aBuilder
, aContext
,
10168 mFrame
->PresContext()->AppUnitsPerDevPixel());
10172 bool nsDisplayFilters::ComputeVisibility(nsDisplayListBuilder
* aBuilder
,
10173 nsRegion
* aVisibleRegion
) {
10174 nsPoint offset
= ToReferenceFrame();
10175 nsRect dirtyRect
= SVGIntegrationUtils::GetRequiredSourceForInvalidArea(
10176 mFrame
, GetPaintRect() - offset
) +
10179 // Our children may be made translucent or arbitrarily deformed so we should
10180 // not allow them to subtract area from aVisibleRegion.
10181 nsRegion
childrenVisible(dirtyRect
);
10182 nsRect r
= dirtyRect
.Intersect(
10183 mList
.GetClippedBoundsWithRespectToASR(aBuilder
, mActiveScrolledRoot
));
10184 mList
.ComputeVisibilityForSublist(aBuilder
, &childrenVisible
, r
);
10188 void nsDisplayFilters::ComputeInvalidationRegion(
10189 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
10190 nsRegion
* aInvalidRegion
) const {
10191 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder
, aGeometry
,
10194 auto* geometry
= static_cast<const nsDisplayFiltersGeometry
*>(aGeometry
);
10196 if (aBuilder
->ShouldSyncDecodeImages() &&
10197 geometry
->ShouldInvalidateToSyncDecodeImages()) {
10199 nsRect bounds
= GetBounds(aBuilder
, &snap
);
10200 aInvalidRegion
->Or(*aInvalidRegion
, bounds
);
10204 void nsDisplayFilters::PaintAsLayer(nsDisplayListBuilder
* aBuilder
,
10205 gfxContext
* aCtx
, LayerManager
* aManager
) {
10206 PaintWithContentsPaintCallback(aBuilder
, aCtx
, [&](gfxContext
* aContext
) {
10207 BasicLayerManager
* basic
= aManager
->AsBasicLayerManager();
10208 RefPtr
<gfxContext
> oldCtx
= basic
->GetTarget();
10209 basic
->SetTarget(aContext
);
10211 aManager
->EndTransaction(FrameLayerBuilder::DrawPaintedLayer
, aBuilder
);
10212 basic
->SetTarget(oldCtx
);
10216 void nsDisplayFilters::PaintWithContentsPaintCallback(
10217 nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
10218 const std::function
<void(gfxContext
* aContext
)>& aPaintChildren
) {
10219 imgDrawingParams
imgParams(aBuilder
->GetImageDecodeFlags());
10220 nsRect borderArea
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
10221 SVGIntegrationUtils::PaintFramesParams
params(*aCtx
, mFrame
, GetPaintRect(),
10222 borderArea
, aBuilder
, nullptr,
10223 mHandleOpacity
, imgParams
);
10225 gfxPoint userSpaceToFrameSpaceOffset
=
10226 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame
, params
);
10228 SVGIntegrationUtils::PaintFilter(
10230 [&](gfxContext
& aContext
, nsIFrame
* aTarget
, const gfxMatrix
& aTransform
,
10231 const nsIntRect
* aDirtyRect
, image::imgDrawingParams
& aImgParams
) {
10232 gfxContextMatrixAutoSaveRestore
autoSR(&aContext
);
10233 aContext
.SetMatrixDouble(aContext
.CurrentMatrixDouble().PreTranslate(
10234 -userSpaceToFrameSpaceOffset
));
10235 aPaintChildren(&aContext
);
10237 nsDisplayFiltersGeometry::UpdateDrawResult(this, imgParams
.result
);
10240 bool nsDisplayFilters::CanCreateWebRenderCommands() {
10241 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame
);
10244 bool nsDisplayFilters::CreateWebRenderCommands(
10245 mozilla::wr::DisplayListBuilder
& aBuilder
,
10246 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
10247 const StackingContextHelper
& aSc
,
10248 mozilla::layers::RenderRootStateManager
* aManager
,
10249 nsDisplayListBuilder
* aDisplayListBuilder
) {
10250 float auPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
10252 WrFiltersHolder wrFilters
;
10253 Maybe
<nsRect
> filterClip
;
10254 auto filterChain
= mFrame
->StyleEffects()->mFilters
.AsSpan();
10255 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain
, mFrame
,
10257 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame
, filterChain
,
10258 wrFilters
, filterClip
)) {
10262 wr::WrStackingContextClip clip
;
10264 auto devPxRect
= LayoutDeviceRect::FromAppUnits(
10265 filterClip
.value() + ToReferenceFrame(), auPerDevPixel
);
10266 wr::WrClipId clipId
= aBuilder
.DefineRectClip(wr::ToLayoutRect(devPxRect
));
10267 clip
= wr::WrStackingContextClip::ClipId(clipId
);
10269 clip
= wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
10272 float opacity
= mFrame
->StyleEffects()->mOpacity
;
10273 wr::StackingContextParams params
;
10274 params
.mFilters
= std::move(wrFilters
.filters
);
10275 params
.mFilterDatas
= std::move(wrFilters
.filter_datas
);
10276 params
.opacity
= opacity
!= 1.0f
&& mHandleOpacity
? &opacity
: nullptr;
10277 params
.clip
= clip
;
10278 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
10281 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder
, aResources
, sc
,
10282 aManager
, aDisplayListBuilder
);
10287 #ifdef MOZ_DUMP_PAINTING
10288 void nsDisplayFilters::PrintEffects(nsACString
& aTo
) {
10289 nsIFrame
* firstFrame
=
10290 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame
);
10292 aTo
+= " effects=(";
10293 if (mHandleOpacity
) {
10295 aTo
+= nsPrintfCString("opacity(%f)", mFrame
->StyleEffects()->mOpacity
);
10297 // We may exist for a mix of CSS filter functions and/or references to SVG
10298 // filters. If we have invalid references to SVG filters then we paint
10299 // nothing, but otherwise we will apply one or more filters.
10300 if (SVGObserverUtils::GetAndObserveFilters(firstFrame
, nullptr) !=
10301 SVGObserverUtils::eHasRefsSomeInvalid
) {
10311 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder
* aBuilder
,
10312 nsIFrame
* aFrame
, nsDisplayList
* aList
)
10313 : nsDisplayWrapList(aBuilder
, aFrame
, aList
) {
10314 MOZ_COUNT_CTOR(nsDisplaySVGWrapper
);
10317 LayerState
nsDisplaySVGWrapper::GetLayerState(
10318 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
10319 const ContainerLayerParameters
& aParameters
) {
10320 RefPtr
<LayerManager
> layerManager
= aBuilder
->GetWidgetLayerManager();
10321 if (layerManager
&&
10322 layerManager
->GetBackendType() == layers::LayersBackend::LAYERS_WR
) {
10323 return LayerState::LAYER_ACTIVE_FORCE
;
10325 return LayerState::LAYER_NONE
;
10328 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) {
10329 RefPtr
<LayerManager
> layerManager
= aBuilder
->GetWidgetLayerManager();
10330 if (layerManager
&&
10331 layerManager
->GetBackendType() == layers::LayersBackend::LAYERS_WR
) {
10337 already_AddRefed
<Layer
> nsDisplaySVGWrapper::BuildLayer(
10338 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
10339 const ContainerLayerParameters
& aContainerParameters
) {
10340 ContainerLayerParameters newContainerParameters
= aContainerParameters
;
10341 newContainerParameters
.mDisableSubpixelAntialiasingInDescendants
= true;
10343 RefPtr
<ContainerLayer
> container
=
10344 aManager
->GetLayerBuilder()->BuildContainerLayerFor(
10345 aBuilder
, aManager
, mFrame
, this, &mList
, newContainerParameters
,
10348 return container
.forget();
10351 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
10352 mozilla::wr::DisplayListBuilder
& aBuilder
,
10353 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
10354 const StackingContextHelper
& aSc
,
10355 mozilla::layers::RenderRootStateManager
* aManager
,
10356 nsDisplayListBuilder
* aDisplayListBuilder
) {
10357 return nsDisplayWrapList::CreateWebRenderCommands(
10358 aBuilder
, aResources
, aSc
, aManager
, aDisplayListBuilder
);
10361 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder
* aBuilder
,
10363 nsDisplayList
* aList
)
10364 : nsDisplayWrapList(aBuilder
, aFrame
, aList
) {
10365 MOZ_COUNT_CTOR(nsDisplayForeignObject
);
10368 #ifdef NS_BUILD_REFCNT_LOGGING
10369 nsDisplayForeignObject::~nsDisplayForeignObject() {
10370 MOZ_COUNT_DTOR(nsDisplayForeignObject
);
10374 LayerState
nsDisplayForeignObject::GetLayerState(
10375 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
10376 const ContainerLayerParameters
& aParameters
) {
10377 RefPtr
<LayerManager
> layerManager
= aBuilder
->GetWidgetLayerManager();
10378 if (layerManager
&&
10379 layerManager
->GetBackendType() == layers::LayersBackend::LAYERS_WR
) {
10380 return LayerState::LAYER_ACTIVE_FORCE
;
10382 return LayerState::LAYER_NONE
;
10385 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) {
10386 RefPtr
<LayerManager
> layerManager
= aBuilder
->GetWidgetLayerManager();
10387 if (layerManager
&&
10388 layerManager
->GetBackendType() == layers::LayersBackend::LAYERS_WR
) {
10394 already_AddRefed
<Layer
> nsDisplayForeignObject::BuildLayer(
10395 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
10396 const ContainerLayerParameters
& aContainerParameters
) {
10397 ContainerLayerParameters newContainerParameters
= aContainerParameters
;
10398 newContainerParameters
.mDisableSubpixelAntialiasingInDescendants
= true;
10400 RefPtr
<ContainerLayer
> container
=
10401 aManager
->GetLayerBuilder()->BuildContainerLayerFor(
10402 aBuilder
, aManager
, mFrame
, this, &mList
, newContainerParameters
,
10405 return container
.forget();
10408 bool nsDisplayForeignObject::CreateWebRenderCommands(
10409 mozilla::wr::DisplayListBuilder
& aBuilder
,
10410 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
10411 const StackingContextHelper
& aSc
,
10412 mozilla::layers::RenderRootStateManager
* aManager
,
10413 nsDisplayListBuilder
* aDisplayListBuilder
) {
10414 AutoRestore
<bool> restoreDoGrouping(aManager
->CommandBuilder().mDoGrouping
);
10415 aManager
->CommandBuilder().mDoGrouping
= false;
10416 return nsDisplayWrapList::CreateWebRenderCommands(
10417 aBuilder
, aResources
, aSc
, aManager
, aDisplayListBuilder
);
10420 void nsDisplayLink::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
10421 auto appPerDev
= mFrame
->PresContext()->AppUnitsPerDevPixel();
10422 aCtx
->GetDrawTarget()->Link(mLinkSpec
.get(),
10423 NSRectToRect(GetPaintRect(), appPerDev
));
10426 void nsDisplayListCollection::SerializeWithCorrectZOrder(
10427 nsDisplayList
* aOutResultList
, nsIContent
* aContent
) {
10428 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
10429 // in content document order and SortByZOrder is a stable sort which
10430 // guarantees that boxes produced by the same element are placed together
10431 // in the sort. Consider a position:relative inline element that breaks
10432 // across lines and has absolutely positioned children; all the abs-pos
10433 // children should be z-ordered after all the boxes for the position:relative
10435 PositionedDescendants()->SortByZOrder();
10437 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
10438 // 1,2: backgrounds and borders
10439 aOutResultList
->AppendToTop(BorderBackground());
10440 // 3: negative z-index children.
10442 nsDisplayItem
* item
= PositionedDescendants()->GetBottom();
10443 if (item
&& item
->ZIndex() < 0) {
10444 PositionedDescendants()->RemoveBottom();
10445 aOutResultList
->AppendToTop(item
);
10450 // 4: block backgrounds
10451 aOutResultList
->AppendToTop(BlockBorderBackgrounds());
10453 aOutResultList
->AppendToTop(Floats());
10454 // 7: general content
10455 aOutResultList
->AppendToTop(Content());
10456 // 7.5: outlines, in content tree order. We need to sort by content order
10457 // because an element with outline that breaks and has children with outline
10458 // might have placed child outline items between its own outline items.
10459 // The element's outline items need to all come before any child outline
10462 Outlines()->SortByContentOrder(aContent
);
10464 aOutResultList
->AppendToTop(Outlines());
10465 // 8, 9: non-negative z-index children
10466 aOutResultList
->AppendToTop(PositionedDescendants());
10469 namespace mozilla
{
10471 uint32_t PaintTelemetry::sPaintLevel
= 0;
10473 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
10474 // Don't record nested paints.
10475 if (sPaintLevel
++ > 0) {
10479 mStart
= TimeStamp::Now();
10482 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
10483 MOZ_ASSERT(sPaintLevel
!= 0);
10484 if (--sPaintLevel
> 0) {
10488 // If we're in multi-process mode, don't include paint times for the parent
10490 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
10494 double totalMs
= (TimeStamp::Now() - mStart
).ToMilliseconds();
10496 // Record the total time.
10497 Telemetry::Accumulate(Telemetry::CONTENT_PAINT_TIME
,
10498 static_cast<uint32_t>(totalMs
));
10501 } // namespace mozilla
10503 static nsIFrame
* GetSelfOrPlaceholderFor(nsIFrame
* aFrame
) {
10504 if (aFrame
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
10508 if (aFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
) &&
10509 !aFrame
->GetPrevInFlow()) {
10510 return aFrame
->GetPlaceholderFrame();
10516 static nsIFrame
* GetAncestorFor(nsIFrame
* aFrame
) {
10517 nsIFrame
* f
= GetSelfOrPlaceholderFor(aFrame
);
10519 return nsLayoutUtils::GetCrossDocParentFrame(f
);
10522 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
10523 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aForChild
,
10524 const nsRect
& aVisibleRect
, const nsRect
& aDirtyRect
,
10525 const bool aIsTransformed
)
10526 : mBuilder(aBuilder
),
10527 mPrevFrame(aBuilder
->mCurrentFrame
),
10528 mPrevReferenceFrame(aBuilder
->mCurrentReferenceFrame
),
10529 mPrevOffset(aBuilder
->mCurrentOffsetToReferenceFrame
),
10530 mPrevAdditionalOffset(aBuilder
->mAdditionalOffset
),
10531 mPrevVisibleRect(aBuilder
->mVisibleRect
),
10532 mPrevDirtyRect(aBuilder
->mDirtyRect
),
10533 mPrevAGR(aBuilder
->mCurrentAGR
),
10534 mPrevCompositorHitTestInfo(aBuilder
->mCompositorHitTestInfo
),
10535 mPrevAncestorHasApzAwareEventHandler(
10536 aBuilder
->mAncestorHasApzAwareEventHandler
),
10537 mPrevBuildingInvisibleItems(aBuilder
->mBuildingInvisibleItems
),
10538 mPrevInInvalidSubtree(aBuilder
->mInInvalidSubtree
) {
10539 if (aIsTransformed
) {
10540 aBuilder
->mCurrentOffsetToReferenceFrame
=
10541 aBuilder
->AdditionalOffset().refOr(nsPoint());
10542 aBuilder
->mCurrentReferenceFrame
= aForChild
;
10543 } else if (aBuilder
->mCurrentFrame
== aForChild
->GetParent()) {
10544 aBuilder
->mCurrentOffsetToReferenceFrame
+= aForChild
->GetPosition();
10546 aBuilder
->mCurrentReferenceFrame
= aBuilder
->FindReferenceFrameFor(
10547 aForChild
, &aBuilder
->mCurrentOffsetToReferenceFrame
);
10551 mCurrentAGRState
= aBuilder
->IsAnimatedGeometryRoot(aForChild
, isAsync
);
10553 if (aBuilder
->mCurrentFrame
== aForChild
->GetParent()) {
10554 if (mCurrentAGRState
== AGR_YES
) {
10555 aBuilder
->mCurrentAGR
=
10556 aBuilder
->WrapAGRForFrame(aForChild
, isAsync
, aBuilder
->mCurrentAGR
);
10558 } else if (aBuilder
->mCurrentFrame
!= aForChild
) {
10559 aBuilder
->mCurrentAGR
= aBuilder
->FindAnimatedGeometryRootFor(aForChild
);
10562 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(
10563 aBuilder
->RootReferenceFrame(), *aBuilder
->mCurrentAGR
));
10565 // If aForChild is being visited from a frame other than it's ancestor frame,
10566 // mInInvalidSubtree will need to be recalculated the slow way.
10567 if (aForChild
== mPrevFrame
|| GetAncestorFor(aForChild
) == mPrevFrame
) {
10568 aBuilder
->mInInvalidSubtree
=
10569 aBuilder
->mInInvalidSubtree
|| aForChild
->IsFrameModified();
10571 aBuilder
->mInInvalidSubtree
= AnyContentAncestorModified(aForChild
);
10574 aBuilder
->mCurrentFrame
= aForChild
;
10575 aBuilder
->mVisibleRect
= aVisibleRect
;
10576 aBuilder
->mDirtyRect
=
10577 aBuilder
->mInInvalidSubtree
? aVisibleRect
: aDirtyRect
;