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/Likely.h"
23 #include "mozilla/dom/BrowserChild.h"
24 #include "mozilla/dom/HTMLCanvasElement.h"
25 #include "mozilla/dom/RemoteBrowser.h"
26 #include "mozilla/dom/Selection.h"
27 #include "mozilla/dom/ServiceWorkerRegistrar.h"
28 #include "mozilla/dom/ServiceWorkerRegistration.h"
29 #include "mozilla/dom/SVGElement.h"
30 #include "mozilla/dom/TouchEvent.h"
31 #include "mozilla/dom/PerformanceMainThread.h"
32 #include "mozilla/gfx/2D.h"
33 #include "mozilla/PresShell.h"
34 #include "mozilla/ShapeUtils.h"
35 #include "mozilla/StaticPrefs_apz.h"
36 #include "mozilla/StaticPrefs_gfx.h"
37 #include "mozilla/StaticPrefs_layers.h"
38 #include "mozilla/StaticPrefs_layout.h"
39 #include "mozilla/StaticPrefs_print.h"
40 #include "mozilla/SVGIntegrationUtils.h"
41 #include "mozilla/SVGUtils.h"
42 #include "mozilla/ViewportUtils.h"
43 #include "nsCSSRendering.h"
44 #include "nsCSSRenderingGradients.h"
45 #include "nsCaseTreatment.h"
46 #include "nsRefreshDriver.h"
48 #include "nsStyleStructInlines.h"
49 #include "nsStyleTransformMatrix.h"
50 #include "nsTransitionManager.h"
51 #include "gfxMatrix.h"
52 #include "nsLayoutUtils.h"
53 #include "nsIScrollableFrame.h"
54 #include "nsIFrameInlines.h"
55 #include "nsStyleConsts.h"
56 #include "BorderConsts.h"
57 #include "mozilla/MathAlgorithms.h"
59 #include "imgIContainer.h"
60 #include "nsImageFrame.h"
61 #include "nsSubDocumentFrame.h"
62 #include "nsViewManager.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/HashTable.h"
73 #include "mozilla/LookAndFeel.h"
74 #include "mozilla/OperatorNewExtensions.h"
75 #include "mozilla/Preferences.h"
76 #include "mozilla/ProfilerLabels.h"
77 #include "mozilla/ProfilerMarkers.h"
78 #include "mozilla/StyleAnimationValue.h"
79 #include "mozilla/ServoBindings.h"
80 #include "mozilla/SVGClipPathFrame.h"
81 #include "mozilla/SVGMaskFrame.h"
82 #include "mozilla/SVGObserverUtils.h"
83 #include "mozilla/Telemetry.h"
84 #include "mozilla/UniquePtr.h"
85 #include "mozilla/Unused.h"
86 #include "mozilla/ViewportFrame.h"
87 #include "mozilla/gfx/gfxVars.h"
88 #include "ActiveLayerTracker.h"
90 #include "nsPrintfCString.h"
91 #include "UnitTransforms.h"
92 #include "LayerAnimationInfo.h"
93 #include "mozilla/EventStateManager.h"
95 #include "nsDOMTokenList.h"
96 #include "nsCSSProps.h"
97 #include "nsTableCellFrame.h"
98 #include "nsTableColFrame.h"
99 #include "nsTextFrame.h"
100 #include "nsTextPaintStyle.h"
101 #include "nsSliderFrame.h"
102 #include "nsFocusManager.h"
103 #include "TextDrawTarget.h"
104 #include "mozilla/layers/AnimationHelper.h"
105 #include "mozilla/layers/CompositorThread.h"
106 #include "mozilla/layers/InputAPZContext.h"
107 #include "mozilla/layers/RenderRootStateManager.h"
108 #include "mozilla/layers/StackingContextHelper.h"
109 #include "mozilla/layers/TreeTraversal.h"
110 #include "mozilla/layers/WebRenderBridgeChild.h"
111 #include "mozilla/layers/WebRenderLayerManager.h"
112 #include "mozilla/layers/WebRenderMessages.h"
113 #include "mozilla/layers/WebRenderScrollData.h"
119 using namespace layout
;
120 using namespace layers
;
121 using namespace image
;
123 LazyLogModule
sContentDisplayListLog("dl.content");
124 LazyLogModule
sParentDisplayListLog("dl.parent");
126 LazyLogModule
& GetLoggerByProcess() {
127 return XRE_IsContentProcess() ? sContentDisplayListLog
128 : sParentDisplayListLog
;
131 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
132 void AssertUniqueItem(nsDisplayItem
* aItem
) {
133 for (nsDisplayItem
* i
: aItem
->Frame()->DisplayItems()) {
134 if (i
!= aItem
&& !i
->HasDeletedFrame() && i
->Frame() == aItem
->Frame() &&
135 i
->GetPerFrameKey() == aItem
->GetPerFrameKey()) {
136 if (i
->IsPreProcessedItem() || i
->IsPreProcessed()) {
139 MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
145 bool ShouldBuildItemForEvents(const DisplayItemType aType
) {
146 return aType
== DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
||
147 (GetDisplayItemFlagsForType(aType
) & TYPE_IS_CONTAINER
);
150 static bool ItemTypeSupportsHitTesting(const DisplayItemType aType
) {
152 case DisplayItemType::TYPE_BACKGROUND
:
153 case DisplayItemType::TYPE_BACKGROUND_COLOR
:
154 case DisplayItemType::TYPE_THEMED_BACKGROUND
:
161 void InitializeHitTestInfo(nsDisplayListBuilder
* aBuilder
,
162 nsPaintedDisplayItem
* aItem
,
163 const DisplayItemType aType
) {
164 if (ItemTypeSupportsHitTesting(aType
)) {
165 aItem
->InitializeHitTestInfo(aBuilder
);
170 already_AddRefed
<ActiveScrolledRoot
> ActiveScrolledRoot::CreateASRForFrame(
171 const ActiveScrolledRoot
* aParent
, nsIScrollableFrame
* aScrollableFrame
,
173 nsIFrame
* f
= do_QueryFrame(aScrollableFrame
);
175 RefPtr
<ActiveScrolledRoot
> asr
;
177 asr
= f
->GetProperty(ActiveScrolledRootCache());
181 asr
= new ActiveScrolledRoot();
184 RefPtr
<ActiveScrolledRoot
> ref
= asr
;
185 f
->SetProperty(ActiveScrolledRootCache(), ref
.forget().take());
188 asr
->mParent
= aParent
;
189 asr
->mScrollableFrame
= aScrollableFrame
;
190 asr
->mViewId
= Nothing();
191 asr
->mDepth
= aParent
? aParent
->mDepth
+ 1 : 1;
192 asr
->mRetained
= aIsRetained
;
198 bool ActiveScrolledRoot::IsAncestor(const ActiveScrolledRoot
* aAncestor
,
199 const ActiveScrolledRoot
* aDescendant
) {
201 // nullptr is the root
204 if (Depth(aAncestor
) > Depth(aDescendant
)) {
207 const ActiveScrolledRoot
* asr
= aDescendant
;
209 if (asr
== aAncestor
) {
218 bool ActiveScrolledRoot::IsProperAncestor(
219 const ActiveScrolledRoot
* aAncestor
,
220 const ActiveScrolledRoot
* aDescendant
) {
221 return aAncestor
!= aDescendant
&& IsAncestor(aAncestor
, aDescendant
);
225 nsCString
ActiveScrolledRoot::ToString(
226 const ActiveScrolledRoot
* aActiveScrolledRoot
) {
228 for (const auto* asr
= aActiveScrolledRoot
; asr
; asr
= asr
->mParent
) {
229 str
.AppendPrintf("<0x%p>", asr
->mScrollableFrame
);
231 str
.AppendLiteral(", ");
234 return std::move(str
);
237 ScrollableLayerGuid::ViewID
ActiveScrolledRoot::ComputeViewId() const {
238 nsIContent
* content
= mScrollableFrame
->GetScrolledFrame()->GetContent();
239 return nsLayoutUtils::FindOrCreateIDFor(content
);
242 ActiveScrolledRoot::~ActiveScrolledRoot() {
243 if (mScrollableFrame
&& mRetained
) {
244 nsIFrame
* f
= do_QueryFrame(mScrollableFrame
);
245 f
->RemoveProperty(ActiveScrolledRootCache());
249 static uint64_t AddAnimationsForWebRender(
250 nsDisplayItem
* aItem
, RenderRootStateManager
* aManager
,
251 nsDisplayListBuilder
* aDisplayListBuilder
,
252 const Maybe
<LayoutDevicePoint
>& aPosition
= Nothing()) {
253 auto* effects
= EffectSet::GetForFrame(aItem
->Frame(), aItem
->GetType());
254 if (!effects
|| effects
->IsEmpty()) {
255 // If there is no animation on the nsIFrame, that means
256 // 1) we've never created any animations on this frame or
257 // 2) the frame was reconstruced or
258 // 3) all animations on the frame have finished
259 // in such cases we don't need do anything here.
261 // Even if there is a WebRenderAnimationData for the display item type on
262 // this frame, it's going to be discarded since it's not marked as being
267 RefPtr
<WebRenderAnimationData
> animationData
=
268 aManager
->CommandBuilder()
269 .CreateOrRecycleWebRenderUserData
<WebRenderAnimationData
>(aItem
);
270 AnimationInfo
& animationInfo
= animationData
->GetAnimationInfo();
271 nsIFrame
* frame
= aItem
->Frame();
272 animationInfo
.AddAnimationsForDisplayItem(
273 frame
, aDisplayListBuilder
, aItem
, aItem
->GetType(),
274 aManager
->LayerManager(), aPosition
);
275 animationInfo
.StartPendingAnimations(
276 frame
->PresContext()->RefreshDriver()->MostRecentRefresh(
277 /* aEnsureTimerStarted = */ false));
279 // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
280 // are no active animations.
281 uint64_t animationsId
= animationInfo
.GetCompositorAnimationsId();
282 if (!animationInfo
.GetAnimations().IsEmpty()) {
283 OpAddCompositorAnimations
anim(
284 CompositorAnimations(animationInfo
.GetAnimations(), animationsId
));
285 aManager
->WrBridge()->AddWebRenderParentCommand(anim
);
286 aManager
->AddActiveCompositorAnimationId(animationsId
);
287 } else if (animationsId
) {
288 aManager
->AddCompositorAnimationsIdForDiscard(animationsId
);
295 static bool GenerateAndPushTextMask(nsIFrame
* aFrame
, gfxContext
* aContext
,
296 const nsRect
& aFillRect
,
297 nsDisplayListBuilder
* aBuilder
) {
298 if (aBuilder
->IsForGenerateGlyphMask()) {
302 SVGObserverUtils::GetAndObserveBackgroundClip(aFrame
);
304 // The main function of enabling background-clip:text property value.
305 // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
307 // 1. Generate a mask by all descendant text frames
308 // 2. Push the generated mask into aContext.
310 gfxContext
* sourceCtx
= aContext
;
311 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
312 aFillRect
, aFrame
->PresContext()->AppUnitsPerDevPixel());
314 // Create a mask surface.
315 RefPtr
<DrawTarget
> sourceTarget
= sourceCtx
->GetDrawTarget();
316 RefPtr
<DrawTarget
> maskDT
= sourceTarget
->CreateClippedDrawTarget(
317 bounds
.ToUnknownRect(), SurfaceFormat::A8
);
318 if (!maskDT
|| !maskDT
->IsValid()) {
321 gfxContext
maskCtx(maskDT
, /* aPreserveTransform */ true);
322 maskCtx
.Multiply(Matrix::Translation(bounds
.TopLeft().ToUnknownPoint()));
324 // Shade text shape into mask A8 surface.
325 nsLayoutUtils::PaintFrame(
326 &maskCtx
, aFrame
, nsRect(nsPoint(0, 0), aFrame
->GetSize()),
327 NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph
);
329 // Push the generated mask into aContext, so that the caller can pop and
332 Matrix currentMatrix
= sourceCtx
->CurrentMatrix();
333 Matrix invCurrentMatrix
= currentMatrix
;
334 invCurrentMatrix
.Invert();
336 RefPtr
<SourceSurface
> maskSurface
= maskDT
->Snapshot();
337 sourceCtx
->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA
, 1.0,
338 maskSurface
, invCurrentMatrix
);
343 nsDisplayWrapper
* nsDisplayWrapList::CreateShallowCopy(
344 nsDisplayListBuilder
* aBuilder
) {
345 const nsDisplayWrapList
* wrappedItem
= AsDisplayWrapList();
346 MOZ_ASSERT(wrappedItem
);
348 // Create a new nsDisplayWrapList using a copy-constructor. This is done
349 // to preserve the information about bounds.
350 nsDisplayWrapper
* wrapper
=
351 new (aBuilder
) nsDisplayWrapper(aBuilder
, *wrappedItem
);
352 wrapper
->SetType(nsDisplayWrapper::ItemType());
355 // Set the display list pointer of the new wrapper item to the display list
356 // of the wrapped item.
357 wrapper
->mListPtr
= wrappedItem
->mListPtr
;
361 nsDisplayWrapList
* nsDisplayListBuilder::MergeItems(
362 nsTArray
<nsDisplayItem
*>& aItems
) {
363 // For merging, we create a temporary item by cloning the last item of the
364 // mergeable items list. This ensures that the temporary item will have the
365 // correct frame and bounds.
366 nsDisplayWrapList
* last
= aItems
.PopLastElement()->AsDisplayWrapList();
368 nsDisplayWrapList
* merged
= last
->Clone(this);
370 AddTemporaryItem(merged
);
372 // Create nsDisplayWrappers that point to the internal display lists of the
373 // items we are merging. These nsDisplayWrappers are added to the display list
374 // of the temporary item.
375 for (nsDisplayItem
* item
: aItems
) {
377 MOZ_ASSERT(merged
->CanMerge(item
));
379 MOZ_ASSERT(item
->AsDisplayWrapList());
380 merged
->GetChildren()->AppendToTop(
381 static_cast<nsDisplayWrapList
*>(item
)->CreateShallowCopy(this));
384 merged
->GetChildren()->AppendToTop(last
->CreateShallowCopy(this));
389 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
390 SetCurrentActiveScrolledRoot(
391 const ActiveScrolledRoot
* aActiveScrolledRoot
) {
394 // Set the builder's mCurrentActiveScrolledRoot.
395 mBuilder
->mCurrentActiveScrolledRoot
= aActiveScrolledRoot
;
397 // We also need to adjust the builder's mCurrentContainerASR.
398 // mCurrentContainerASR needs to be an ASR that all the container's
399 // contents have finite bounds with respect to. If aActiveScrolledRoot
400 // is an ancestor ASR of mCurrentContainerASR, that means we need to
401 // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
402 // the items that will be created with aActiveScrolledRoot wouldn't
403 // have finite bounds with respect to mCurrentContainerASR. There's one
404 // exception, in the case where there's a content clip on the builder
405 // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
406 // content clip will clip all items that are created while this
407 // AutoCurrentActiveScrolledRootSetter exists. This means that the items
408 // created during our lifetime will have finite bounds with respect to
409 // the content clip's ASR, even if the items' actual ASR is an ancestor
410 // of that. And it also means that mCurrentContainerASR only needs to be
411 // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
412 // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
413 // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
415 // finiteBoundsASR is the leafmost ASR that all items created during
416 // object's lifetime have finite bounds with respect to.
417 const ActiveScrolledRoot
* finiteBoundsASR
=
418 ActiveScrolledRoot::PickDescendant(mContentClipASR
, aActiveScrolledRoot
);
420 // mCurrentContainerASR is adjusted so that it's still an ancestor of
422 mBuilder
->mCurrentContainerASR
= ActiveScrolledRoot::PickAncestor(
423 mBuilder
->mCurrentContainerASR
, finiteBoundsASR
);
425 // If we are entering out-of-flow content inside a CSS filter, mark
426 // scroll frames wrt. which the content is fixed as containing such content.
427 if (mBuilder
->mFilterASR
&& ActiveScrolledRoot::IsAncestor(
428 aActiveScrolledRoot
, mBuilder
->mFilterASR
)) {
429 for (const ActiveScrolledRoot
* asr
= mBuilder
->mFilterASR
;
430 asr
&& asr
!= aActiveScrolledRoot
; asr
= asr
->mParent
) {
431 asr
->mScrollableFrame
->SetHasOutOfFlowContentInsideFilter();
438 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
439 InsertScrollFrame(nsIScrollableFrame
* aScrollableFrame
) {
441 size_t descendantsEndIndex
= mBuilder
->mActiveScrolledRoots
.Length();
442 const ActiveScrolledRoot
* parentASR
= mBuilder
->mCurrentActiveScrolledRoot
;
443 const ActiveScrolledRoot
* asr
=
444 mBuilder
->AllocateActiveScrolledRoot(parentASR
, aScrollableFrame
);
445 mBuilder
->mCurrentActiveScrolledRoot
= asr
;
447 // All child ASRs of parentASR that were created while this
448 // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
449 // now. Reparent them to asr.
450 for (size_t i
= mDescendantsStartIndex
; i
< descendantsEndIndex
; i
++) {
451 ActiveScrolledRoot
* descendantASR
= mBuilder
->mActiveScrolledRoots
[i
];
452 if (ActiveScrolledRoot::IsAncestor(parentASR
, descendantASR
)) {
453 descendantASR
->IncrementDepth();
454 if (descendantASR
->mParent
== parentASR
) {
455 descendantASR
->mParent
= asr
;
463 nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
464 nsDisplayListBuilder
* aBuilder
)
465 : mBuilder(aBuilder
), mSavedContainerASR(aBuilder
->mCurrentContainerASR
) {
466 mBuilder
->mCurrentContainerASR
= mBuilder
->mCurrentActiveScrolledRoot
;
469 nsPresContext
* nsDisplayListBuilder::CurrentPresContext() {
470 return CurrentPresShellState()->mPresShell
->GetPresContext();
474 nsRect
nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
475 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
476 const nsRect
& aVisibleRect
, const nsRect
& aDirtyRect
,
477 nsRect
* aOutDirtyRect
) {
478 nsRect visible
= aVisibleRect
;
479 nsRect dirtyRectRelativeToDirtyFrame
= aDirtyRect
;
481 bool inPartialUpdate
=
482 aBuilder
->IsRetainingDisplayList() && aBuilder
->IsPartialUpdate();
483 if (StaticPrefs::apz_allow_zooming() &&
484 DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame
) &&
485 aBuilder
->IsPaintingToWindow() && !inPartialUpdate
) {
486 dirtyRectRelativeToDirtyFrame
=
487 nsRect(nsPoint(0, 0), aFrame
->GetParent()->GetSize());
489 // If there's a visual viewport size set, restrict the amount of the
490 // fixed-position element we paint to the visual viewport. (In general
491 // the fixed-position element can be as large as the layout viewport,
492 // which at a high zoom level can cause us to paint too large of an
494 PresShell
* presShell
= aFrame
->PresShell();
495 if (presShell
->IsVisualViewportSizeSet()) {
496 dirtyRectRelativeToDirtyFrame
=
497 nsRect(presShell
->GetVisualViewportOffsetRelativeToLayoutViewport(),
498 presShell
->GetVisualViewportSize());
499 // But if we have a displayport, expand it to the displayport, so
500 // that async-scrolling the visual viewport within the layout viewport
501 // will not checkerboard.
502 if (nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame()) {
504 // Note that the displayport here is already in the right coordinate
505 // space: it's relative to the scroll port (= layout viewport), but
506 // covers the visual viewport with some margins around it, which is
507 // exactly what we want.
508 if (DisplayPortUtils::GetDisplayPort(
509 rootScrollFrame
->GetContent(), &displayport
,
510 DisplayPortOptions().With(ContentGeometryType::Fixed
))) {
511 dirtyRectRelativeToDirtyFrame
= displayport
;
515 visible
= dirtyRectRelativeToDirtyFrame
;
516 if (StaticPrefs::apz_test_logging_enabled() &&
517 presShell
->GetDocument()->IsContentDocument()) {
518 nsLayoutUtils::LogAdditionalTestData(
519 aBuilder
, "fixedPosDisplayport",
520 ToString(CSSSize::FromAppUnits(visible
)));
524 *aOutDirtyRect
= dirtyRectRelativeToDirtyFrame
- aFrame
->GetPosition();
525 visible
-= aFrame
->GetPosition();
527 nsRect overflowRect
= aFrame
->InkOverflowRect();
529 if (aFrame
->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
530 aFrame
, DisplayItemType::TYPE_TRANSFORM
)) {
532 * Add a fuzz factor to the overflow rectangle so that elements only
533 * just out of view are pulled into the display list, so they can be
534 * prerendered if necessary.
536 overflowRect
.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
539 visible
.IntersectRect(visible
, overflowRect
);
540 aOutDirtyRect
->IntersectRect(*aOutDirtyRect
, overflowRect
);
545 nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder
* aBuilder
,
547 nsDisplayList
* aList
)
549 // Find the element that we need to check for link-ness, bailing out if
550 // we can't find one.
551 Element
* elem
= Element::FromNodeOrNull(aFrame
->GetContent());
556 // If the element has an id and/or name attribute, generate a destination
557 // for possible internal linking.
558 auto maybeGenerateDest
= [&](const nsAtom
* aAttr
) {
559 nsAutoString attrValue
;
560 elem
->GetAttr(aAttr
, attrValue
);
561 if (!attrValue
.IsEmpty()) {
562 NS_ConvertUTF16toUTF8
dest(attrValue
);
563 // Ensure that we only emit a given destination once, although there may
564 // be multiple frames associated with a given element; we'll simply use
565 // the first of them as the target of any links to it.
566 // XXX(jfkthame) This prevents emitting duplicate destinations *on the
567 // same page*, but does not prevent duplicates on subsequent pages, as
568 // each new page is handled by a new temporary DisplayListBuilder. This
569 // seems to be harmless in practice, though a bit wasteful of space. To
570 // fix, we need to maintain the set of already-seen destinations globally
571 // for the print job, rather than attached to the (per-page) builder.
572 if (aBuilder
->mDestinations
.EnsureInserted(dest
)) {
573 auto* destination
= MakeDisplayItem
<nsDisplayDestination
>(
574 aBuilder
, aFrame
, dest
.get(), aFrame
->GetRect().TopLeft());
575 mList
->AppendToTop(destination
);
580 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
582 maybeGenerateDest(nsGkAtoms::id
);
584 if (elem
->HasName()) {
585 maybeGenerateDest(nsGkAtoms::name
);
589 // Links don't nest, so if the builder already has a destination, no need to
590 // check for a link element here.
591 if (!aBuilder
->mLinkSpec
.IsEmpty()) {
595 // Check if we have actually found a link.
596 if (!elem
->IsLink()) {
600 nsCOMPtr
<nsIURI
> uri
= elem
->GetHrefURI();
605 // Is it a local (in-page) destination?
606 bool hasRef
, eqExRef
;
608 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
609 NS_SUCCEEDED(uri
->GetHasRef(&hasRef
)) && hasRef
&&
610 (docURI
= aFrame
->PresContext()->Document()->GetDocumentURI()) &&
611 NS_SUCCEEDED(uri
->EqualsExceptRef(docURI
, &eqExRef
)) && eqExRef
) {
612 if (NS_FAILED(uri
->GetRef(aBuilder
->mLinkSpec
)) ||
613 aBuilder
->mLinkSpec
.IsEmpty()) {
616 // The destination name is simply a string; we don't want URL-escaping
618 NS_UnescapeURL(aBuilder
->mLinkSpec
);
619 // Mark the link spec as being an internal destination
620 aBuilder
->mLinkSpec
.Insert('#', 0);
622 if (NS_FAILED(uri
->GetSpec(aBuilder
->mLinkSpec
)) ||
623 aBuilder
->mLinkSpec
.IsEmpty()) {
628 // Record that we need to reset the builder's state on destruction.
629 mBuilderToReset
= aBuilder
;
632 void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
633 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
) {
634 // Note that we may generate a link here even if the constructor bailed out
635 // without updating aBuilder->LinkSpec(), because it may have been set by
636 // an ancestor that was associated with a link element.
637 if (!aBuilder
->mLinkSpec
.IsEmpty()) {
638 auto* link
= MakeDisplayItem
<nsDisplayLink
>(
639 aBuilder
, aFrame
, aBuilder
->mLinkSpec
.get(), aFrame
->GetRect());
640 mList
->AppendToTop(link
);
644 uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1);
646 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame
* aReferenceFrame
,
647 nsDisplayListBuilderMode aMode
,
649 bool aRetainingDisplayList
)
650 : mReferenceFrame(aReferenceFrame
),
651 mIgnoreScrollFrame(nullptr),
652 mCurrentActiveScrolledRoot(nullptr),
653 mCurrentContainerASR(nullptr),
654 mCurrentFrame(aReferenceFrame
),
655 mCurrentReferenceFrame(aReferenceFrame
),
656 mCaretFrame(nullptr),
657 mScrollInfoItemsForHoisting(nullptr),
658 mFirstClipChainToDestroy(nullptr),
659 mTableBackgroundSet(nullptr),
660 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID
),
661 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID
),
663 mDirtyRect(-1, -1, -1, -1),
664 mBuildingExtraPagesForPageNum(0),
666 mContainsBlendMode(false),
667 mIsBuildingScrollbar(false),
668 mCurrentScrollbarWillHaveLayer(false),
669 mBuildCaret(aBuildCaret
),
670 mRetainingDisplayList(aRetainingDisplayList
),
671 mPartialUpdate(false),
672 mIgnoreSuppression(false),
673 mIncludeAllOutOfFlows(false),
674 mDescendIntoSubdocuments(true),
675 mSelectedFramesOnly(false),
676 mAllowMergingAndFlattening(true),
678 mInEventsOnly(false),
680 mInPageSequence(false),
681 mIsInChromePresContext(false),
682 mSyncDecodeImages(false),
683 mIsPaintingToWindow(false),
684 mUseHighQualityScaling(false),
685 mIsPaintingForWebRender(false),
686 mIsCompositingCheap(false),
687 mAncestorHasApzAwareEventHandler(false),
688 mHaveScrollableDisplayPort(false),
689 mWindowDraggingAllowed(false),
690 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame
)),
691 mForceLayerForScrollParent(false),
692 mContainsNonMinimalDisplayPort(false),
693 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame
)),
694 mBuildingInvisibleItems(false),
696 mInInvalidSubtree(false),
697 mDisablePartialUpdates(false),
698 mPartialBuildFailed(false),
699 mIsInActiveDocShell(false),
700 mBuildAsyncZoomContainer(false),
701 mIsRelativeToLayoutViewport(false),
702 mUseOverlayScrollbars(false),
703 mAlwaysLayerizeScrollbars(false) {
704 MOZ_COUNT_CTOR(nsDisplayListBuilder
);
706 mBuildCompositorHitTestInfo
= mAsyncPanZoomEnabled
&& IsForPainting();
708 ShouldRebuildDisplayListDueToPrefChange();
710 mUseOverlayScrollbars
=
711 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars
);
713 mAlwaysLayerizeScrollbars
=
714 StaticPrefs::layout_scrollbars_always_layerize_track();
717 static_cast<uint32_t>(DisplayItemType::TYPE_MAX
) < (1 << TYPE_BITS
),
718 "Check TYPE_MAX should not overflow");
720 mIsReusingStackingContextItems
=
721 mRetainingDisplayList
&& StaticPrefs::layout_display_list_retain_sc();
724 static PresShell
* GetFocusedPresShell() {
725 nsPIDOMWindowOuter
* focusedWnd
=
726 nsFocusManager::GetFocusManager()->GetFocusedWindow();
731 nsCOMPtr
<nsIDocShell
> focusedDocShell
= focusedWnd
->GetDocShell();
732 if (!focusedDocShell
) {
736 return focusedDocShell
->GetPresShell();
739 void nsDisplayListBuilder::BeginFrame() {
740 nsCSSRendering::BeginFrameTreesLocked();
742 mIsPaintingToWindow
= false;
743 mUseHighQualityScaling
= false;
744 mIgnoreSuppression
= false;
745 mInTransform
= false;
747 mSyncDecodeImages
= false;
753 RefPtr
<PresShell
> presShell
= GetFocusedPresShell();
755 RefPtr
<nsCaret
> caret
= presShell
->GetCaret();
756 mCaretFrame
= caret
->GetPaintGeometry(&mCaretRect
);
758 // The focused pres shell may not be in the document that we're
759 // painting, or be in a popup. Check if the display root for
760 // the caret matches the display root that we're painting, and
761 // only use it if it matches.
763 nsLayoutUtils::GetDisplayRootFrame(mCaretFrame
) !=
764 nsLayoutUtils::GetDisplayRootFrame(mReferenceFrame
)) {
765 mCaretFrame
= nullptr;
770 void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser
* aBrowser
,
771 const dom::EffectsInfo
& aUpdate
) {
772 dom::EffectsInfo update
= aUpdate
;
773 // For printing we create one display item for each page that an iframe
774 // appears on, the proper visible rect is the union of all the visible rects
775 // we get from each display item.
777 mReferenceFrame
? mReferenceFrame
->PresContext() : nullptr;
778 if (pc
&& pc
->Type() != nsPresContext::eContext_Galley
) {
779 Maybe
<dom::EffectsInfo
> existing
= mEffectsUpdates
.MaybeGet(aBrowser
);
781 // Only the visible rect should differ, the scales should match.
782 MOZ_ASSERT(existing
->mRasterScale
== aUpdate
.mRasterScale
&&
783 existing
->mTransformToAncestorScale
==
784 aUpdate
.mTransformToAncestorScale
);
785 if (existing
->mVisibleRect
) {
786 if (update
.mVisibleRect
) {
787 update
.mVisibleRect
=
788 Some(update
.mVisibleRect
->Union(*existing
->mVisibleRect
));
790 update
.mVisibleRect
= existing
->mVisibleRect
;
795 mEffectsUpdates
.InsertOrUpdate(aBrowser
, update
);
798 void nsDisplayListBuilder::EndFrame() {
799 NS_ASSERTION(!mInInvalidSubtree
,
800 "Someone forgot to cleanup mInInvalidSubtree!");
801 mCurrentContainerASR
= nullptr;
802 mActiveScrolledRoots
.Clear();
803 mEffectsUpdates
.Clear();
805 FreeTemporaryItems();
806 nsCSSRendering::EndFrameTreesLocked();
807 mCaretFrame
= nullptr;
810 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame
* aFrame
,
811 const nsIFrame
* aStopAtFrame
) {
812 mFramesMarkedForDisplay
.AppendElement(aFrame
);
813 for (nsIFrame
* f
= aFrame
; f
;
814 f
= nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f
)) {
815 if (f
->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
)) {
818 f
->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
);
819 if (f
== aStopAtFrame
) {
820 // we've reached a frame that we know will be painted, so we can stop.
826 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame
* aFrame
) {
827 mFramesMarkedForDisplayIfVisible
.AppendElement(aFrame
);
830 static void MarkFrameForDisplayIfVisibleInternal(nsIFrame
* aFrame
,
831 const nsIFrame
* aStopAtFrame
) {
832 for (nsIFrame
* f
= aFrame
; f
; f
= nsLayoutUtils::GetDisplayListParent(f
)) {
833 if (f
->ForceDescendIntoIfVisible()) {
836 f
->SetForceDescendIntoIfVisible(true);
838 // This condition must match the condition in
839 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
840 // nsLayoutUtils::GetDisplayListParent
841 if (f
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
) && !f
->GetPrevInFlow()) {
842 nsIFrame
* parent
= f
->GetParent();
843 if (parent
&& !parent
->ForceDescendIntoIfVisible()) {
844 // If the GetDisplayListParent call is going to walk to a placeholder,
845 // in rare cases the placeholder might be contained in a different
846 // continuation from the oof. So we have to make sure to mark the oofs
847 // parent. In the common case this doesn't make us do any extra work,
848 // just changes the order in which we visit the frames since walking
849 // through placeholders will walk through the parent, and we stop when
850 // we find a ForceDescendIntoIfVisible bit set.
851 MarkFrameForDisplayIfVisibleInternal(parent
, aStopAtFrame
);
855 if (f
== aStopAtFrame
) {
856 // we've reached a frame that we know will be painted, so we can stop.
862 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
863 nsIFrame
* aFrame
, const nsIFrame
* aStopAtFrame
) {
864 AddFrameMarkedForDisplayIfVisible(aFrame
);
866 MarkFrameForDisplayIfVisibleInternal(aFrame
, aStopAtFrame
);
869 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
870 mIsRelativeToLayoutViewport
= true;
871 UpdateShouldBuildAsyncZoomContainer();
874 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
875 const Document
* document
= mReferenceFrame
->PresContext()->Document();
876 mBuildAsyncZoomContainer
= !mIsRelativeToLayoutViewport
&&
877 !document
->Fullscreen() &&
878 nsLayoutUtils::AllowZoomingForDocument(document
);
881 // Certain prefs may cause display list items to be added or removed when they
882 // are toggled. In those cases, we need to fully rebuild the display list.
883 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
884 // If we transition between wrapping the RCD-RSF contents into an async
885 // zoom container vs. not, we need to rebuild the display list. This only
886 // happens when the zooming or container scrolling prefs are toggled
887 // (manually by the user, or during test setup).
888 bool didBuildAsyncZoomContainer
= mBuildAsyncZoomContainer
;
889 UpdateShouldBuildAsyncZoomContainer();
891 bool hadOverlayScrollbarsLastTime
= mUseOverlayScrollbars
;
892 mUseOverlayScrollbars
=
893 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars
);
895 bool alwaysLayerizedScrollbarsLastTime
= mAlwaysLayerizeScrollbars
;
896 mAlwaysLayerizeScrollbars
=
897 StaticPrefs::layout_scrollbars_always_layerize_track();
899 if (didBuildAsyncZoomContainer
!= mBuildAsyncZoomContainer
) {
903 if (hadOverlayScrollbarsLastTime
!= mUseOverlayScrollbars
) {
907 if (alwaysLayerizedScrollbarsLastTime
!= mAlwaysLayerizeScrollbars
) {
914 void nsDisplayListBuilder::AddScrollFrameToNotify(
915 nsIScrollableFrame
* aScrollFrame
) {
916 mScrollFramesToNotify
.insert(aScrollFrame
);
919 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
920 for (const auto& it
: mScrollFramesToNotify
) {
921 it
->NotifyApzTransaction();
923 mScrollFramesToNotify
.clear();
926 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
927 nsIFrame
* aDirtyFrame
, nsIFrame
* aFrame
, const nsRect
& aVisibleRect
,
928 const nsRect
& aDirtyRect
) {
929 MOZ_ASSERT(aFrame
->GetParent() == aDirtyFrame
);
931 nsRect visible
= OutOfFlowDisplayData::ComputeVisibleRectForFrame(
932 this, aFrame
, aVisibleRect
, aDirtyRect
, &dirty
);
933 if (!aFrame
->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) &&
938 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
939 // frame, then it will also mark any outer frames to ensure that building
940 // reaches the dirty feame.
941 if (!dirty
.IsEmpty() || aFrame
->ForceDescendIntoIfVisible()) {
942 MarkFrameForDisplay(aFrame
, aDirtyFrame
);
948 static void UnmarkFrameForDisplay(nsIFrame
* aFrame
,
949 const nsIFrame
* aStopAtFrame
) {
950 for (nsIFrame
* f
= aFrame
; f
;
951 f
= nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f
)) {
952 if (!f
->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
)) {
955 f
->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
);
956 if (f
== aStopAtFrame
) {
957 // we've reached a frame that we know will be painted, so we can stop.
963 static void UnmarkFrameForDisplayIfVisible(nsIFrame
* aFrame
) {
964 for (nsIFrame
* f
= aFrame
; f
; f
= nsLayoutUtils::GetDisplayListParent(f
)) {
965 if (!f
->ForceDescendIntoIfVisible()) {
968 f
->SetForceDescendIntoIfVisible(false);
970 // This condition must match the condition in
971 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
972 // nsLayoutUtils::GetDisplayListParent
973 if (f
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
) && !f
->GetPrevInFlow()) {
974 nsIFrame
* parent
= f
->GetParent();
975 if (parent
&& parent
->ForceDescendIntoIfVisible()) {
976 // If the GetDisplayListParent call is going to walk to a placeholder,
977 // in rare cases the placeholder might be contained in a different
978 // continuation from the oof. So we have to make sure to mark the oofs
979 // parent. In the common case this doesn't make us do any extra work,
980 // just changes the order in which we visit the frames since walking
981 // through placeholders will walk through the parent, and we stop when
982 // we find a ForceDescendIntoIfVisible bit set.
983 UnmarkFrameForDisplayIfVisible(f
);
989 nsDisplayListBuilder::~nsDisplayListBuilder() {
990 NS_ASSERTION(mFramesMarkedForDisplay
.Length() == 0,
991 "All frames should have been unmarked");
992 NS_ASSERTION(mFramesWithOOFData
.Length() == 0,
993 "All OOF data should have been removed");
994 NS_ASSERTION(mPresShellStates
.Length() == 0,
995 "All presshells should have been exited");
997 DisplayItemClipChain
* c
= mFirstClipChainToDestroy
;
999 DisplayItemClipChain
* next
= c
->mNextClipChainToDestroy
;
1000 c
->DisplayItemClipChain::~DisplayItemClipChain();
1004 MOZ_COUNT_DTOR(nsDisplayListBuilder
);
1007 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
1009 if (mSyncDecodeImages
) {
1010 flags
|= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES
;
1012 if (mIsPaintingToWindow
) {
1013 flags
|= nsCSSRendering::PAINTBG_TO_WINDOW
;
1015 if (mUseHighQualityScaling
) {
1016 flags
|= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING
;
1021 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
1022 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
1024 if (mSyncDecodeImages
) {
1025 flags
|= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES
;
1027 if (mIsPaintingToWindow
) {
1028 flags
|= nsImageRenderer::FLAG_PAINTING_TO_WINDOW
;
1030 if (mUseHighQualityScaling
) {
1031 flags
|= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING
;
1036 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1037 uint32_t flags
= imgIContainer::FLAG_ASYNC_NOTIFY
;
1038 if (mSyncDecodeImages
) {
1039 flags
|= imgIContainer::FLAG_SYNC_DECODE
;
1041 flags
|= imgIContainer::FLAG_SYNC_DECODE_IF_FAST
;
1043 if (mIsPaintingToWindow
|| mUseHighQualityScaling
) {
1044 flags
|= imgIContainer::FLAG_HIGH_QUALITY_SCALING
;
1049 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion
* aVisibleRegion
,
1050 const nsRegion
& aRegion
) {
1051 if (aRegion
.IsEmpty()) {
1056 tmp
.Sub(*aVisibleRegion
, aRegion
);
1057 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1058 // to its bounds either, which can be very bad (see bug 516740).
1059 // Do let aVisibleRegion get more complex if by doing so we reduce its
1060 // area by at least half.
1061 if (tmp
.GetNumRects() <= 15 || tmp
.Area() <= aVisibleRegion
->Area() / 2) {
1062 *aVisibleRegion
= tmp
;
1066 nsCaret
* nsDisplayListBuilder::GetCaret() {
1067 RefPtr
<nsCaret
> caret
= CurrentPresShellState()->mPresShell
->GetCaret();
1071 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell
* aPresShell
) {
1072 if (mIsPaintingToWindow
) {
1073 aPresShell
->IncrementPaintCount();
1077 void nsDisplayListBuilder::EnterPresShell(const nsIFrame
* aReferenceFrame
,
1078 bool aPointerEventsNoneDoc
) {
1079 PresShellState
* state
= mPresShellStates
.AppendElement();
1080 state
->mPresShell
= aReferenceFrame
->PresShell();
1081 state
->mFirstFrameMarkedForDisplay
= mFramesMarkedForDisplay
.Length();
1082 state
->mFirstFrameWithOOFData
= mFramesWithOOFData
.Length();
1084 nsIScrollableFrame
* sf
= state
->mPresShell
->GetRootScrollFrameAsScrollable();
1085 if (sf
&& IsInSubdocument()) {
1086 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1087 // that the canvas background color will be set correctly, and that only one
1088 // unscrollable item will be created.
1089 // This is done to avoid, for example, a case where only scrollbar frames
1090 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1091 // and possibly end up with an extra nsDisplaySolidColor item.
1092 // We skip this for the root document, since we don't want to use
1093 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1094 // do it manually there.
1095 nsCanvasFrame
* canvasFrame
= do_QueryFrame(sf
->GetScrolledFrame());
1097 MarkFrameForDisplayIfVisible(canvasFrame
, aReferenceFrame
);
1102 state
->mAutoLayoutPhase
.emplace(aReferenceFrame
->PresContext(),
1103 nsLayoutPhase::DisplayListBuilding
);
1106 state
->mPresShell
->UpdateCanvasBackground();
1108 bool buildCaret
= mBuildCaret
;
1109 if (mIgnoreSuppression
|| !state
->mPresShell
->IsPaintingSuppressed()) {
1110 state
->mIsBackgroundOnly
= false;
1112 state
->mIsBackgroundOnly
= true;
1116 bool pointerEventsNone
= aPointerEventsNoneDoc
;
1117 if (IsInSubdocument()) {
1118 pointerEventsNone
|= mPresShellStates
[mPresShellStates
.Length() - 2]
1119 .mInsidePointerEventsNoneDoc
;
1121 state
->mInsidePointerEventsNoneDoc
= pointerEventsNone
;
1123 state
->mPresShellIgnoreScrollFrame
=
1124 state
->mPresShell
->IgnoringViewportScrolling()
1125 ? state
->mPresShell
->GetRootScrollFrame()
1128 nsPresContext
* pc
= aReferenceFrame
->PresContext();
1129 mIsInChromePresContext
= pc
->IsChrome();
1130 nsIDocShell
* docShell
= pc
->GetDocShell();
1133 docShell
->GetWindowDraggingAllowed(&mWindowDraggingAllowed
);
1136 state
->mTouchEventPrefEnabledDoc
= dom::TouchEvent::PrefEnabled(docShell
);
1142 // Caret frames add visual area to their frame, but we don't update the
1143 // overflow area. Use flags to make sure we build display items for that frame
1145 if (mCaretFrame
&& mCaretFrame
->PresShell() == state
->mPresShell
) {
1146 MarkFrameForDisplay(mCaretFrame
, aReferenceFrame
);
1150 // A non-blank paint is a paint that does not just contain the canvas
1152 static bool DisplayListIsNonBlank(nsDisplayList
* aList
) {
1153 for (nsDisplayItem
* i
: *aList
) {
1154 switch (i
->GetType()) {
1155 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
:
1156 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR
:
1157 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE
:
1159 case DisplayItemType::TYPE_SOLID_COLOR
:
1160 case DisplayItemType::TYPE_BACKGROUND
:
1161 case DisplayItemType::TYPE_BACKGROUND_COLOR
:
1162 if (i
->Frame()->IsCanvasFrame()) {
1173 // A contentful paint is a paint that does contains DOM content (text,
1174 // images, non-blank canvases, SVG): "First Contentful Paint entry
1175 // contains a DOMHighResTimeStamp reporting the time when the browser
1176 // first rendered any text, image (including background images),
1177 // non-white canvas or SVG. This excludes any content of iframes, but
1178 // includes text with pending webfonts. This is the first time users
1179 // could start consuming page content."
1180 static bool DisplayListIsContentful(nsDisplayListBuilder
* aBuilder
,
1181 nsDisplayList
* aList
) {
1182 for (nsDisplayItem
* i
: *aList
) {
1183 DisplayItemType type
= i
->GetType();
1184 nsDisplayList
* children
= i
->GetChildren();
1187 case DisplayItemType::TYPE_SUBDOCUMENT
: // iframes are ignored
1189 // CANVASes check if they may have been modified (as a stand-in
1190 // actually tracking all modifications)
1192 if (i
->IsContentful()) {
1194 nsRect bound
= i
->GetBounds(aBuilder
, &dummy
);
1195 if (!bound
.IsEmpty()) {
1200 if (DisplayListIsContentful(aBuilder
, children
)) {
1210 void nsDisplayListBuilder::LeavePresShell(const nsIFrame
* aReferenceFrame
,
1211 nsDisplayList
* aPaintedContents
) {
1213 CurrentPresShellState()->mPresShell
== aReferenceFrame
->PresShell(),
1214 "Presshell mismatch");
1216 if (mIsPaintingToWindow
&& aPaintedContents
) {
1217 nsPresContext
* pc
= aReferenceFrame
->PresContext();
1218 if (!pc
->HadNonBlankPaint()) {
1219 if (!CurrentPresShellState()->mIsBackgroundOnly
&&
1220 DisplayListIsNonBlank(aPaintedContents
)) {
1221 pc
->NotifyNonBlankPaint();
1224 nsRootPresContext
* rootPresContext
= pc
->GetRootPresContext();
1225 if (!pc
->HasStoppedGeneratingLCP() && rootPresContext
) {
1226 if (!CurrentPresShellState()->mIsBackgroundOnly
) {
1227 if (pc
->HasEverBuiltInvisibleText() ||
1228 DisplayListIsContentful(this, aPaintedContents
)) {
1229 pc
->NotifyContentfulPaint();
1235 ResetMarkedFramesForDisplayList(aReferenceFrame
);
1236 mPresShellStates
.RemoveLastElement();
1238 if (!mPresShellStates
.IsEmpty()) {
1239 nsPresContext
* pc
= CurrentPresContext();
1240 nsIDocShell
* docShell
= pc
->GetDocShell();
1242 docShell
->GetWindowDraggingAllowed(&mWindowDraggingAllowed
);
1244 mIsInChromePresContext
= pc
->IsChrome();
1246 for (uint32_t i
= 0; i
< mFramesMarkedForDisplayIfVisible
.Length(); ++i
) {
1247 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible
[i
]);
1249 mFramesMarkedForDisplayIfVisible
.SetLength(0);
1253 void nsDisplayListBuilder::FreeClipChains() {
1254 // Iterate the clip chains from newest to oldest (forward
1255 // iteration), so that we destroy descendants first which
1256 // will drop the ref count on their ancestors.
1257 DisplayItemClipChain
** indirect
= &mFirstClipChainToDestroy
;
1260 if (!(*indirect
)->mRefCount
) {
1261 DisplayItemClipChain
* next
= (*indirect
)->mNextClipChainToDestroy
;
1263 mClipDeduplicator
.erase(*indirect
);
1264 (*indirect
)->DisplayItemClipChain::~DisplayItemClipChain();
1265 Destroy(DisplayListArenaObjectId::CLIPCHAIN
, *indirect
);
1269 indirect
= &(*indirect
)->mNextClipChainToDestroy
;
1274 void nsDisplayListBuilder::FreeTemporaryItems() {
1275 for (nsDisplayItem
* i
: mTemporaryItems
) {
1276 // Temporary display items are not added to the frames.
1277 MOZ_ASSERT(i
->Frame());
1278 i
->RemoveFrame(i
->Frame());
1282 mTemporaryItems
.Clear();
1285 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1286 const nsIFrame
* aReferenceFrame
) {
1287 // Unmark and pop off the frames marked for display in this pres shell.
1288 uint32_t firstFrameForShell
=
1289 CurrentPresShellState()->mFirstFrameMarkedForDisplay
;
1290 for (uint32_t i
= firstFrameForShell
; i
< mFramesMarkedForDisplay
.Length();
1292 UnmarkFrameForDisplay(mFramesMarkedForDisplay
[i
], aReferenceFrame
);
1294 mFramesMarkedForDisplay
.SetLength(firstFrameForShell
);
1296 firstFrameForShell
= CurrentPresShellState()->mFirstFrameWithOOFData
;
1297 for (uint32_t i
= firstFrameForShell
; i
< mFramesWithOOFData
.Length(); ++i
) {
1298 mFramesWithOOFData
[i
]->RemoveProperty(OutOfFlowDisplayDataProperty());
1300 mFramesWithOOFData
.SetLength(firstFrameForShell
);
1303 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1304 CurrentPresShellState()->mFixedBackgroundDisplayData
= Nothing();
1307 void nsDisplayListBuilder::MarkFramesForDisplayList(
1308 nsIFrame
* aDirtyFrame
, const nsFrameList
& aFrames
) {
1309 nsRect visibleRect
= GetVisibleRect();
1310 nsRect dirtyRect
= GetDirtyRect();
1312 // If we are entering content that is fixed to the RCD-RSF, we are
1313 // crossing the async zoom container boundary, and need to convert from
1314 // visual to layout coordinates.
1315 if (ViewportFrame
* viewportFrame
= do_QueryFrame(aDirtyFrame
)) {
1316 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1317 viewportFrame
->PresContext()->IsRootContentDocumentCrossProcess()) {
1318 if (viewportFrame
->PresShell()->GetRootScrollFrame()) {
1320 for (nsIFrame
* f
: aFrames
) {
1321 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f
));
1324 visibleRect
= ViewportUtils::VisualToLayout(visibleRect
,
1325 viewportFrame
->PresShell());
1326 dirtyRect
= ViewportUtils::VisualToLayout(dirtyRect
,
1327 viewportFrame
->PresShell());
1331 // This is an edge case that should only happen if we are in a
1332 // document with a XUL root element so that it does not have a root
1333 // scroll frame but it has fixed pos content and all of the frames in
1334 // aFrames are that fixed pos content.
1335 for (nsIFrame
* f
: aFrames
) {
1336 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f
) &&
1337 f
->GetParent() == aDirtyFrame
&&
1338 f
->StyleDisplay()->mPosition
==
1339 StylePositionProperty::Fixed
);
1341 // There's no root scroll frame so there can't be any zooming or async
1342 // panning so we don't need to adjust the visible and dirty rects.
1348 bool markedFrames
= false;
1349 for (nsIFrame
* e
: aFrames
) {
1350 // Skip the AccessibleCaret frame when building no caret.
1351 if (!IsBuildingCaret()) {
1352 nsIContent
* content
= e
->GetContent();
1353 if (content
&& content
->IsInNativeAnonymousSubtree() &&
1354 content
->IsElement()) {
1355 const nsAttrValue
* classes
= content
->AsElement()->GetClasses();
1357 classes
->Contains(nsGkAtoms::mozAccessiblecaret
, eCaseMatters
)) {
1362 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame
, e
, visibleRect
, dirtyRect
)) {
1363 markedFrames
= true;
1368 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1369 // to objects on the stack, so we need to clone the chain.
1370 const DisplayItemClipChain
* clipChain
=
1371 CopyWholeChain(mClipState
.GetClipChainForContainingBlockDescendants());
1372 const DisplayItemClipChain
* combinedClipChain
=
1373 mClipState
.GetCurrentCombinedClipChain(this);
1374 const ActiveScrolledRoot
* asr
= mCurrentActiveScrolledRoot
;
1376 OutOfFlowDisplayData
* data
= new OutOfFlowDisplayData(
1377 clipChain
, combinedClipChain
, asr
, this->mCurrentScrollParentId
,
1378 visibleRect
, dirtyRect
);
1379 aDirtyFrame
->SetProperty(
1380 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data
);
1381 mFramesWithOOFData
.AppendElement(aDirtyFrame
);
1384 if (!aDirtyFrame
->GetParent()) {
1385 // This is the viewport frame of aDirtyFrame's presshell.
1386 // Store the current display data so that it can be used for fixed
1387 // background images.
1389 CurrentPresShellState()->mPresShell
== aDirtyFrame
->PresShell(),
1390 "Presshell mismatch");
1391 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData
,
1392 "already traversed this presshell's root frame?");
1394 const DisplayItemClipChain
* clipChain
=
1395 CopyWholeChain(mClipState
.GetClipChainForContainingBlockDescendants());
1396 const DisplayItemClipChain
* combinedClipChain
=
1397 mClipState
.GetCurrentCombinedClipChain(this);
1398 const ActiveScrolledRoot
* asr
= mCurrentActiveScrolledRoot
;
1399 CurrentPresShellState()->mFixedBackgroundDisplayData
.emplace(
1400 clipChain
, combinedClipChain
, asr
, this->mCurrentScrollParentId
,
1401 GetVisibleRect(), GetDirtyRect());
1406 * Mark all preserve-3d children with
1407 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1408 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1409 * dirty rect for preserve-3d children.
1411 * @param aDirtyFrame is the frame to mark children extending context.
1413 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1414 nsIFrame
* aDirtyFrame
) {
1415 for (const auto& childList
: aDirtyFrame
->ChildLists()) {
1416 for (nsIFrame
* child
: childList
.mList
) {
1417 if (child
->Combines3DTransformWithAncestors()) {
1418 MarkFrameForDisplay(child
, aDirtyFrame
);
1421 if (child
->IsBlockWrapper()) {
1422 // Mark preserve-3d frames inside the block wrapper.
1423 MarkPreserve3DFramesForDisplayList(child
);
1429 ActiveScrolledRoot
* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1430 const ActiveScrolledRoot
* aParent
, nsIScrollableFrame
* aScrollableFrame
) {
1431 RefPtr
<ActiveScrolledRoot
> asr
= ActiveScrolledRoot::CreateASRForFrame(
1432 aParent
, aScrollableFrame
, IsRetainingDisplayList());
1433 mActiveScrolledRoots
.AppendElement(asr
);
1437 const DisplayItemClipChain
* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1438 const DisplayItemClip
& aClip
, const ActiveScrolledRoot
* aASR
,
1439 const DisplayItemClipChain
* aParent
) {
1440 MOZ_DIAGNOSTIC_ASSERT(!(aParent
&& aParent
->mOnStack
));
1441 void* p
= Allocate(sizeof(DisplayItemClipChain
),
1442 DisplayListArenaObjectId::CLIPCHAIN
);
1443 DisplayItemClipChain
* c
= new (KnownNotNull
, p
)
1444 DisplayItemClipChain(aClip
, aASR
, aParent
, mFirstClipChainToDestroy
);
1445 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
1446 c
->mOnStack
= false;
1448 auto result
= mClipDeduplicator
.insert(c
);
1449 if (!result
.second
) {
1450 // An equivalent clip chain item was already created, so let's return that
1451 // instead. Destroy the one we just created.
1452 // Note that this can cause clip chains from different coordinate systems to
1453 // collapse into the same clip chain object, because clip chains do not keep
1454 // track of the reference frame that they were created in.
1455 c
->DisplayItemClipChain::~DisplayItemClipChain();
1456 Destroy(DisplayListArenaObjectId::CLIPCHAIN
, c
);
1457 return *(result
.first
);
1459 mFirstClipChainToDestroy
= c
;
1463 struct ClipChainItem
{
1464 DisplayItemClip clip
;
1465 const ActiveScrolledRoot
* asr
;
1468 static const DisplayItemClipChain
* FindCommonAncestorClipForIntersection(
1469 const DisplayItemClipChain
* aOne
, const DisplayItemClipChain
* aTwo
) {
1470 for (const ActiveScrolledRoot
* asr
=
1471 ActiveScrolledRoot::PickDescendant(aOne
->mASR
, aTwo
->mASR
);
1472 asr
; asr
= asr
->mParent
) {
1476 if (aOne
->mASR
== asr
) {
1477 aOne
= aOne
->mParent
;
1479 if (aTwo
->mASR
== asr
) {
1480 aTwo
= aTwo
->mParent
;
1492 const DisplayItemClipChain
* nsDisplayListBuilder::CreateClipChainIntersection(
1493 const DisplayItemClipChain
* aAncestor
,
1494 const DisplayItemClipChain
* aLeafClip1
,
1495 const DisplayItemClipChain
* aLeafClip2
) {
1496 AutoTArray
<ClipChainItem
, 8> intersectedClips
;
1498 const DisplayItemClipChain
* clip1
= aLeafClip1
;
1499 const DisplayItemClipChain
* clip2
= aLeafClip2
;
1501 const ActiveScrolledRoot
* asr
= ActiveScrolledRoot::PickDescendant(
1502 clip1
? clip1
->mASR
: nullptr, clip2
? clip2
->mASR
: nullptr);
1504 // Build up the intersection from the leaf to the root and put it into
1505 // intersectedClips. The loop below will convert intersectedClips into an
1506 // actual DisplayItemClipChain.
1507 // (We need to do this in two passes because we need the parent clip in order
1508 // to create the DisplayItemClipChain object, but the parent clip has not
1509 // been created at that point.)
1510 while (!aAncestor
|| asr
!= aAncestor
->mASR
) {
1511 if (clip1
&& clip1
->mASR
== asr
) {
1512 if (clip2
&& clip2
->mASR
== asr
) {
1513 DisplayItemClip intersection
= clip1
->mClip
;
1514 intersection
.IntersectWith(clip2
->mClip
);
1515 intersectedClips
.AppendElement(ClipChainItem
{intersection
, asr
});
1516 clip2
= clip2
->mParent
;
1518 intersectedClips
.AppendElement(ClipChainItem
{clip1
->mClip
, asr
});
1520 clip1
= clip1
->mParent
;
1521 } else if (clip2
&& clip2
->mASR
== asr
) {
1522 intersectedClips
.AppendElement(ClipChainItem
{clip2
->mClip
, asr
});
1523 clip2
= clip2
->mParent
;
1526 MOZ_ASSERT(!aAncestor
, "We should have exited this loop earlier");
1532 // Convert intersectedClips into a DisplayItemClipChain.
1533 const DisplayItemClipChain
* parentSC
= aAncestor
;
1534 for (auto& sc
: Reversed(intersectedClips
)) {
1535 parentSC
= AllocateDisplayItemClipChain(sc
.clip
, sc
.asr
, parentSC
);
1540 const DisplayItemClipChain
* nsDisplayListBuilder::CreateClipChainIntersection(
1541 const DisplayItemClipChain
* aLeafClip1
,
1542 const DisplayItemClipChain
* aLeafClip2
) {
1543 // aLeafClip2 might be a reference to a clip on the stack. We need to make
1544 // sure that CreateClipChainIntersection will allocate the actual intersected
1545 // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1546 // we supply nullptr as the common ancestor so that
1547 // CreateClipChainIntersection clones the whole chain.
1548 const DisplayItemClipChain
* ancestorClip
=
1549 aLeafClip1
? FindCommonAncestorClipForIntersection(aLeafClip1
, aLeafClip2
)
1552 return CreateClipChainIntersection(ancestorClip
, aLeafClip1
, aLeafClip2
);
1555 const DisplayItemClipChain
* nsDisplayListBuilder::CopyWholeChain(
1556 const DisplayItemClipChain
* aClipChain
) {
1557 return CreateClipChainIntersection(nullptr, aClipChain
, nullptr);
1560 const nsIFrame
* nsDisplayListBuilder::FindReferenceFrameFor(
1561 const nsIFrame
* aFrame
, nsPoint
* aOffset
) const {
1562 auto MaybeApplyAdditionalOffset
= [&]() {
1563 if (auto offset
= AdditionalOffset()) {
1564 *aOffset
+= *offset
;
1568 if (aFrame
== mCurrentFrame
) {
1570 *aOffset
= mCurrentOffsetToReferenceFrame
;
1572 return mCurrentReferenceFrame
;
1575 for (const nsIFrame
* f
= aFrame
; f
;
1576 f
= nsLayoutUtils::GetCrossDocParentFrameInProcess(f
)) {
1577 if (f
== mReferenceFrame
|| f
->IsTransformed()) {
1579 *aOffset
= aFrame
->GetOffsetToCrossDoc(f
);
1580 MaybeApplyAdditionalOffset();
1587 *aOffset
= aFrame
->GetOffsetToCrossDoc(mReferenceFrame
);
1588 MaybeApplyAdditionalOffset();
1591 return mReferenceFrame
;
1594 // Sticky frames are active if their nearest scrollable frame is also active.
1595 static bool IsStickyFrameActive(nsDisplayListBuilder
* aBuilder
,
1597 MOZ_ASSERT(aFrame
->StyleDisplay()->mPosition
==
1598 StylePositionProperty::Sticky
);
1600 StickyScrollContainer
* stickyScrollContainer
=
1601 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame
);
1602 return stickyScrollContainer
&&
1603 stickyScrollContainer
->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1606 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame
* aFrame
,
1607 nsIFrame
** aParent
) {
1608 if (aFrame
== mReferenceFrame
) {
1612 if (!IsPaintingToWindow()) {
1614 *aParent
= nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame
);
1619 nsIFrame
* parent
= nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame
);
1625 if (aFrame
->StyleDisplay()->mPosition
== StylePositionProperty::Sticky
&&
1626 IsStickyFrameActive(this, aFrame
)) {
1630 if (aFrame
->IsTransformed()) {
1631 if (EffectCompositor::HasAnimationsForCompositor(
1632 aFrame
, DisplayItemType::TYPE_TRANSFORM
)) {
1637 LayoutFrameType parentType
= parent
->Type();
1638 if (parentType
== LayoutFrameType::Scroll
||
1639 parentType
== LayoutFrameType::ListControl
) {
1640 nsIScrollableFrame
* sf
= do_QueryFrame(parent
);
1641 if (sf
->GetScrolledFrame() == aFrame
) {
1642 MOZ_ASSERT(!aFrame
->IsTransformed());
1643 return sf
->IsMaybeAsynchronouslyScrolled();
1650 nsIFrame
* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1652 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1653 RootReferenceFrame(), aFrame
));
1654 nsIFrame
* cursor
= aFrame
;
1655 while (cursor
!= RootReferenceFrame()) {
1657 if (IsAnimatedGeometryRoot(cursor
, &next
)) {
1665 static nsRect
ApplyAllClipNonRoundedIntersection(
1666 const DisplayItemClipChain
* aClipChain
, const nsRect
& aRect
) {
1667 nsRect result
= aRect
;
1668 while (aClipChain
) {
1669 result
= aClipChain
->mClip
.ApplyNonRoundedIntersection(result
);
1670 aClipChain
= aClipChain
->mParent
;
1675 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame
* aFrame
) {
1676 if (!mWindowDraggingAllowed
|| !IsForPainting()) {
1680 const nsStyleUIReset
* styleUI
= aFrame
->StyleUIReset();
1681 if (styleUI
->mWindowDragging
== StyleWindowDragging::Default
) {
1682 // This frame has the default value and doesn't influence the window
1687 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame
;
1689 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1690 nsIFrame
* referenceFrame
=
1691 const_cast<nsIFrame
*>(FindReferenceFrameFor(aFrame
));
1693 if (IsInTransform()) {
1694 // Only support 2d rectilinear transforms. Transform support is needed for
1695 // the horizontal flip transform that's applied to the urlbar textbox in
1696 // RTL mode - it should be able to exclude itself from the draggable region.
1697 referenceFrameToRootReferenceFrame
=
1698 ViewAs
<LayoutDeviceToLayoutDeviceMatrix4x4
>(
1699 nsLayoutUtils::GetTransformToAncestor(RelativeTo
{referenceFrame
},
1700 RelativeTo
{mReferenceFrame
})
1702 Matrix referenceFrameToRootReferenceFrame2d
;
1703 if (!referenceFrameToRootReferenceFrame
.Is2D(
1704 &referenceFrameToRootReferenceFrame2d
) ||
1705 !referenceFrameToRootReferenceFrame2d
.IsRectilinear()) {
1709 MOZ_ASSERT(referenceFrame
== mReferenceFrame
,
1710 "referenceFrameToRootReferenceFrame needs to be adjusted");
1713 // We do some basic visibility checking on the frame's border box here.
1714 // We intersect it both with the current dirty rect and with the current
1715 // clip. Either one is just a conservative approximation on its own, but
1716 // their intersection luckily works well enough for our purposes, so that
1717 // we don't have to do full-blown visibility computations.
1718 // The most important case we need to handle is the scrolled-off tab:
1719 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1720 // should not be allowed to interfere with the window dragging region. Using
1721 // just the current DisplayItemClip is not enough to cover this case
1722 // completely because clips are reset while building stacking context
1723 // contents, so for example we'd fail to clip frames that have a clip path
1724 // applied to them. But the current dirty rect doesn't get reset in that
1725 // case, so we use it to make this case work.
1726 nsRect borderBox
= aFrame
->GetRectRelativeToSelf().Intersect(mVisibleRect
);
1727 borderBox
+= ToReferenceFrame(aFrame
);
1728 const DisplayItemClipChain
* clip
=
1729 ClipState().GetCurrentCombinedClipChain(this);
1730 borderBox
= ApplyAllClipNonRoundedIntersection(clip
, borderBox
);
1731 if (borderBox
.IsEmpty()) {
1735 LayoutDeviceRect devPixelBorderBox
= LayoutDevicePixel::FromAppUnits(
1736 borderBox
, aFrame
->PresContext()->AppUnitsPerDevPixel());
1738 LayoutDeviceRect transformedDevPixelBorderBox
=
1739 TransformBy(referenceFrameToRootReferenceFrame
, devPixelBorderBox
);
1740 transformedDevPixelBorderBox
.Round();
1741 LayoutDeviceIntRect transformedDevPixelBorderBoxInt
;
1743 if (!transformedDevPixelBorderBox
.ToIntRect(
1744 &transformedDevPixelBorderBoxInt
)) {
1748 LayoutDeviceIntRegion
& region
=
1749 styleUI
->mWindowDragging
== StyleWindowDragging::Drag
1750 ? mWindowDraggingRegion
1751 : mWindowNoDraggingRegion
;
1753 if (!IsRetainingDisplayList()) {
1754 region
.OrWith(transformedDevPixelBorderBoxInt
);
1758 gfx::IntRect
rect(transformedDevPixelBorderBoxInt
.ToUnknownRect());
1759 if (styleUI
->mWindowDragging
== StyleWindowDragging::Drag
) {
1760 mRetainedWindowDraggingRegion
.Add(aFrame
, rect
);
1762 mRetainedWindowNoDraggingRegion
.Add(aFrame
, rect
);
1766 LayoutDeviceIntRegion
nsDisplayListBuilder::GetWindowDraggingRegion() const {
1767 LayoutDeviceIntRegion result
;
1768 if (!IsRetainingDisplayList()) {
1769 result
.Sub(mWindowDraggingRegion
, mWindowNoDraggingRegion
);
1773 LayoutDeviceIntRegion dragRegion
=
1774 mRetainedWindowDraggingRegion
.ToLayoutDeviceIntRegion();
1776 LayoutDeviceIntRegion noDragRegion
=
1777 mRetainedWindowNoDraggingRegion
.ToLayoutDeviceIntRegion();
1779 result
.Sub(dragRegion
, noDragRegion
);
1783 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
) const {
1784 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes
);
1785 aSizes
.mLayoutRetainedDisplayListSize
+=
1786 aSizes
.mState
.mMallocSizeOf(mTransformPreserves3D
.get());
1789 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
) const {
1790 mPool
.AddSizeOfExcludingThis(aSizes
, Arena::ArenaKind::DisplayList
);
1793 MallocSizeOf mallocSizeOf
= aSizes
.mState
.mMallocSizeOf
;
1794 n
+= mDocumentWillChangeBudgets
.ShallowSizeOfExcludingThis(mallocSizeOf
);
1795 n
+= mFrameWillChangeBudgets
.ShallowSizeOfExcludingThis(mallocSizeOf
);
1796 n
+= mEffectsUpdates
.ShallowSizeOfExcludingThis(mallocSizeOf
);
1797 n
+= mRetainedWindowDraggingRegion
.SizeOfExcludingThis(mallocSizeOf
);
1798 n
+= mRetainedWindowNoDraggingRegion
.SizeOfExcludingThis(mallocSizeOf
);
1799 n
+= mRetainedWindowOpaqueRegion
.SizeOfExcludingThis(mallocSizeOf
);
1800 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1802 aSizes
.mLayoutRetainedDisplayListSize
+= n
;
1805 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
) const {
1806 for (nsDisplayItem
* item
: *this) {
1807 item
->AddSizeOfExcludingThis(aSizes
);
1808 if (RetainedDisplayList
* children
= item
->GetChildren()) {
1809 children
->AddSizeOfExcludingThis(aSizes
);
1815 n
+= mDAG
.mDirectPredecessorList
.ShallowSizeOfExcludingThis(
1816 aSizes
.mState
.mMallocSizeOf
);
1817 n
+= mDAG
.mNodesInfo
.ShallowSizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
1818 n
+= mOldItems
.ShallowSizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
1820 aSizes
.mLayoutRetainedDisplayListSize
+= n
;
1823 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1824 MallocSizeOf aMallocSizeOf
) const {
1826 n
+= mFrames
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
1827 for (const auto& frame
: mFrames
) {
1828 const UniquePtr
<WeakFrame
>& weakFrame
= frame
.mWeakFrame
;
1829 n
+= aMallocSizeOf(weakFrame
.get());
1831 n
+= mRects
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
1836 * Removes modified frames and rects from this WeakFrameRegion.
1838 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1839 MOZ_ASSERT(mFrames
.Length() == mRects
.Length());
1842 uint32_t length
= mFrames
.Length();
1844 while (i
< length
) {
1845 auto& wrapper
= mFrames
[i
];
1847 if (!wrapper
.mWeakFrame
->IsAlive() ||
1848 AnyContentAncestorModified(wrapper
.mWeakFrame
->GetFrame())) {
1849 // To avoid multiple O(n) shifts in the array, move the last element of
1850 // the array to the current position and decrease the array length.
1851 mFrameSet
.Remove(wrapper
.mFrame
);
1852 mFrames
[i
] = std::move(mFrames
[length
- 1]);
1853 mRects
[i
] = std::move(mRects
[length
- 1]);
1860 mFrames
.TruncateLength(length
);
1861 mRects
.TruncateLength(length
);
1864 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1865 mRetainedWindowDraggingRegion
.RemoveModifiedFramesAndRects();
1866 mRetainedWindowNoDraggingRegion
.RemoveModifiedFramesAndRects();
1867 mRetainedWindowOpaqueRegion
.RemoveModifiedFramesAndRects();
1870 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1871 mRetainedWindowDraggingRegion
.Clear();
1872 mRetainedWindowNoDraggingRegion
.Clear();
1873 mRetainedWindowOpaqueRegion
.Clear();
1876 const uint32_t gWillChangeAreaMultiplier
= 3;
1877 static uint32_t GetLayerizationCost(const nsSize
& aSize
) {
1878 // There's significant overhead for each layer created from Gecko
1879 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1880 // Therefore we set a minimum cost threshold of a 64x64 area.
1881 const int minBudgetCost
= 64 * 64;
1883 const uint32_t budgetCost
= std::max(
1884 minBudgetCost
, nsPresContext::AppUnitsToIntCSSPixels(aSize
.width
) *
1885 nsPresContext::AppUnitsToIntCSSPixels(aSize
.height
));
1890 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame
* aFrame
,
1891 const nsSize
& aSize
) {
1892 MOZ_ASSERT(IsForPainting());
1894 if (aFrame
->MayHaveWillChangeBudget()) {
1895 // The frame is already in the will-change budget.
1899 const nsPresContext
* presContext
= aFrame
->PresContext();
1900 const nsRect area
= presContext
->GetVisibleArea();
1901 const uint32_t budgetLimit
=
1902 nsPresContext::AppUnitsToIntCSSPixels(area
.width
) *
1903 nsPresContext::AppUnitsToIntCSSPixels(area
.height
);
1904 const uint32_t cost
= GetLayerizationCost(aSize
);
1906 DocumentWillChangeBudget
& documentBudget
=
1907 mDocumentWillChangeBudgets
.LookupOrInsert(presContext
);
1909 const bool onBudget
=
1910 (documentBudget
+ cost
) / gWillChangeAreaMultiplier
< budgetLimit
;
1913 documentBudget
+= cost
;
1914 mFrameWillChangeBudgets
.InsertOrUpdate(
1915 aFrame
, FrameWillChangeBudget(presContext
, cost
));
1916 aFrame
->SetMayHaveWillChangeBudget(true);
1922 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame
* aFrame
,
1923 const nsSize
& aSize
) {
1924 if (!IsForPainting()) {
1925 // If this nsDisplayListBuilder is not for painting, the layerization should
1926 // not matter. Do the simple thing and return false.
1930 const bool onBudget
= AddToWillChangeBudget(aFrame
, aSize
);
1935 auto* pc
= aFrame
->PresContext();
1936 auto* doc
= pc
->Document();
1937 if (!doc
->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget
)) {
1938 AutoTArray
<nsString
, 2> params
;
1939 params
.AppendElement()->AppendInt(gWillChangeAreaMultiplier
);
1941 nsRect area
= pc
->GetVisibleArea();
1942 uint32_t budgetLimit
= nsPresContext::AppUnitsToIntCSSPixels(area
.width
) *
1943 nsPresContext::AppUnitsToIntCSSPixels(area
.height
);
1944 params
.AppendElement()->AppendInt(budgetLimit
);
1946 doc
->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget
, false, params
);
1952 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame
* aFrame
) {
1953 MOZ_ASSERT(IsForPainting());
1955 if (!aFrame
->MayHaveWillChangeBudget()) {
1959 aFrame
->SetMayHaveWillChangeBudget(false);
1960 RemoveFromWillChangeBudgets(aFrame
);
1963 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame
* aFrame
) {
1964 if (auto entry
= mFrameWillChangeBudgets
.Lookup(aFrame
)) {
1965 const FrameWillChangeBudget
& frameBudget
= entry
.Data();
1967 auto documentBudget
=
1968 mDocumentWillChangeBudgets
.Lookup(frameBudget
.mPresContext
);
1970 if (documentBudget
) {
1971 *documentBudget
-= frameBudget
.mUsage
;
1978 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1979 mFrameWillChangeBudgets
.Clear();
1980 mDocumentWillChangeBudgets
.Clear();
1983 void nsDisplayListBuilder::EnterSVGEffectsContents(
1984 nsIFrame
* aEffectsFrame
, nsDisplayList
* aHoistedItemsStorage
) {
1985 MOZ_ASSERT(aHoistedItemsStorage
);
1986 if (mSVGEffectsFrames
.IsEmpty()) {
1987 MOZ_ASSERT(!mScrollInfoItemsForHoisting
);
1988 mScrollInfoItemsForHoisting
= aHoistedItemsStorage
;
1990 mSVGEffectsFrames
.AppendElement(aEffectsFrame
);
1993 void nsDisplayListBuilder::ExitSVGEffectsContents() {
1994 MOZ_ASSERT(!mSVGEffectsFrames
.IsEmpty());
1995 mSVGEffectsFrames
.RemoveLastElement();
1996 MOZ_ASSERT(mScrollInfoItemsForHoisting
);
1997 if (mSVGEffectsFrames
.IsEmpty()) {
1998 mScrollInfoItemsForHoisting
= nullptr;
2002 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
2004 * Note: if changing the conditions under which scroll info layers
2005 * are created, make a corresponding change to
2006 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
2008 for (nsIFrame
* frame
: mSVGEffectsFrames
) {
2009 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame
)) {
2016 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
2017 nsDisplayScrollInfoLayer
* aScrollInfoItem
) {
2018 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2019 MOZ_ASSERT(mScrollInfoItemsForHoisting
);
2020 mScrollInfoItemsForHoisting
->AppendToTop(aScrollInfoItem
);
2023 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2024 nsIFrame
* aFrame
, nsDisplayList
* aList
) {
2028 if (!BuildCompositorHitTestInfo()) {
2032 const CompositorHitTestInfo info
= aFrame
->GetCompositorHitTestInfo(this);
2033 if (info
!= CompositorHitTestInvisibleToHit
) {
2034 aList
->AppendNewToTop
<nsDisplayCompositorHitTestInfo
>(this, aFrame
);
2038 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem
* aItem
) {
2039 mReuseableItems
.Insert(aItem
);
2042 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem
* aItem
) {
2043 MOZ_ASSERT(aItem
->IsReusedItem());
2044 mReuseableItems
.Remove(aItem
);
2047 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2048 const size_t total
= mReuseableItems
.Count();
2051 for (auto* item
: mReuseableItems
) {
2052 if (item
->IsReusedItem()) {
2054 item
->SetReusable();
2056 item
->Destroy(this);
2060 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused
, total
);
2061 mReuseableItems
.Clear();
2064 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem
* aItem
) {
2065 const auto* previous
= mCurrentContainerASR
;
2066 const auto* asr
= aItem
->GetActiveScrolledRoot();
2067 mCurrentContainerASR
=
2068 ActiveScrolledRoot::PickAncestor(asr
, mCurrentContainerASR
);
2070 if (previous
!= mCurrentContainerASR
) {
2071 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous
,
2072 mCurrentContainerASR
);
2075 aItem
->SetReusedItem();
2078 void nsDisplayListSet::CopyTo(const nsDisplayListSet
& aDestination
) const {
2079 for (size_t i
= 0; i
< mLists
.size(); ++i
) {
2080 auto* from
= mLists
[i
];
2081 auto* to
= aDestination
.mLists
[i
];
2087 void nsDisplayListSet::MoveTo(const nsDisplayListSet
& aDestination
) const {
2088 aDestination
.BorderBackground()->AppendToTop(BorderBackground());
2089 aDestination
.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2090 aDestination
.Floats()->AppendToTop(Floats());
2091 aDestination
.Content()->AppendToTop(Content());
2092 aDestination
.PositionedDescendants()->AppendToTop(PositionedDescendants());
2093 aDestination
.Outlines()->AppendToTop(Outlines());
2096 nsRect
nsDisplayList::GetClippedBounds(nsDisplayListBuilder
* aBuilder
) const {
2098 for (nsDisplayItem
* i
: *this) {
2099 bounds
.UnionRect(bounds
, i
->GetClippedBounds(aBuilder
));
2104 nsRect
nsDisplayList::GetClippedBoundsWithRespectToASR(
2105 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
,
2106 nsRect
* aBuildingRect
) const {
2108 for (nsDisplayItem
* i
: *this) {
2109 nsRect r
= i
->GetClippedBounds(aBuilder
);
2110 if (aASR
!= i
->GetActiveScrolledRoot() && !r
.IsEmpty()) {
2111 if (Maybe
<nsRect
> clip
= i
->GetClipWithRespectToASR(aBuilder
, aASR
)) {
2115 if (aBuildingRect
) {
2116 aBuildingRect
->UnionRect(*aBuildingRect
, i
->GetBuildingRect());
2118 bounds
.UnionRect(bounds
, r
);
2123 nsRect
nsDisplayList::GetBuildingRect() const {
2125 for (nsDisplayItem
* i
: *this) {
2126 result
.UnionRect(result
, i
->GetBuildingRect());
2131 WindowRenderer
* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView
** aView
) {
2133 *aView
= RootReferenceFrame()->GetView();
2135 if (RootReferenceFrame() !=
2136 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2139 nsIWidget
* window
= RootReferenceFrame()->GetNearestWidget();
2141 return window
->GetWindowRenderer();
2146 WebRenderLayerManager
* nsDisplayListBuilder::GetWidgetLayerManager(
2148 WindowRenderer
* renderer
= GetWidgetWindowRenderer();
2150 return renderer
->AsWebRender();
2155 void nsDisplayList::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
2156 int32_t aAppUnitsPerDevPixel
) {
2157 FlattenedDisplayListIterator
iter(aBuilder
, this);
2158 while (iter
.HasNext()) {
2159 nsPaintedDisplayItem
* item
= iter
.GetNextItem()->AsPaintedDisplayItem();
2164 nsRect visible
= item
->GetClippedBounds(aBuilder
);
2165 visible
= visible
.Intersect(item
->GetPaintRect(aBuilder
, aCtx
));
2166 if (visible
.IsEmpty()) {
2170 DisplayItemClip currentClip
= item
->GetClip();
2171 if (currentClip
.HasClip()) {
2173 if (currentClip
.IsRectClippedByRoundedCorner(visible
)) {
2174 currentClip
.ApplyTo(aCtx
, aAppUnitsPerDevPixel
);
2176 currentClip
.ApplyRectTo(aCtx
, aAppUnitsPerDevPixel
);
2181 item
->Paint(aBuilder
, aCtx
);
2183 if (currentClip
.HasClip()) {
2190 * We paint by executing a layer manager transaction, constructing a
2191 * single layer representing the display list, and then making it the
2192 * root of the layer manager, drawing into the PaintedLayers.
2194 void nsDisplayList::PaintRoot(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
2196 Maybe
<double> aDisplayListBuildTime
) {
2197 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS
);
2199 RefPtr
<WebRenderLayerManager
> layerManager
;
2200 WindowRenderer
* renderer
= nullptr;
2201 bool widgetTransaction
= false;
2202 bool doBeginTransaction
= true;
2203 nsView
* view
= nullptr;
2204 if (aFlags
& PAINT_USE_WIDGET_LAYERS
) {
2205 renderer
= aBuilder
->GetWidgetWindowRenderer(&view
);
2207 // The fallback renderer doesn't retain any content, so it's
2208 // not meaningful to use it when drawing to an external context.
2209 if (aCtx
&& renderer
->AsFallback()) {
2210 MOZ_ASSERT(!(aFlags
& PAINT_EXISTING_TRANSACTION
));
2213 layerManager
= renderer
->AsWebRender();
2214 doBeginTransaction
= !(aFlags
& PAINT_EXISTING_TRANSACTION
);
2215 widgetTransaction
= true;
2220 nsIFrame
* frame
= aBuilder
->RootReferenceFrame();
2221 nsPresContext
* presContext
= frame
->PresContext();
2222 PresShell
* presShell
= presContext
->PresShell();
2223 Document
* document
= presShell
->GetDocument();
2227 MOZ_ASSERT(!layerManager
|| !layerManager
->GetTarget());
2230 // For layers-free mode, we check the invalidation state bits in the
2231 // EndTransaction. So we clear the invalidation state bits after
2233 if (widgetTransaction
||
2234 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2235 // but they still need the invalidation state bits cleared in order for
2236 // invalidation for CSS/SMIL animation to work properly.
2237 (document
&& document
->IsBeingUsedAsImage())) {
2238 DL_LOGD("Clearing invalidation state bits");
2239 frame
->ClearInvalidationStateBits();
2245 NS_WARNING("Nowhere to paint into");
2248 bool prevIsCompositingCheap
= aBuilder
->SetIsCompositingCheap(false);
2249 Paint(aBuilder
, aCtx
, presContext
->AppUnitsPerDevPixel());
2251 aBuilder
->SetIsCompositingCheap(prevIsCompositingCheap
);
2255 if (renderer
->GetBackendType() == LayersBackend::LAYERS_WR
) {
2256 MOZ_ASSERT(layerManager
);
2257 if (doBeginTransaction
) {
2259 if (!layerManager
->BeginTransactionWithTarget(aCtx
, nsCString())) {
2263 if (!layerManager
->BeginTransaction(nsCString())) {
2269 bool prevIsCompositingCheap
= aBuilder
->SetIsCompositingCheap(true);
2270 layerManager
->SetTransactionIdAllocator(presContext
->RefreshDriver());
2273 if (aFlags
& PAINT_IDENTICAL_DISPLAY_LIST
) {
2275 sent
= layerManager
->EndEmptyTransaction();
2279 auto* wrManager
= static_cast<WebRenderLayerManager
*>(layerManager
.get());
2281 nsIDocShell
* docShell
= presContext
->GetDocShell();
2282 WrFiltersHolder wrFilters
;
2283 gfx::Matrix5x4
* colorMatrix
=
2284 nsDocShell::Cast(docShell
)->GetColorMatrix();
2286 wrFilters
.filters
.AppendElement(
2287 wr::FilterOp::ColorMatrix(colorMatrix
->components
));
2290 wrManager
->EndTransactionWithoutLayer(this, aBuilder
,
2291 std::move(wrFilters
), nullptr,
2292 aDisplayListBuildTime
.valueOr(0.0));
2295 aBuilder
->SetIsCompositingCheap(prevIsCompositingCheap
);
2296 if (presContext
->RefreshDriver()->HasScheduleFlush()) {
2297 presContext
->NotifyInvalidation(layerManager
->GetLastTransactionId(),
2304 FallbackRenderer
* fallback
= renderer
->AsFallback();
2305 MOZ_ASSERT(fallback
);
2307 if (doBeginTransaction
) {
2309 if (!fallback
->BeginTransaction()) {
2314 bool temp
= aBuilder
->SetIsCompositingCheap(renderer
->IsCompositingCheap());
2315 fallback
->EndTransactionWithList(aBuilder
, this,
2316 presContext
->AppUnitsPerDevPixel(),
2317 WindowRenderer::END_DEFAULT
);
2319 aBuilder
->SetIsCompositingCheap(temp
);
2322 void nsDisplayList::DeleteAll(nsDisplayListBuilder
* aBuilder
) {
2323 for (auto* item
: TakeItems()) {
2324 item
->Destroy(aBuilder
);
2328 static bool IsFrameReceivingPointerEvents(nsIFrame
* aFrame
) {
2329 return aFrame
->Style()->PointerEvents() != StylePointerEvents::None
;
2332 // A list of frames, and their z depth. Used for sorting
2333 // the results of hit testing.
2334 struct FramesWithDepth
{
2335 explicit FramesWithDepth(float aDepth
) : mDepth(aDepth
) {}
2337 bool operator<(const FramesWithDepth
& aOther
) const {
2338 if (!FuzzyEqual(mDepth
, aOther
.mDepth
, 0.1f
)) {
2339 // We want to sort so that the shallowest item (highest depth value) is
2341 return mDepth
> aOther
.mDepth
;
2345 bool operator==(const FramesWithDepth
& aOther
) const {
2346 return this == &aOther
;
2350 nsTArray
<nsIFrame
*> mFrames
;
2353 // Sort the frames by depth and then moves all the contained frames to the
2355 static void FlushFramesArray(nsTArray
<FramesWithDepth
>& aSource
,
2356 nsTArray
<nsIFrame
*>* aDest
) {
2357 if (aSource
.IsEmpty()) {
2360 aSource
.StableSort();
2361 uint32_t length
= aSource
.Length();
2362 for (uint32_t i
= 0; i
< length
; i
++) {
2363 aDest
->AppendElements(std::move(aSource
[i
].mFrames
));
2368 void nsDisplayList::HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
2369 nsDisplayItem::HitTestState
* aState
,
2370 nsTArray
<nsIFrame
*>* aOutFrames
) const {
2371 nsDisplayItem
* item
;
2373 if (aState
->mInPreserves3D
) {
2374 // Collect leaves of the current 3D rendering context.
2375 for (nsDisplayItem
* item
: *this) {
2376 auto itemType
= item
->GetType();
2377 if (itemType
!= DisplayItemType::TYPE_TRANSFORM
||
2378 !static_cast<nsDisplayTransform
*>(item
)->IsLeafOf3DContext()) {
2379 item
->HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
2381 // One of leaves in the current 3D rendering context.
2382 aState
->mItemBuffer
.AppendElement(item
);
2388 int32_t itemBufferStart
= aState
->mItemBuffer
.Length();
2389 for (nsDisplayItem
* item
: *this) {
2390 aState
->mItemBuffer
.AppendElement(item
);
2393 AutoTArray
<FramesWithDepth
, 16> temp
;
2394 for (int32_t i
= aState
->mItemBuffer
.Length() - 1; i
>= itemBufferStart
;
2396 // Pop element off the end of the buffer. We want to shorten the buffer
2397 // so that recursive calls to HitTest have more buffer space.
2398 item
= aState
->mItemBuffer
[i
];
2399 aState
->mItemBuffer
.SetLength(i
);
2402 nsRect r
= item
->GetBounds(aBuilder
, &snap
).Intersect(aRect
);
2403 auto itemType
= item
->GetType();
2404 bool same3DContext
=
2405 (itemType
== DisplayItemType::TYPE_TRANSFORM
&&
2406 static_cast<nsDisplayTransform
*>(item
)->IsParticipating3DContext()) ||
2407 (itemType
== DisplayItemType::TYPE_PERSPECTIVE
&&
2408 item
->Frame()->Extend3DContext());
2409 if (same3DContext
&&
2410 (itemType
!= DisplayItemType::TYPE_TRANSFORM
||
2411 !static_cast<nsDisplayTransform
*>(item
)->IsLeafOf3DContext())) {
2412 if (!item
->GetClip().MayIntersect(aRect
)) {
2415 AutoTArray
<nsIFrame
*, 1> neverUsed
;
2416 // Start gathering leaves of the 3D rendering context, and
2417 // append leaves at the end of mItemBuffer. Leaves are
2418 // processed at following iterations.
2419 aState
->mInPreserves3D
= true;
2420 item
->HitTest(aBuilder
, aRect
, aState
, &neverUsed
);
2421 aState
->mInPreserves3D
= false;
2422 i
= aState
->mItemBuffer
.Length();
2425 if (same3DContext
|| item
->GetClip().MayIntersect(r
)) {
2426 AutoTArray
<nsIFrame
*, 16> outFrames
;
2427 item
->HitTest(aBuilder
, aRect
, aState
, &outFrames
);
2429 // For 3d transforms with preserve-3d we add hit frames into the temp list
2430 // so we can sort them later, otherwise we add them directly to the output
2432 nsTArray
<nsIFrame
*>* writeFrames
= aOutFrames
;
2433 if (item
->GetType() == DisplayItemType::TYPE_TRANSFORM
&&
2434 static_cast<nsDisplayTransform
*>(item
)->IsLeafOf3DContext()) {
2435 if (outFrames
.Length()) {
2436 nsDisplayTransform
* transform
=
2437 static_cast<nsDisplayTransform
*>(item
);
2438 nsPoint point
= aRect
.TopLeft();
2439 // A 1x1 rect means a point, otherwise use the center of the rect
2440 if (aRect
.width
!= 1 || aRect
.height
!= 1) {
2441 point
= aRect
.Center();
2444 FramesWithDepth(transform
->GetHitDepthAtPoint(aBuilder
, point
)));
2445 writeFrames
= &temp
[temp
.Length() - 1].mFrames
;
2448 // We may have just finished a run of consecutive preserve-3d
2449 // transforms, so flush these into the destination array before
2450 // processing our frame list.
2451 FlushFramesArray(temp
, aOutFrames
);
2454 for (uint32_t j
= 0; j
< outFrames
.Length(); j
++) {
2455 nsIFrame
* f
= outFrames
.ElementAt(j
);
2456 // Filter out some frames depending on the type of hittest
2457 // we are doing. For visibility tests, pass through all frames.
2458 // For pointer tests, only pass through frames that are styled
2459 // to receive pointer events.
2460 if (aBuilder
->HitTestIsForVisibility() ||
2461 IsFrameReceivingPointerEvents(f
)) {
2462 writeFrames
->AppendElement(f
);
2466 if (aBuilder
->HitTestIsForVisibility()) {
2467 aState
->mHitOccludingItem
= [&] {
2468 if (aState
->mHitOccludingItem
) {
2469 // We already hit something before.
2472 if (aState
->mCurrentOpacity
== 1.0f
&&
2473 item
->GetOpaqueRegion(aBuilder
, &snap
).Contains(aRect
)) {
2474 // An opaque item always occludes everything. Note that we need to
2475 // check wrapping opacity and such as well.
2478 float threshold
= aBuilder
->VisibilityThreshold();
2479 if (threshold
== 1.0f
) {
2482 float itemOpacity
= [&] {
2483 switch (item
->GetType()) {
2484 case DisplayItemType::TYPE_OPACITY
:
2485 return static_cast<nsDisplayOpacity
*>(item
)->GetOpacity();
2486 case DisplayItemType::TYPE_BACKGROUND_COLOR
:
2487 return static_cast<nsDisplayBackgroundColor
*>(item
)
2490 // Be conservative and assume it won't occlude other items.
2494 return itemOpacity
* aState
->mCurrentOpacity
>= threshold
;
2497 if (aState
->mHitOccludingItem
) {
2498 // We're exiting early, so pop the remaining items off the buffer.
2499 aState
->mItemBuffer
.TruncateLength(itemBufferStart
);
2505 // Clear any remaining preserve-3d transforms.
2506 FlushFramesArray(temp
, aOutFrames
);
2507 NS_ASSERTION(aState
->mItemBuffer
.Length() == uint32_t(itemBufferStart
),
2508 "How did we forget to pop some elements?");
2511 static nsIContent
* FindContentInDocument(nsDisplayItem
* aItem
, Document
* aDoc
) {
2512 nsIFrame
* f
= aItem
->Frame();
2514 nsPresContext
* pc
= f
->PresContext();
2515 if (pc
->Document() == aDoc
) {
2516 return f
->GetContent();
2518 f
= nsLayoutUtils::GetCrossDocParentFrameInProcess(
2519 pc
->PresShell()->GetRootFrame());
2525 nsDisplayItem
* item
;
2528 explicit ZSortItem(nsDisplayItem
* aItem
)
2529 : item(aItem
), zIndex(aItem
->ZIndex()) {}
2531 operator nsDisplayItem
*() { return item
; }
2534 struct ZOrderComparator
{
2535 bool operator()(const ZSortItem
& aLeft
, const ZSortItem
& aRight
) const {
2536 // Note that we can't just take the difference of the two
2537 // z-indices here, because that might overflow a 32-bit int.
2538 return aLeft
.zIndex
< aRight
.zIndex
;
2542 void nsDisplayList::SortByZOrder() { Sort
<ZSortItem
>(ZOrderComparator()); }
2544 struct ContentComparator
{
2545 nsIContent
* mCommonAncestor
;
2547 explicit ContentComparator(nsIContent
* aCommonAncestor
)
2548 : mCommonAncestor(aCommonAncestor
) {}
2550 bool operator()(nsDisplayItem
* aLeft
, nsDisplayItem
* aRight
) const {
2551 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2552 // subdocument of commonAncestor, because display items for subdocuments
2553 // have been mixed into the same list. Ensure that we're looking at content
2554 // in commonAncestor's document.
2555 Document
* commonAncestorDoc
= mCommonAncestor
->OwnerDoc();
2556 nsIContent
* content1
= FindContentInDocument(aLeft
, commonAncestorDoc
);
2557 nsIContent
* content2
= FindContentInDocument(aRight
, commonAncestorDoc
);
2558 if (!content1
|| !content2
) {
2559 NS_ERROR("Document trees are mixed up!");
2560 // Something weird going on
2563 return nsLayoutUtils::CompareTreePosition(content1
, content2
,
2564 mCommonAncestor
) < 0;
2568 void nsDisplayList::SortByContentOrder(nsIContent
* aCommonAncestor
) {
2569 Sort
<nsDisplayItem
*>(ContentComparator(aCommonAncestor
));
2572 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2573 static_assert(sizeof(nsDisplayItem
) <= 176, "nsDisplayItem has grown");
2576 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
)
2577 : nsDisplayItem(aBuilder
, aFrame
, aBuilder
->CurrentActiveScrolledRoot()) {}
2579 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
2580 const ActiveScrolledRoot
* aActiveScrolledRoot
)
2581 : mFrame(aFrame
), mActiveScrolledRoot(aActiveScrolledRoot
) {
2582 MOZ_COUNT_CTOR(nsDisplayItem
);
2584 if (aBuilder
->IsRetainingDisplayList()) {
2585 mFrame
->AddDisplayItem(this);
2588 aBuilder
->FindReferenceFrameFor(aFrame
, &mToReferenceFrame
);
2590 aBuilder
->GetVisibleRect().width
>= 0 || !aBuilder
->IsForPainting(),
2591 "visible rect not set");
2593 mClipChain
= aBuilder
->ClipState().GetCurrentCombinedClipChain(aBuilder
);
2595 // The visible rect is for mCurrentFrame, so we have to use
2596 // mCurrentOffsetToReferenceFrame
2597 nsRect visible
= aBuilder
->GetVisibleRect() +
2598 aBuilder
->GetCurrentFrameOffsetToReferenceFrame();
2599 SetBuildingRect(visible
);
2601 const nsStyleDisplay
* disp
= mFrame
->StyleDisplay();
2602 if (mFrame
->BackfaceIsHidden(disp
)) {
2603 mItemFlags
+= ItemFlag::BackfaceHidden
;
2605 if (mFrame
->Combines3DTransformWithAncestors()) {
2606 mItemFlags
+= ItemFlag::Combines3DTransformWithAncestors
;
2610 void nsDisplayItem::SetDeletedFrame() { mItemFlags
+= ItemFlag::DeletedFrame
; }
2612 bool nsDisplayItem::HasDeletedFrame() const {
2613 bool retval
= mItemFlags
.contains(ItemFlag::DeletedFrame
) ||
2614 (GetType() == DisplayItemType::TYPE_REMOTE
&&
2615 !static_cast<const nsDisplayRemote
*>(this)->GetFrameLoader());
2616 MOZ_ASSERT(retval
|| mFrame
);
2621 bool nsDisplayItem::ForceActiveLayers() {
2622 return StaticPrefs::layers_force_active();
2625 int32_t nsDisplayItem::ZIndex() const { return mFrame
->ZIndex().valueOr(0); }
2627 void nsDisplayItem::SetClipChain(const DisplayItemClipChain
* aClipChain
,
2629 mClipChain
= aClipChain
;
2632 Maybe
<nsRect
> nsDisplayItem::GetClipWithRespectToASR(
2633 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
) const {
2634 if (const DisplayItemClip
* clip
=
2635 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR
)) {
2636 return Some(clip
->GetClipRect());
2639 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
2644 const DisplayItemClip
& nsDisplayItem::GetClip() const {
2645 const DisplayItemClip
* clip
=
2646 DisplayItemClipChain::ClipForASR(mClipChain
, mActiveScrolledRoot
);
2647 return clip
? *clip
: DisplayItemClip::NoClip();
2650 void nsDisplayItem::IntersectClip(nsDisplayListBuilder
* aBuilder
,
2651 const DisplayItemClipChain
* aOther
,
2653 if (!aOther
|| mClipChain
== aOther
) {
2657 // aOther might be a reference to a clip on the stack. We need to make sure
2658 // that CreateClipChainIntersection will allocate the actual intersected
2659 // clip in the builder's arena, so for the mClipChain == nullptr case,
2660 // we supply nullptr as the common ancestor so that
2661 // CreateClipChainIntersection clones the whole chain.
2662 const DisplayItemClipChain
* ancestorClip
=
2663 mClipChain
? FindCommonAncestorClipForIntersection(mClipChain
, aOther
)
2667 aBuilder
->CreateClipChainIntersection(ancestorClip
, mClipChain
, aOther
),
2671 nsRect
nsDisplayItem::GetClippedBounds(nsDisplayListBuilder
* aBuilder
) const {
2673 nsRect r
= GetBounds(aBuilder
, &snap
);
2674 return GetClip().ApplyNonRoundedIntersection(r
);
2677 nsDisplayContainer::nsDisplayContainer(
2678 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
2679 const ActiveScrolledRoot
* aActiveScrolledRoot
, nsDisplayList
* aList
)
2680 : nsDisplayItem(aBuilder
, aFrame
, aActiveScrolledRoot
),
2681 mChildren(aBuilder
) {
2682 MOZ_COUNT_CTOR(nsDisplayContainer
);
2683 mChildren
.AppendToTop(aList
);
2684 UpdateBounds(aBuilder
);
2686 // Clear and store the clip chain set by nsDisplayItem constructor.
2687 nsDisplayItem::SetClipChain(nullptr, true);
2690 nsRect
nsDisplayItem::GetPaintRect(nsDisplayListBuilder
* aBuilder
,
2693 nsRect result
= GetBounds(aBuilder
, &dummy
);
2695 result
.IntersectRect(result
,
2696 nsLayoutUtils::RoundGfxRectToAppRect(
2697 aCtx
->GetClipExtents(),
2698 mFrame
->PresContext()->AppUnitsPerDevPixel()));
2703 bool nsDisplayContainer::CreateWebRenderCommands(
2704 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
2705 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
2706 nsDisplayListBuilder
* aDisplayListBuilder
) {
2707 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2708 GetChildren(), this, aDisplayListBuilder
, aSc
, aBuilder
, aResources
,
2713 nsRect
nsDisplayContainer::GetBounds(nsDisplayListBuilder
* aBuilder
,
2714 bool* aSnap
) const {
2719 nsRect
nsDisplayContainer::GetComponentAlphaBounds(
2720 nsDisplayListBuilder
* aBuilder
) const {
2721 return mChildren
.GetComponentAlphaBounds(aBuilder
);
2724 static nsRegion
GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
2725 nsDisplayList
* aList
,
2726 const nsRect
& aListBounds
) {
2727 return aList
->GetOpaqueRegion(aBuilder
);
2730 nsRegion
nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
2731 bool* aSnap
) const {
2732 return ::mozilla::GetOpaqueRegion(aBuilder
, GetChildren(),
2733 GetBounds(aBuilder
, aSnap
));
2736 Maybe
<nsRect
> nsDisplayContainer::GetClipWithRespectToASR(
2737 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
) const {
2738 // Our children should have finite bounds with respect to |aASR|.
2739 if (aASR
== mActiveScrolledRoot
) {
2740 return Some(mBounds
);
2743 return Some(mChildren
.GetClippedBoundsWithRespectToASR(aBuilder
, aASR
));
2746 void nsDisplayContainer::HitTest(nsDisplayListBuilder
* aBuilder
,
2747 const nsRect
& aRect
, HitTestState
* aState
,
2748 nsTArray
<nsIFrame
*>* aOutFrames
) {
2749 mChildren
.HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
2752 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder
* aBuilder
) {
2753 // Container item bounds are expected to be clipped.
2755 mChildren
.GetClippedBoundsWithRespectToASR(aBuilder
, mActiveScrolledRoot
);
2758 nsRect
nsDisplaySolidColor::GetBounds(nsDisplayListBuilder
* aBuilder
,
2759 bool* aSnap
) const {
2764 void nsDisplaySolidColor::Paint(nsDisplayListBuilder
* aBuilder
,
2766 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
2767 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
2768 Rect rect
= NSRectToSnappedRect(GetPaintRect(aBuilder
, aCtx
),
2769 appUnitsPerDevPixel
, *drawTarget
);
2770 drawTarget
->FillRect(rect
, ColorPattern(ToDeviceColor(mColor
)));
2773 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream
& aStream
) {
2774 aStream
<< " (rgba " << (int)NS_GET_R(mColor
) << "," << (int)NS_GET_G(mColor
)
2775 << "," << (int)NS_GET_B(mColor
) << "," << (int)NS_GET_A(mColor
)
2779 bool nsDisplaySolidColor::CreateWebRenderCommands(
2780 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
2781 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
2782 nsDisplayListBuilder
* aDisplayListBuilder
) {
2783 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
2784 mBounds
, mFrame
->PresContext()->AppUnitsPerDevPixel());
2785 wr::LayoutRect r
= wr::ToLayoutRect(bounds
);
2786 aBuilder
.PushRect(r
, r
, !BackfaceIsHidden(), false, mIsCheckerboardBackground
,
2787 wr::ToColorF(ToDeviceColor(mColor
)));
2792 nsRect
nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder
* aBuilder
,
2793 bool* aSnap
) const {
2795 return mRegion
.GetBounds();
2798 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder
* aBuilder
,
2800 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
2801 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
2802 ColorPattern
color(ToDeviceColor(mColor
));
2803 for (auto iter
= mRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
2805 NSRectToSnappedRect(iter
.Get(), appUnitsPerDevPixel
, *drawTarget
);
2806 drawTarget
->FillRect(rect
, color
);
2810 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream
& aStream
) {
2811 aStream
<< " (rgba " << int(mColor
.r
* 255) << "," << int(mColor
.g
* 255)
2812 << "," << int(mColor
.b
* 255) << "," << mColor
.a
<< ")";
2815 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2816 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
2817 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
2818 nsDisplayListBuilder
* aDisplayListBuilder
) {
2819 for (auto iter
= mRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
2820 nsRect rect
= iter
.Get();
2821 LayoutDeviceRect layerRects
= LayoutDeviceRect::FromAppUnits(
2822 rect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
2823 wr::LayoutRect r
= wr::ToLayoutRect(layerRects
);
2824 aBuilder
.PushRect(r
, r
, !BackfaceIsHidden(), false, false,
2825 wr::ToColorF(ToDeviceColor(mColor
)));
2831 static void RegisterThemeGeometry(nsDisplayListBuilder
* aBuilder
,
2832 nsDisplayItem
* aItem
, nsIFrame
* aFrame
,
2833 nsITheme::ThemeGeometryType aType
) {
2834 if (aBuilder
->IsInChromeDocumentOrPopup()) {
2835 nsIFrame
* displayRoot
= nsLayoutUtils::GetDisplayRootFrame(aFrame
);
2836 bool preservesAxisAlignedRectangles
= false;
2837 nsRect borderBox
= nsLayoutUtils::TransformFrameRectToAncestor(
2838 aFrame
, aFrame
->GetRectRelativeToSelf(), displayRoot
,
2839 &preservesAxisAlignedRectangles
);
2840 if (preservesAxisAlignedRectangles
) {
2841 aBuilder
->RegisterThemeGeometry(
2843 LayoutDeviceIntRect::FromUnknownRect(borderBox
.ToNearestPixels(
2844 aFrame
->PresContext()->AppUnitsPerDevPixel())));
2849 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2850 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
2851 static Maybe
<nsRect
> GetViewportRectRelativeToReferenceFrame(
2852 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
) {
2853 nsIFrame
* rootFrame
= aFrame
->PresShell()->GetRootFrame();
2854 nsRect rootRect
= rootFrame
->GetRectRelativeToSelf();
2855 if (nsLayoutUtils::TransformRect(rootFrame
, aFrame
, rootRect
) ==
2856 nsLayoutUtils::TRANSFORM_SUCCEEDED
) {
2857 return Some(rootRect
+ aBuilder
->ToReferenceFrame(aFrame
));
2862 /* static */ nsDisplayBackgroundImage::InitData
2863 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder
* aBuilder
,
2864 nsIFrame
* aFrame
, uint16_t aLayer
,
2865 const nsRect
& aBackgroundRect
,
2866 const ComputedStyle
* aBackgroundStyle
) {
2867 nsPresContext
* presContext
= aFrame
->PresContext();
2868 uint32_t flags
= aBuilder
->GetBackgroundPaintFlags();
2869 const nsStyleImageLayers::Layer
& layer
=
2870 aBackgroundStyle
->StyleBackground()->mImage
.mLayers
[aLayer
];
2872 bool isTransformedFixed
;
2873 nsBackgroundLayerState state
= nsCSSRendering::PrepareImageLayer(
2874 presContext
, aFrame
, flags
, aBackgroundRect
, aBackgroundRect
, layer
,
2875 &isTransformedFixed
);
2877 // background-attachment:fixed is treated as background-attachment:scroll
2878 // if it's affected by a transform.
2879 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2880 bool shouldTreatAsFixed
=
2881 layer
.mAttachment
== StyleImageLayerAttachment::Fixed
&&
2882 !isTransformedFixed
;
2884 bool shouldFixToViewport
= shouldTreatAsFixed
&& !layer
.mImage
.IsNone();
2885 bool isRasterImage
= state
.mImageRenderer
.IsRasterImage();
2886 nsCOMPtr
<imgIContainer
> image
;
2887 if (isRasterImage
) {
2888 image
= state
.mImageRenderer
.GetImage();
2890 return InitData
{aBuilder
, aBackgroundStyle
, image
,
2891 aBackgroundRect
, state
.mFillArea
, state
.mDestArea
,
2892 aLayer
, isRasterImage
, shouldFixToViewport
};
2895 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2896 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, const InitData
& aInitData
,
2897 nsIFrame
* aFrameForBounds
)
2898 : nsPaintedDisplayItem(aBuilder
, aFrame
),
2899 mBackgroundStyle(aInitData
.backgroundStyle
),
2900 mImage(aInitData
.image
),
2901 mDependentFrame(nullptr),
2902 mBackgroundRect(aInitData
.backgroundRect
),
2903 mFillRect(aInitData
.fillArea
),
2904 mDestRect(aInitData
.destArea
),
2905 mLayer(aInitData
.layer
),
2906 mIsRasterImage(aInitData
.isRasterImage
),
2907 mShouldFixToViewport(aInitData
.shouldFixToViewport
) {
2908 MOZ_COUNT_CTOR(nsDisplayBackgroundImage
);
2910 if (mBackgroundStyle
&& mBackgroundStyle
!= mFrame
->Style()) {
2911 // If this changes, then you also need to adjust css::ImageLoader to
2912 // invalidate mFrame as needed.
2913 MOZ_ASSERT(mFrame
->IsCanvasFrame() || mFrame
->IsTablePart());
2917 mBounds
= GetBoundsInternal(aInitData
.builder
, aFrameForBounds
);
2918 if (mShouldFixToViewport
) {
2919 // Expand the item's visible rect to cover the entire bounds, limited to the
2920 // viewport rect. This is necessary because the background's clip can move
2922 if (Maybe
<nsRect
> viewportRect
= GetViewportRectRelativeToReferenceFrame(
2923 aInitData
.builder
, mFrame
)) {
2924 SetBuildingRect(mBounds
.Intersect(*viewportRect
));
2929 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2930 MOZ_COUNT_DTOR(nsDisplayBackgroundImage
);
2931 if (mDependentFrame
) {
2932 mDependentFrame
->RemoveDisplayItem(this);
2936 static void SetBackgroundClipRegion(
2937 DisplayListClipState::AutoSaveRestore
& aClipState
, nsIFrame
* aFrame
,
2938 const nsStyleImageLayers::Layer
& aLayer
, const nsRect
& aBackgroundRect
,
2939 bool aWillPaintBorder
) {
2940 nsCSSRendering::ImageLayerClipState clip
;
2941 nsCSSRendering::GetImageLayerClip(
2942 aLayer
, aFrame
, *aFrame
->StyleBorder(), aBackgroundRect
, aBackgroundRect
,
2943 aWillPaintBorder
, aFrame
->PresContext()->AppUnitsPerDevPixel(), &clip
);
2945 if (clip
.mHasAdditionalBGClipArea
) {
2946 aClipState
.ClipContentDescendants(
2947 clip
.mAdditionalBGClipArea
, clip
.mBGClipArea
,
2948 clip
.mHasRoundedCorners
? clip
.mRadii
: nullptr);
2950 aClipState
.ClipContentDescendants(
2951 clip
.mBGClipArea
, clip
.mHasRoundedCorners
? clip
.mRadii
: nullptr);
2956 * This is used for the find bar highlighter overlay. It's only accessible
2957 * through the AnonymousContent API, so it's not exposed to general web pages.
2959 static bool SpecialCutoutRegionCase(nsDisplayListBuilder
* aBuilder
,
2961 const nsRect
& aBackgroundRect
,
2962 nsDisplayList
* aList
, nscolor aColor
) {
2963 nsIContent
* content
= aFrame
->GetContent();
2968 void* cutoutRegion
= content
->GetProperty(nsGkAtoms::cutoutregion
);
2969 if (!cutoutRegion
) {
2973 if (NS_GET_A(aColor
) == 0) {
2978 region
.Sub(aBackgroundRect
, *static_cast<nsRegion
*>(cutoutRegion
));
2979 region
.MoveBy(aBuilder
->ToReferenceFrame(aFrame
));
2980 aList
->AppendNewToTop
<nsDisplaySolidColorRegion
>(aBuilder
, aFrame
, region
,
2986 enum class TableType
: uint8_t {
2997 enum class TableTypeBits
: uint8_t { Count
= 3 };
2999 static_assert(static_cast<uint8_t>(TableType::MAX
) <
3000 (1 << (static_cast<uint8_t>(TableTypeBits::Count
) + 1)),
3001 "TableType cannot fit with TableTypeBits::Count");
3002 TableType
GetTableTypeFromFrame(nsIFrame
* aFrame
);
3004 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex
,
3005 const TableType aType
) {
3006 const uint32_t key
= (aIndex
<< static_cast<uint8_t>(TableTypeBits::Count
)) |
3007 static_cast<uint8_t>(aType
);
3009 return static_cast<uint16_t>(key
);
3012 static nsDisplayBackgroundImage
* CreateBackgroundImage(
3013 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
3014 const nsDisplayBackgroundImage::InitData
& aBgData
) {
3015 const auto index
= aBgData
.layer
;
3017 if (aSecondaryFrame
) {
3018 const auto tableType
= GetTableTypeFromFrame(aFrame
);
3019 const uint16_t tableItemIndex
= CalculateTablePerFrameKey(index
, tableType
);
3021 return MakeDisplayItemWithIndex
<nsDisplayTableBackgroundImage
>(
3022 aBuilder
, aSecondaryFrame
, tableItemIndex
, aBgData
, aFrame
);
3025 return MakeDisplayItemWithIndex
<nsDisplayBackgroundImage
>(aBuilder
, aFrame
,
3029 static nsDisplayThemedBackground
* CreateThemedBackground(
3030 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
3031 const nsRect
& aBgRect
) {
3032 if (aSecondaryFrame
) {
3033 const uint16_t index
= static_cast<uint16_t>(GetTableTypeFromFrame(aFrame
));
3034 return MakeDisplayItemWithIndex
<nsDisplayTableThemedBackground
>(
3035 aBuilder
, aSecondaryFrame
, index
, aBgRect
, aFrame
);
3038 return MakeDisplayItem
<nsDisplayThemedBackground
>(aBuilder
, aFrame
, aBgRect
);
3041 static nsDisplayBackgroundColor
* CreateBackgroundColor(
3042 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
3043 nsRect
& aBgRect
, const ComputedStyle
* aBgSC
, nscolor aColor
) {
3044 if (aSecondaryFrame
) {
3045 const uint16_t index
= static_cast<uint16_t>(GetTableTypeFromFrame(aFrame
));
3046 return MakeDisplayItemWithIndex
<nsDisplayTableBackgroundColor
>(
3047 aBuilder
, aSecondaryFrame
, index
, aBgRect
, aBgSC
, aColor
, aFrame
);
3050 return MakeDisplayItem
<nsDisplayBackgroundColor
>(aBuilder
, aFrame
, aBgRect
,
3054 static void DealWithWindowsAppearanceHacks(nsIFrame
* aFrame
,
3055 nsDisplayListBuilder
* aBuilder
) {
3056 const auto& disp
= *aFrame
->StyleDisplay();
3058 // We use default appearance rather than effective appearance because we want
3059 // to handle when titlebar buttons that have appearance: none.
3060 const auto defaultAppearance
= disp
.mDefaultAppearance
;
3061 if (MOZ_LIKELY(defaultAppearance
== StyleAppearance::None
)) {
3065 if (auto type
= disp
.GetWindowButtonType()) {
3066 if (auto* widget
= aFrame
->GetNearestWidget()) {
3067 auto rect
= LayoutDevicePixel::FromAppUnitsToNearest(
3068 nsRect(aBuilder
->ToReferenceFrame(aFrame
), aFrame
->GetSize()),
3069 aFrame
->PresContext()->AppUnitsPerDevPixel());
3070 widget
->SetWindowButtonRect(*type
, rect
);
3076 AppendedBackgroundType
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3077 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
3078 const nsRect
& aBackgroundRect
, nsDisplayList
* aList
,
3079 bool aAllowWillPaintBorderOptimization
, const nsRect
& aBackgroundOriginRect
,
3080 nsIFrame
* aSecondaryReferenceFrame
,
3081 Maybe
<nsDisplayListBuilder::AutoBuildingDisplayList
>*
3082 aAutoBuildingDisplayList
) {
3083 MOZ_ASSERT(!aFrame
->IsCanvasFrame(),
3084 "We don't expect propagated canvas backgrounds here");
3087 nsIFrame
* bgFrame
= nsCSSRendering::FindBackgroundFrame(aFrame
);
3089 !bgFrame
|| bgFrame
== aFrame
,
3090 "Should only suppress backgrounds, never propagate to another frame");
3094 DealWithWindowsAppearanceHacks(aFrame
, aBuilder
);
3096 const bool isThemed
= aFrame
->IsThemed();
3098 const ComputedStyle
* bgSC
= aFrame
->Style();
3099 const nsStyleBackground
* bg
= bgSC
->StyleBackground();
3100 const bool needsBackgroundColor
=
3101 aBuilder
->IsForEventDelivery() ||
3102 (EffectCompositor::HasAnimationsForCompositor(
3103 aFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
) &&
3105 if (!needsBackgroundColor
&& !isThemed
&& bg
->IsTransparent(bgSC
)) {
3106 return AppendedBackgroundType::None
;
3109 bool drawBackgroundColor
= false;
3110 bool drawBackgroundImage
= false;
3111 nscolor color
= NS_RGBA(0, 0, 0, 0);
3112 // Don't get background color / images if we propagated our background to the
3113 // canvas (that is, if FindBackgroundFrame is null). But don't early return
3114 // yet, since we might still need a background-color item for hit-testing.
3115 if (!isThemed
&& nsCSSRendering::FindBackgroundFrame(aFrame
)) {
3116 color
= nsCSSRendering::DetermineBackgroundColor(
3117 aFrame
->PresContext(), bgSC
, aFrame
, drawBackgroundImage
,
3118 drawBackgroundColor
);
3121 if (SpecialCutoutRegionCase(aBuilder
, aFrame
, aBackgroundRect
, aList
,
3123 return AppendedBackgroundType::None
;
3126 const nsStyleBorder
& border
= *aFrame
->StyleBorder();
3127 const bool willPaintBorder
=
3128 aAllowWillPaintBorderOptimization
&& !isThemed
&&
3129 !aFrame
->StyleEffects()->HasBoxShadowWithInset(true) &&
3132 auto EnsureBuildingDisplayList
= [&] {
3133 if (!aAutoBuildingDisplayList
|| *aAutoBuildingDisplayList
) {
3136 nsPoint offset
= aBuilder
->GetCurrentFrame()->GetOffsetTo(aFrame
);
3137 aAutoBuildingDisplayList
->emplace(aBuilder
, aFrame
,
3138 aBuilder
->GetVisibleRect() + offset
,
3139 aBuilder
->GetDirtyRect() + offset
);
3142 // An auxiliary list is necessary in case we have background blending; if that
3143 // is the case, background items need to be wrapped by a blend container to
3144 // isolate blending to the background
3145 nsDisplayList
bgItemList(aBuilder
);
3146 // Even if we don't actually have a background color to paint, we may still
3147 // need to create an item for hit testing and we still need to create an item
3148 // for background-color animations.
3149 if ((drawBackgroundColor
&& color
!= NS_RGBA(0, 0, 0, 0)) ||
3150 needsBackgroundColor
) {
3151 EnsureBuildingDisplayList();
3152 Maybe
<DisplayListClipState::AutoSaveRestore
> clipState
;
3153 nsRect bgColorRect
= aBackgroundRect
;
3154 if (!isThemed
&& !aBuilder
->IsForEventDelivery()) {
3155 // Disable the will-paint-border optimization for background
3156 // colors with no border-radius. Enabling it for background colors
3157 // doesn't help much (there are no tiling issues) and clipping the
3158 // background breaks detection of the element's border-box being
3159 // opaque. For nonzero border-radius we still need it because we
3160 // want to inset the background if possible to avoid antialiasing
3161 // artifacts along the rounded corners.
3162 const bool useWillPaintBorderOptimization
=
3164 nsLayoutUtils::HasNonZeroCorner(border
.mBorderRadius
);
3166 nsCSSRendering::ImageLayerClipState clip
;
3167 nsCSSRendering::GetImageLayerClip(
3168 bg
->BottomLayer(), aFrame
, border
, aBackgroundRect
, aBackgroundRect
,
3169 useWillPaintBorderOptimization
,
3170 aFrame
->PresContext()->AppUnitsPerDevPixel(), &clip
);
3172 bgColorRect
= bgColorRect
.Intersect(clip
.mBGClipArea
);
3173 if (clip
.mHasAdditionalBGClipArea
) {
3174 bgColorRect
= bgColorRect
.Intersect(clip
.mAdditionalBGClipArea
);
3176 if (clip
.mHasRoundedCorners
) {
3177 clipState
.emplace(aBuilder
);
3178 clipState
->ClipContentDescendants(clip
.mBGClipArea
, clip
.mRadii
);
3182 nsDisplayBackgroundColor
* bgItem
= CreateBackgroundColor(
3183 aBuilder
, aFrame
, aSecondaryReferenceFrame
, bgColorRect
, bgSC
,
3184 drawBackgroundColor
? color
: NS_RGBA(0, 0, 0, 0));
3187 bgItemList
.AppendToTop(bgItem
);
3192 nsDisplayThemedBackground
* bgItem
= CreateThemedBackground(
3193 aBuilder
, aFrame
, aSecondaryReferenceFrame
, aBackgroundRect
);
3196 bgItem
->Init(aBuilder
);
3197 bgItemList
.AppendToTop(bgItem
);
3200 if (!bgItemList
.IsEmpty()) {
3201 aList
->AppendToTop(&bgItemList
);
3202 return AppendedBackgroundType::ThemedBackground
;
3205 return AppendedBackgroundType::None
;
3208 if (!drawBackgroundImage
) {
3209 if (!bgItemList
.IsEmpty()) {
3210 aList
->AppendToTop(&bgItemList
);
3211 return AppendedBackgroundType::Background
;
3214 return AppendedBackgroundType::None
;
3217 const ActiveScrolledRoot
* asr
= aBuilder
->CurrentActiveScrolledRoot();
3219 bool needBlendContainer
= false;
3220 const nsRect
& bgOriginRect
=
3221 aBackgroundOriginRect
.IsEmpty() ? aBackgroundRect
: aBackgroundOriginRect
;
3223 // Passing bg == nullptr in this macro will result in one iteration with
3225 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i
, bg
->mImage
) {
3226 if (bg
->mImage
.mLayers
[i
].mImage
.IsNone()) {
3230 EnsureBuildingDisplayList();
3232 if (bg
->mImage
.mLayers
[i
].mBlendMode
!= StyleBlend::Normal
) {
3233 needBlendContainer
= true;
3236 DisplayListClipState::AutoSaveRestore
clipState(aBuilder
);
3237 if (!aBuilder
->IsForEventDelivery()) {
3238 const nsStyleImageLayers::Layer
& layer
= bg
->mImage
.mLayers
[i
];
3239 SetBackgroundClipRegion(clipState
, aFrame
, layer
, aBackgroundRect
,
3243 nsDisplayList
thisItemList(aBuilder
);
3244 nsDisplayBackgroundImage::InitData bgData
=
3245 nsDisplayBackgroundImage::GetInitData(aBuilder
, aFrame
, i
, bgOriginRect
,
3248 if (bgData
.shouldFixToViewport
) {
3249 auto* displayData
= aBuilder
->GetCurrentFixedBackgroundDisplayData();
3250 nsDisplayListBuilder::AutoBuildingDisplayList
buildingDisplayList(
3251 aBuilder
, aFrame
, aBuilder
->GetVisibleRect(),
3252 aBuilder
->GetDirtyRect());
3254 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter
asrSetter(
3257 asrSetter
.SetCurrentActiveScrolledRoot(
3258 displayData
->mContainingBlockActiveScrolledRoot
);
3259 asrSetter
.SetCurrentScrollParentId(displayData
->mScrollParentId
);
3260 if (nsLayoutUtils::UsesAsyncScrolling(aFrame
)) {
3261 // Override the dirty rect on the builder to be the dirty rect of
3263 // displayData->mDirtyRect is relative to the presshell's viewport
3264 // frame (the root frame), and we need it to be relative to aFrame.
3265 nsIFrame
* rootFrame
=
3266 aBuilder
->CurrentPresShellState()->mPresShell
->GetRootFrame();
3267 // There cannot be any transforms between aFrame and rootFrame
3268 // because then bgData.shouldFixToViewport would have been false.
3269 nsRect visibleRect
=
3270 displayData
->mVisibleRect
+ aFrame
->GetOffsetTo(rootFrame
);
3271 aBuilder
->SetVisibleRect(visibleRect
);
3273 displayData
->mDirtyRect
+ aFrame
->GetOffsetTo(rootFrame
);
3274 aBuilder
->SetDirtyRect(dirtyRect
);
3278 nsDisplayBackgroundImage
* bgItem
= nullptr;
3280 // The clip is captured by the nsDisplayFixedPosition, so clear the
3281 // clip for the nsDisplayBackgroundImage inside.
3282 DisplayListClipState::AutoSaveRestore
bgImageClip(aBuilder
);
3283 bgImageClip
.Clear();
3284 bgItem
= CreateBackgroundImage(aBuilder
, aFrame
,
3285 aSecondaryReferenceFrame
, bgData
);
3288 thisItemList
.AppendToTop(
3289 nsDisplayFixedPosition::CreateForFixedBackground(
3290 aBuilder
, aFrame
, aSecondaryReferenceFrame
, bgItem
, i
, asr
));
3292 } else { // bgData.shouldFixToViewport == false
3293 nsDisplayBackgroundImage
* bgItem
= CreateBackgroundImage(
3294 aBuilder
, aFrame
, aSecondaryReferenceFrame
, bgData
);
3296 thisItemList
.AppendToTop(bgItem
);
3300 if (bg
->mImage
.mLayers
[i
].mBlendMode
!= StyleBlend::Normal
) {
3301 // asr is scrolled. Even if we wrap a fixed background layer, that's
3302 // fine, because the item will have a scrolled clip that limits the
3303 // item with respect to asr.
3304 if (aSecondaryReferenceFrame
) {
3305 const auto tableType
= GetTableTypeFromFrame(aFrame
);
3306 const uint16_t index
= CalculateTablePerFrameKey(i
+ 1, tableType
);
3308 thisItemList
.AppendNewToTopWithIndex
<nsDisplayTableBlendMode
>(
3309 aBuilder
, aSecondaryReferenceFrame
, index
, &thisItemList
,
3310 bg
->mImage
.mLayers
[i
].mBlendMode
, asr
, aFrame
, true);
3312 thisItemList
.AppendNewToTopWithIndex
<nsDisplayBlendMode
>(
3313 aBuilder
, aFrame
, i
+ 1, &thisItemList
,
3314 bg
->mImage
.mLayers
[i
].mBlendMode
, asr
, true);
3317 bgItemList
.AppendToTop(&thisItemList
);
3320 if (needBlendContainer
) {
3321 bgItemList
.AppendToTop(
3322 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3323 aBuilder
, aFrame
, aSecondaryReferenceFrame
, &bgItemList
, asr
));
3326 if (!bgItemList
.IsEmpty()) {
3327 aList
->AppendToTop(&bgItemList
);
3328 return AppendedBackgroundType::Background
;
3331 return AppendedBackgroundType::None
;
3334 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3335 // intersects aRect. Assumes that the unrounded border has already
3336 // been checked for intersection.
3337 static bool RoundedBorderIntersectsRect(nsIFrame
* aFrame
,
3338 const nsPoint
& aFrameToReferenceFrame
,
3339 const nsRect
& aTestRect
) {
3340 if (!nsRect(aFrameToReferenceFrame
, aFrame
->GetSize())
3341 .Intersects(aTestRect
)) {
3346 return !aFrame
->GetBorderRadii(radii
) ||
3347 nsLayoutUtils::RoundedRectIntersectsRect(
3348 nsRect(aFrameToReferenceFrame
, aFrame
->GetSize()), radii
,
3352 // Returns TRUE if aContainedRect is guaranteed to be contained in
3353 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3354 // handled conservatively by returning FALSE in some situations where
3355 // a more thorough analysis could return TRUE.
3357 // See also RoundedRectIntersectsRect.
3358 static bool RoundedRectContainsRect(const nsRect
& aRoundedRect
,
3359 const nscoord aRadii
[8],
3360 const nsRect
& aContainedRect
) {
3361 nsRegion rgn
= nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect
, aRadii
,
3363 return rgn
.Contains(aContainedRect
);
3366 bool nsDisplayBackgroundImage::CanApplyOpacity(
3367 WebRenderLayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
) const {
3368 return CanBuildWebRenderDisplayItems(aManager
, aBuilder
);
3371 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3372 WebRenderLayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
) const {
3373 return mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
].mClip
!=
3374 StyleGeometryBox::Text
&&
3375 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3376 aManager
, *StyleFrame()->PresContext(), StyleFrame(),
3377 mBackgroundStyle
->StyleBackground(), mLayer
,
3378 aBuilder
->GetBackgroundPaintFlags());
3381 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3382 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
3383 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
3384 nsDisplayListBuilder
* aDisplayListBuilder
) {
3385 if (!CanBuildWebRenderDisplayItems(aManager
->LayerManager(),
3386 aDisplayListBuilder
)) {
3390 uint32_t paintFlags
= aDisplayListBuilder
->GetBackgroundPaintFlags();
3392 nsCSSRendering::PaintBGParams params
=
3393 nsCSSRendering::PaintBGParams::ForSingleLayer(
3394 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder
, &dummy
),
3395 mBackgroundRect
, StyleFrame(), paintFlags
, mLayer
,
3396 CompositionOp::OP_OVER
, aBuilder
.GetInheritedOpacity());
3397 params
.bgClipRect
= &mBounds
;
3398 ImgDrawResult result
=
3399 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3400 params
, aBuilder
, aResources
, aSc
, aManager
, this);
3401 if (result
== ImgDrawResult::NOT_SUPPORTED
) {
3405 if (nsIContent
* content
= StyleFrame()->GetContent()) {
3406 if (imgRequestProxy
* requestProxy
= mBackgroundStyle
->StyleBackground()
3407 ->mImage
.mLayers
[mLayer
]
3408 .mImage
.GetImageRequest()) {
3409 // LCP don't consider gradient backgrounds.
3410 LCPHelpers::FinalizeLCPEntryForImage(content
->AsElement(), requestProxy
,
3411 mBounds
- ToReferenceFrame());
3418 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder
* aBuilder
,
3419 const nsRect
& aRect
,
3420 HitTestState
* aState
,
3421 nsTArray
<nsIFrame
*>* aOutFrames
) {
3422 if (RoundedBorderIntersectsRect(mFrame
, ToReferenceFrame(), aRect
)) {
3423 aOutFrames
->AppendElement(mFrame
);
3427 static nsRect
GetInsideClipRect(const nsDisplayItem
* aItem
,
3428 StyleGeometryBox aClip
, const nsRect
& aRect
,
3429 const nsRect
& aBackgroundRect
) {
3430 if (aRect
.IsEmpty()) {
3434 nsIFrame
* frame
= aItem
->Frame();
3436 nsRect clipRect
= aBackgroundRect
;
3437 if (frame
->IsCanvasFrame()) {
3438 nsCanvasFrame
* canvasFrame
= static_cast<nsCanvasFrame
*>(frame
);
3439 clipRect
= canvasFrame
->CanvasArea() + aItem
->ToReferenceFrame();
3440 } else if (aClip
== StyleGeometryBox::PaddingBox
||
3441 aClip
== StyleGeometryBox::ContentBox
) {
3442 nsMargin border
= frame
->GetUsedBorder();
3443 if (aClip
== StyleGeometryBox::ContentBox
) {
3444 border
+= frame
->GetUsedPadding();
3446 border
.ApplySkipSides(frame
->GetSkipSides());
3447 clipRect
.Deflate(border
);
3450 return clipRect
.Intersect(aRect
);
3453 nsRegion
nsDisplayBackgroundImage::GetOpaqueRegion(
3454 nsDisplayListBuilder
* aBuilder
, bool* aSnap
) const {
3458 if (!mBackgroundStyle
) {
3464 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3465 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3466 // which expects frames to be sent to it in content order, not reverse
3467 // content order which we'll produce here.
3468 // Of course, if there's only one frame in the flow, it doesn't matter.
3469 if (mFrame
->StyleBorder()->mBoxDecorationBreak
==
3470 StyleBoxDecorationBreak::Clone
||
3471 (!mFrame
->GetPrevContinuation() && !mFrame
->GetNextContinuation())) {
3472 const nsStyleImageLayers::Layer
& layer
=
3473 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
3474 if (layer
.mImage
.IsOpaque() && layer
.mBlendMode
== StyleBlend::Normal
&&
3475 layer
.mRepeat
.mXRepeat
!= StyleImageLayerRepeat::Space
&&
3476 layer
.mRepeat
.mYRepeat
!= StyleImageLayerRepeat::Space
&&
3477 layer
.mClip
!= StyleGeometryBox::Text
) {
3478 result
= GetInsideClipRect(this, layer
.mClip
, mBounds
, mBackgroundRect
);
3485 Maybe
<nscolor
> nsDisplayBackgroundImage::IsUniform(
3486 nsDisplayListBuilder
* aBuilder
) const {
3487 if (!mBackgroundStyle
) {
3488 return Some(NS_RGBA(0, 0, 0, 0));
3493 nsRect
nsDisplayBackgroundImage::GetPositioningArea() const {
3494 if (!mBackgroundStyle
) {
3497 nsIFrame
* attachedToFrame
;
3498 bool transformedFixed
;
3499 return nsCSSRendering::ComputeImageLayerPositioningArea(
3500 mFrame
->PresContext(), mFrame
, mBackgroundRect
,
3501 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
],
3502 &attachedToFrame
, &transformedFixed
) +
3506 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3508 if (!mBackgroundStyle
) {
3513 if (mFrame
->GetBorderRadii(radii
)) {
3514 // A change in the size of the positioning area might change the position
3515 // of the rounded corners.
3519 const nsStyleImageLayers::Layer
& layer
=
3520 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
3521 return layer
.RenderingMightDependOnPositioningAreaSizeChange();
3524 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder
* aBuilder
,
3526 PaintInternal(aBuilder
, aCtx
, GetPaintRect(aBuilder
, aCtx
), &mBounds
);
3529 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder
* aBuilder
,
3531 const nsRect
& aBounds
,
3532 nsRect
* aClipRect
) {
3533 gfxContext
* ctx
= aCtx
;
3534 StyleGeometryBox clip
=
3535 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
].mClip
;
3537 if (clip
== StyleGeometryBox::Text
) {
3538 if (!GenerateAndPushTextMask(StyleFrame(), aCtx
, mBackgroundRect
,
3544 nsCSSRendering::PaintBGParams params
=
3545 nsCSSRendering::PaintBGParams::ForSingleLayer(
3546 *StyleFrame()->PresContext(), aBounds
, mBackgroundRect
, StyleFrame(),
3547 aBuilder
->GetBackgroundPaintFlags(), mLayer
, CompositionOp::OP_OVER
,
3549 params
.bgClipRect
= aClipRect
;
3550 Unused
<< nsCSSRendering::PaintStyleImageLayer(params
, *aCtx
);
3552 if (clip
== StyleGeometryBox::Text
) {
3553 ctx
->PopGroupAndBlend();
3557 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3558 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
3559 nsRegion
* aInvalidRegion
) const {
3560 if (!mBackgroundStyle
) {
3564 const auto* geometry
=
3565 static_cast<const nsDisplayBackgroundGeometry
*>(aGeometry
);
3568 nsRect bounds
= GetBounds(aBuilder
, &snap
);
3569 nsRect positioningArea
= GetPositioningArea();
3570 if (positioningArea
.TopLeft() != geometry
->mPositioningArea
.TopLeft() ||
3571 (positioningArea
.Size() != geometry
->mPositioningArea
.Size() &&
3572 RenderingMightDependOnPositioningAreaSizeChange())) {
3573 // Positioning area changed in a way that could cause everything to change,
3574 // so invalidate everything (both old and new painting areas).
3575 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
3578 if (!mDestRect
.IsEqualInterior(geometry
->mDestRect
)) {
3579 // Dest area changed in a way that could cause everything to change,
3580 // so invalidate everything (both old and new painting areas).
3581 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
3584 if (!bounds
.IsEqualInterior(geometry
->mBounds
)) {
3585 // Positioning area is unchanged, so invalidate just the change in the
3587 aInvalidRegion
->Xor(bounds
, geometry
->mBounds
);
3591 nsRect
nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder
* aBuilder
,
3592 bool* aSnap
) const {
3597 nsRect
nsDisplayBackgroundImage::GetBoundsInternal(
3598 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrameForBounds
) {
3599 // This allows nsDisplayTableBackgroundImage to change the frame used for
3600 // bounds calculation.
3601 nsIFrame
* frame
= aFrameForBounds
? aFrameForBounds
: mFrame
;
3603 nsPresContext
* presContext
= frame
->PresContext();
3605 if (!mBackgroundStyle
) {
3609 nsRect clipRect
= mBackgroundRect
;
3610 if (frame
->IsCanvasFrame()) {
3611 nsCanvasFrame
* canvasFrame
= static_cast<nsCanvasFrame
*>(frame
);
3612 clipRect
= canvasFrame
->CanvasArea() + ToReferenceFrame();
3614 const nsStyleImageLayers::Layer
& layer
=
3615 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
3616 return nsCSSRendering::GetBackgroundLayerRect(
3617 presContext
, frame
, mBackgroundRect
, clipRect
, layer
,
3618 aBuilder
->GetBackgroundPaintFlags());
3621 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3622 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, const InitData
& aData
,
3623 nsIFrame
* aCellFrame
)
3624 : nsDisplayBackgroundImage(aBuilder
, aFrame
, aData
, aCellFrame
),
3625 mStyleFrame(aCellFrame
) {
3626 if (aBuilder
->IsRetainingDisplayList()) {
3627 mStyleFrame
->AddDisplayItem(this);
3631 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3633 mStyleFrame
->RemoveDisplayItem(this);
3637 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect
& aRect
) const {
3638 bool result
= mStyleFrame
? mStyleFrame
->IsInvalid(aRect
) : false;
3639 aRect
+= ToReferenceFrame();
3643 nsDisplayThemedBackground::nsDisplayThemedBackground(
3644 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
3645 const nsRect
& aBackgroundRect
)
3646 : nsPaintedDisplayItem(aBuilder
, aFrame
), mBackgroundRect(aBackgroundRect
) {
3647 MOZ_COUNT_CTOR(nsDisplayThemedBackground
);
3650 void nsDisplayThemedBackground::Init(nsDisplayListBuilder
* aBuilder
) {
3651 const nsStyleDisplay
* disp
= StyleFrame()->StyleDisplay();
3652 mAppearance
= disp
->EffectiveAppearance();
3653 StyleFrame()->IsThemed(disp
, &mThemeTransparency
);
3655 // Perform necessary RegisterThemeGeometry
3656 nsITheme
* theme
= StyleFrame()->PresContext()->Theme();
3657 nsITheme::ThemeGeometryType type
=
3658 theme
->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance
);
3659 if (type
!= nsITheme::eThemeGeometryTypeUnknown
) {
3660 RegisterThemeGeometry(aBuilder
, this, StyleFrame(), type
);
3663 mBounds
= GetBoundsInternal();
3666 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream
& aStream
) {
3667 aStream
<< " (themed, appearance:" << (int)mAppearance
<< ")";
3670 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder
* aBuilder
,
3671 const nsRect
& aRect
,
3672 HitTestState
* aState
,
3673 nsTArray
<nsIFrame
*>* aOutFrames
) {
3674 // Assume that any point in our background rect is a hit.
3675 if (mBackgroundRect
.Intersects(aRect
)) {
3676 aOutFrames
->AppendElement(mFrame
);
3680 nsRegion
nsDisplayThemedBackground::GetOpaqueRegion(
3681 nsDisplayListBuilder
* aBuilder
, bool* aSnap
) const {
3685 if (mThemeTransparency
== nsITheme::eOpaque
) {
3687 result
= mBackgroundRect
;
3692 Maybe
<nscolor
> nsDisplayThemedBackground::IsUniform(
3693 nsDisplayListBuilder
* aBuilder
) const {
3697 nsRect
nsDisplayThemedBackground::GetPositioningArea() const {
3698 return mBackgroundRect
;
3701 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder
* aBuilder
,
3703 PaintInternal(aBuilder
, aCtx
, GetPaintRect(aBuilder
, aCtx
), nullptr);
3706 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder
* aBuilder
,
3708 const nsRect
& aBounds
,
3709 nsRect
* aClipRect
) {
3710 // XXXzw this ignores aClipRect.
3711 nsPresContext
* presContext
= StyleFrame()->PresContext();
3712 nsITheme
* theme
= presContext
->Theme();
3713 nsRect
drawing(mBackgroundRect
);
3714 theme
->GetWidgetOverflow(presContext
->DeviceContext(), StyleFrame(),
3715 mAppearance
, &drawing
);
3716 drawing
.IntersectRect(drawing
, aBounds
);
3717 theme
->DrawWidgetBackground(aCtx
, StyleFrame(), mAppearance
, mBackgroundRect
,
3721 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3722 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
3723 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
3724 nsDisplayListBuilder
* aDisplayListBuilder
) {
3725 nsITheme
* theme
= StyleFrame()->PresContext()->Theme();
3726 return theme
->CreateWebRenderCommandsForWidget(aBuilder
, aResources
, aSc
,
3727 aManager
, StyleFrame(),
3728 mAppearance
, mBackgroundRect
);
3731 bool nsDisplayThemedBackground::IsWindowActive() const {
3732 return !mFrame
->PresContext()->Document()->IsTopLevelWindowInactive();
3735 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3736 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
3737 nsRegion
* aInvalidRegion
) const {
3738 const auto* geometry
=
3739 static_cast<const nsDisplayThemedBackgroundGeometry
*>(aGeometry
);
3742 nsRect bounds
= GetBounds(aBuilder
, &snap
);
3743 nsRect positioningArea
= GetPositioningArea();
3744 if (!positioningArea
.IsEqualInterior(geometry
->mPositioningArea
)) {
3745 // Invalidate everything (both old and new painting areas).
3746 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
3749 if (!bounds
.IsEqualInterior(geometry
->mBounds
)) {
3750 // Positioning area is unchanged, so invalidate just the change in the
3752 aInvalidRegion
->Xor(bounds
, geometry
->mBounds
);
3754 nsITheme
* theme
= StyleFrame()->PresContext()->Theme();
3755 if (theme
->WidgetAppearanceDependsOnWindowFocus(mAppearance
) &&
3756 IsWindowActive() != geometry
->mWindowIsActive
) {
3757 aInvalidRegion
->Or(*aInvalidRegion
, bounds
);
3761 nsRect
nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder
* aBuilder
,
3762 bool* aSnap
) const {
3767 nsRect
nsDisplayThemedBackground::GetBoundsInternal() {
3768 nsPresContext
* presContext
= mFrame
->PresContext();
3770 nsRect r
= mBackgroundRect
- ToReferenceFrame();
3771 presContext
->Theme()->GetWidgetOverflow(
3772 presContext
->DeviceContext(), mFrame
,
3773 mFrame
->StyleDisplay()->EffectiveAppearance(), &r
);
3774 return r
+ ToReferenceFrame();
3777 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
3778 void nsDisplayReflowCount::Paint(nsDisplayListBuilder
* aBuilder
,
3780 mFrame
->PresShell()->PaintCount(mFrameName
, aCtx
, mFrame
->PresContext(),
3781 mFrame
, ToReferenceFrame(), mColor
);
3785 bool nsDisplayBackgroundColor::CanApplyOpacity(
3786 WebRenderLayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
) const {
3787 // Don't apply opacity if the background color is animated since the color is
3788 // going to be changed on the compositor.
3789 return !EffectCompositor::HasAnimationsForCompositor(
3790 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
);
3793 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3794 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
3795 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
3796 nsDisplayListBuilder
* aDisplayListBuilder
) {
3797 gfx::sRGBColor color
= mColor
;
3798 color
.a
*= aBuilder
.GetInheritedOpacity();
3800 if (color
== sRGBColor() &&
3801 !EffectCompositor::HasAnimationsForCompositor(
3802 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
)) {
3806 if (HasBackgroundClipText()) {
3810 uint64_t animationsId
= 0;
3811 // We don't support background-color animations on table elements yet.
3812 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR
) {
3814 AddAnimationsForWebRender(this, aManager
, aDisplayListBuilder
);
3817 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
3818 mBackgroundRect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
3819 wr::LayoutRect r
= wr::ToLayoutRect(bounds
);
3822 wr::WrAnimationProperty prop
{
3823 wr::WrAnimationType::BackgroundColor
,
3826 aBuilder
.PushRectWithAnimation(r
, r
, !BackfaceIsHidden(),
3827 wr::ToColorF(ToDeviceColor(color
)), &prop
);
3829 aBuilder
.StartGroup(this);
3830 aBuilder
.PushRect(r
, r
, !BackfaceIsHidden(), false, false,
3831 wr::ToColorF(ToDeviceColor(color
)));
3832 aBuilder
.FinishGroup();
3838 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder
* aBuilder
,
3840 const DisplayItemClip
& aClip
) {
3841 MOZ_ASSERT(!HasBackgroundClipText());
3843 if (mColor
== sRGBColor()) {
3847 nsRect fillRect
= mBackgroundRect
;
3848 if (aClip
.HasClip()) {
3849 fillRect
.IntersectRect(fillRect
, aClip
.GetClipRect());
3852 DrawTarget
* dt
= aCtx
->GetDrawTarget();
3853 int32_t A2D
= mFrame
->PresContext()->AppUnitsPerDevPixel();
3854 Rect bounds
= ToRect(nsLayoutUtils::RectToGfxRect(fillRect
, A2D
));
3855 MaybeSnapToDevicePixels(bounds
, *dt
);
3856 ColorPattern
fill(ToDeviceColor(mColor
));
3858 if (aClip
.GetRoundedRectCount()) {
3859 MOZ_ASSERT(aClip
.GetRoundedRectCount() == 1);
3861 AutoTArray
<DisplayItemClip::RoundedRect
, 1> roundedRect
;
3862 aClip
.AppendRoundedRects(&roundedRect
);
3864 bool pushedClip
= false;
3865 if (!fillRect
.Contains(roundedRect
[0].mRect
)) {
3866 dt
->PushClipRect(bounds
);
3870 RectCornerRadii pixelRadii
;
3871 nsCSSRendering::ComputePixelRadii(roundedRect
[0].mRadii
, A2D
, &pixelRadii
);
3872 dt
->FillRoundedRect(
3873 RoundedRect(NSRectToSnappedRect(roundedRect
[0].mRect
, A2D
, *dt
),
3880 dt
->FillRect(bounds
, fill
);
3884 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder
* aBuilder
,
3886 if (mColor
== sRGBColor()) {
3891 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3892 // results in a precision induced rounding issue that makes the rect one
3893 // pixel shorter in rare cases. Disabled in favor of the old code for now.
3894 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3895 // reproduce the bug.
3898 // This new path does not include support for background-clip:text; need to
3899 // be fixed if/when we switch to this new code path.
3901 DrawTarget
& aDrawTarget
= *aCtx
->GetDrawTarget();
3903 Rect rect
= NSRectToSnappedRect(mBackgroundRect
,
3904 mFrame
->PresContext()->AppUnitsPerDevPixel(),
3906 ColorPattern
color(ToDeviceColor(mColor
));
3907 aDrawTarget
.FillRect(rect
, color
);
3909 gfxContext
* ctx
= aCtx
;
3910 gfxRect bounds
= nsLayoutUtils::RectToGfxRect(
3911 mBackgroundRect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
3913 if (HasBackgroundClipText()) {
3914 if (!GenerateAndPushTextMask(mFrame
, aCtx
, mBackgroundRect
, aBuilder
)) {
3918 ctx
->SetColor(mColor
);
3920 ctx
->SnappedRectangle(bounds
);
3922 ctx
->PopGroupAndBlend();
3926 ctx
->SetColor(mColor
);
3928 ctx
->SnappedRectangle(bounds
);
3933 nsRegion
nsDisplayBackgroundColor::GetOpaqueRegion(
3934 nsDisplayListBuilder
* aBuilder
, bool* aSnap
) const {
3937 if (mColor
.a
!= 1 ||
3938 // Even if the current alpha channel is 1, we treat this item as if it's
3939 // non-opaque if there is a background-color animation since the animation
3940 // might change the alpha channel.
3941 EffectCompositor::HasAnimationsForCompositor(
3942 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
)) {
3946 if (!mHasStyle
|| HasBackgroundClipText()) {
3951 return GetInsideClipRect(this, mBottomLayerClip
, mBackgroundRect
,
3955 Maybe
<nscolor
> nsDisplayBackgroundColor::IsUniform(
3956 nsDisplayListBuilder
* aBuilder
) const {
3957 return Some(mColor
.ToABGR());
3960 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder
* aBuilder
,
3961 const nsRect
& aRect
,
3962 HitTestState
* aState
,
3963 nsTArray
<nsIFrame
*>* aOutFrames
) {
3964 if (!RoundedBorderIntersectsRect(mFrame
, ToReferenceFrame(), aRect
)) {
3965 // aRect doesn't intersect our border-radius curve.
3969 aOutFrames
->AppendElement(mFrame
);
3972 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream
& aStream
) {
3973 aStream
<< " (rgba " << mColor
.r
<< "," << mColor
.g
<< "," << mColor
.b
<< ","
3975 aStream
<< " backgroundRect" << mBackgroundRect
;
3978 nsRect
nsDisplayOutline::GetBounds(nsDisplayListBuilder
* aBuilder
,
3979 bool* aSnap
) const {
3981 return mFrame
->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
3984 nsRect
nsDisplayOutline::GetInnerRect() const {
3985 if (nsRect
* savedOutlineInnerRect
=
3986 mFrame
->GetProperty(nsIFrame::OutlineInnerRectProperty())) {
3987 return *savedOutlineInnerRect
;
3989 return mFrame
->GetRectRelativeToSelf();
3992 void nsDisplayOutline::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
3993 // TODO join outlines together
3994 MOZ_ASSERT(mFrame
->StyleOutline()->ShouldPaintOutline(),
3995 "Should have not created a nsDisplayOutline!");
3997 nsRect rect
= GetInnerRect() + ToReferenceFrame();
3998 nsPresContext
* pc
= mFrame
->PresContext();
3999 if (IsThemedOutline()) {
4000 rect
.Inflate(mFrame
->StyleOutline()->EffectiveOffsetFor(rect
));
4001 pc
->Theme()->DrawWidgetBackground(aCtx
, mFrame
,
4002 StyleAppearance::FocusOutline
, rect
,
4003 GetPaintRect(aBuilder
, aCtx
));
4007 nsCSSRendering::PaintNonThemedOutline(
4008 pc
, *aCtx
, mFrame
, GetPaintRect(aBuilder
, aCtx
), rect
, mFrame
->Style());
4011 bool nsDisplayOutline::IsThemedOutline() const {
4013 nsPresContext
* pc
= mFrame
->PresContext();
4015 pc
->Theme()->ThemeSupportsWidget(pc
, mFrame
,
4016 StyleAppearance::FocusOutline
),
4017 "All of our supported platforms have support for themed focus-outlines");
4019 return mFrame
->StyleOutline()->mOutlineStyle
.IsAuto();
4022 bool nsDisplayOutline::CreateWebRenderCommands(
4023 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4024 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4025 nsDisplayListBuilder
* aDisplayListBuilder
) {
4026 nsPresContext
* pc
= mFrame
->PresContext();
4027 nsRect rect
= GetInnerRect() + ToReferenceFrame();
4028 if (IsThemedOutline()) {
4029 rect
.Inflate(mFrame
->StyleOutline()->EffectiveOffsetFor(rect
));
4030 return pc
->Theme()->CreateWebRenderCommandsForWidget(
4031 aBuilder
, aResources
, aSc
, aManager
, mFrame
,
4032 StyleAppearance::FocusOutline
, rect
);
4036 Maybe
<nsCSSBorderRenderer
> borderRenderer
=
4037 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4038 pc
, /* aDrawTarget = */ nullptr, mFrame
,
4039 GetBounds(aDisplayListBuilder
, &dummy
), rect
, mFrame
->Style());
4041 if (!borderRenderer
) {
4042 // No border renderer means "there is no outline".
4043 // Paint nothing and return success.
4047 borderRenderer
->CreateWebRenderCommands(this, aBuilder
, aResources
, aSc
);
4051 bool nsDisplayOutline::HasRadius() const {
4052 const auto& radius
= mFrame
->StyleBorder()->mBorderRadius
;
4053 return !nsLayoutUtils::HasNonZeroCorner(radius
);
4056 bool nsDisplayOutline::IsInvisibleInRect(const nsRect
& aRect
) const {
4057 const nsStyleOutline
* outline
= mFrame
->StyleOutline();
4058 nsRect
borderBox(ToReferenceFrame(), mFrame
->GetSize());
4059 if (borderBox
.Contains(aRect
) && !HasRadius() &&
4060 outline
->mOutlineOffset
.ToCSSPixels() >= 0.0f
) {
4061 // aRect is entirely inside the border-rect, and the outline isn't rendered
4062 // inside the border-rect, so the outline is not visible.
4068 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder
* aBuilder
,
4069 const nsRect
& aRect
, HitTestState
* aState
,
4070 nsTArray
<nsIFrame
*>* aOutFrames
) {
4071 if (!RoundedBorderIntersectsRect(mFrame
, ToReferenceFrame(), aRect
)) {
4072 // aRect doesn't intersect our border-radius curve.
4076 aOutFrames
->AppendElement(mFrame
);
4079 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4080 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4081 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4082 nsDisplayListBuilder
* aDisplayListBuilder
) {
4086 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4087 return mOverrideZIndex
? *mOverrideZIndex
: nsDisplayItem::ZIndex();
4090 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex
) {
4091 mOverrideZIndex
= Some(aZIndex
);
4094 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder
* aBuilder
,
4095 nsIFrame
* aCaretFrame
)
4096 : nsPaintedDisplayItem(aBuilder
, aCaretFrame
),
4097 mCaret(aBuilder
->GetCaret()),
4098 mBounds(aBuilder
->GetCaretRect() + ToReferenceFrame()) {
4099 MOZ_COUNT_CTOR(nsDisplayCaret
);
4100 // The presence of a caret doesn't change the overflow rect
4101 // of the owning frame, so the normal building rect might not
4102 // include the caret at all. We use MarkFrameForDisplay to ensure
4103 // we build this item, and here we override the building rect
4104 // to cover the pixels we're going to draw.
4105 SetBuildingRect(mBounds
);
4108 #ifdef NS_BUILD_REFCNT_LOGGING
4109 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret
); }
4112 nsRect
nsDisplayCaret::GetBounds(nsDisplayListBuilder
* aBuilder
,
4113 bool* aSnap
) const {
4115 // The caret returns a rect in the coordinates of mFrame.
4119 void nsDisplayCaret::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
4120 // Note: Because we exist, we know that the caret is visible, so we don't
4121 // need to check for the caret's visibility.
4122 mCaret
->PaintCaret(*aCtx
->GetDrawTarget(), mFrame
, ToReferenceFrame());
4125 bool nsDisplayCaret::CreateWebRenderCommands(
4126 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4127 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4128 nsDisplayListBuilder
* aDisplayListBuilder
) {
4129 using namespace layers
;
4134 mCaret
->GetPaintGeometry(&caretRect
, &hookRect
, &caretColor
);
4135 MOZ_ASSERT(frame
== mFrame
, "We're referring different frame");
4140 int32_t appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
4141 gfx::DeviceColor color
= ToDeviceColor(caretColor
);
4142 LayoutDeviceRect devCaretRect
= LayoutDeviceRect::FromAppUnits(
4143 caretRect
+ ToReferenceFrame(), appUnitsPerDevPixel
);
4144 LayoutDeviceRect devHookRect
= LayoutDeviceRect::FromAppUnits(
4145 hookRect
+ ToReferenceFrame(), appUnitsPerDevPixel
);
4147 wr::LayoutRect caret
= wr::ToLayoutRect(devCaretRect
);
4148 wr::LayoutRect hook
= wr::ToLayoutRect(devHookRect
);
4150 // Note, WR will pixel snap anything that is layout aligned.
4151 aBuilder
.PushRect(caret
, caret
, !BackfaceIsHidden(), false, false,
4152 wr::ToColorF(color
));
4154 if (!devHookRect
.IsEmpty()) {
4155 aBuilder
.PushRect(hook
, hook
, !BackfaceIsHidden(), false, false,
4156 wr::ToColorF(color
));
4161 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder
* aBuilder
,
4163 : nsPaintedDisplayItem(aBuilder
, aFrame
) {
4164 MOZ_COUNT_CTOR(nsDisplayBorder
);
4166 mBounds
= CalculateBounds
<nsRect
>(*mFrame
->StyleBorder());
4169 bool nsDisplayBorder::IsInvisibleInRect(const nsRect
& aRect
) const {
4170 nsRect paddingRect
= GetPaddingRect();
4171 const nsStyleBorder
* styleBorder
;
4172 if (paddingRect
.Contains(aRect
) &&
4173 !(styleBorder
= mFrame
->StyleBorder())->IsBorderImageSizeAvailable() &&
4174 !nsLayoutUtils::HasNonZeroCorner(styleBorder
->mBorderRadius
)) {
4175 // aRect is entirely inside the content rect, and no part
4176 // of the border is rendered inside the content rect, so we are not
4178 // Skip this if there's a border-image (which draws a background
4179 // too) or if there is a border-radius (which makes the border draw
4187 nsDisplayItemGeometry
* nsDisplayBorder::AllocateGeometry(
4188 nsDisplayListBuilder
* aBuilder
) {
4189 return new nsDisplayBorderGeometry(this, aBuilder
);
4192 void nsDisplayBorder::ComputeInvalidationRegion(
4193 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
4194 nsRegion
* aInvalidRegion
) const {
4195 const auto* geometry
= static_cast<const nsDisplayBorderGeometry
*>(aGeometry
);
4198 if (!geometry
->mBounds
.IsEqualInterior(GetBounds(aBuilder
, &snap
))) {
4199 // We can probably get away with only invalidating the difference
4200 // between the border and padding rects, but the XUL ui at least
4201 // is apparently painting a background with this?
4202 aInvalidRegion
->Or(GetBounds(aBuilder
, &snap
), geometry
->mBounds
);
4206 bool nsDisplayBorder::CreateWebRenderCommands(
4207 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4208 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4209 nsDisplayListBuilder
* aDisplayListBuilder
) {
4210 nsRect rect
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
4212 ImgDrawResult drawResult
= nsCSSRendering::CreateWebRenderCommandsForBorder(
4213 this, mFrame
, rect
, aBuilder
, aResources
, aSc
, aManager
,
4214 aDisplayListBuilder
);
4216 if (drawResult
== ImgDrawResult::NOT_SUPPORTED
) {
4222 void nsDisplayBorder::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
4223 nsPoint offset
= ToReferenceFrame();
4225 PaintBorderFlags flags
= aBuilder
->ShouldSyncDecodeImages()
4226 ? PaintBorderFlags::SyncDecodeImages
4227 : PaintBorderFlags();
4229 Unused
<< nsCSSRendering::PaintBorder(
4230 mFrame
->PresContext(), *aCtx
, mFrame
, GetPaintRect(aBuilder
, aCtx
),
4231 nsRect(offset
, mFrame
->GetSize()), mFrame
->Style(), flags
,
4232 mFrame
->GetSkipSides());
4235 nsRect
nsDisplayBorder::GetBounds(nsDisplayListBuilder
* aBuilder
,
4236 bool* aSnap
) const {
4241 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder
* aBuilder
,
4243 nsPoint offset
= ToReferenceFrame();
4244 nsRect borderRect
= mFrame
->VisualBorderRectRelativeToSelf() + offset
;
4245 nsPresContext
* presContext
= mFrame
->PresContext();
4247 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS
);
4249 nsCSSRendering::PaintBoxShadowOuter(presContext
, *aCtx
, mFrame
, borderRect
,
4250 GetPaintRect(aBuilder
, aCtx
), 1.0f
);
4253 nsRect
nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder
* aBuilder
,
4254 bool* aSnap
) const {
4259 nsRect
nsDisplayBoxShadowOuter::GetBoundsInternal() {
4260 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame
, mFrame
->GetSize()) +
4264 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect
& aRect
) const {
4265 nsPoint origin
= ToReferenceFrame();
4266 nsRect
frameRect(origin
, mFrame
->GetSize());
4267 if (!frameRect
.Contains(aRect
)) {
4271 // the visible region is entirely inside the border-rect, and box shadows
4272 // never render within the border-rect (unless there's a border radius).
4273 nscoord twipsRadii
[8];
4274 bool hasBorderRadii
= mFrame
->GetBorderRadii(twipsRadii
);
4275 if (!hasBorderRadii
) {
4279 return RoundedRectContainsRect(frameRect
, twipsRadii
, aRect
);
4282 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4283 auto shadows
= mFrame
->StyleEffects()->mBoxShadow
.AsSpan();
4284 if (shadows
.IsEmpty()) {
4288 bool hasBorderRadius
;
4289 // We don't support native themed things yet like box shadows around
4292 // TODO(emilio): The non-native theme could provide the right rect+radius
4293 // instead relatively painlessly, if we find this causes performance issues or
4295 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame
, hasBorderRadius
);
4298 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4299 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4300 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4301 nsDisplayListBuilder
* aDisplayListBuilder
) {
4302 if (!CanBuildWebRenderDisplayItems()) {
4306 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
4307 nsPoint offset
= ToReferenceFrame();
4308 nsRect borderRect
= mFrame
->VisualBorderRectRelativeToSelf() + offset
;
4310 nsRect bounds
= GetBounds(aDisplayListBuilder
, &snap
);
4312 bool hasBorderRadius
;
4314 nsCSSRendering::HasBoxShadowNativeTheme(mFrame
, hasBorderRadius
);
4316 // Don't need the full size of the shadow rect like we do in
4317 // nsCSSRendering since WR takes care of calculations for blur
4318 // and spread radius.
4320 nsCSSRendering::GetShadowRect(borderRect
, nativeTheme
, mFrame
);
4322 RectCornerRadii borderRadii
;
4323 if (hasBorderRadius
) {
4324 hasBorderRadius
= nsCSSRendering::GetBorderRadii(frameRect
, borderRect
,
4325 mFrame
, borderRadii
);
4328 // Everything here is in app units, change to device units.
4329 LayoutDeviceRect clipRect
=
4330 LayoutDeviceRect::FromAppUnits(bounds
, appUnitsPerDevPixel
);
4331 auto shadows
= mFrame
->StyleEffects()->mBoxShadow
.AsSpan();
4332 MOZ_ASSERT(!shadows
.IsEmpty());
4334 for (const auto& shadow
: Reversed(shadows
)) {
4340 float(shadow
.base
.blur
.ToAppUnits()) / float(appUnitsPerDevPixel
);
4341 gfx::sRGBColor shadowColor
= nsCSSRendering::GetShadowColor(
4342 shadow
.base
, mFrame
, aBuilder
.GetInheritedOpacity());
4344 // We don't move the shadow rect here since WR does it for us
4345 // Now translate everything to device pixels.
4346 const nsRect
& shadowRect
= frameRect
;
4347 LayoutDevicePoint shadowOffset
= LayoutDevicePoint::FromAppUnits(
4348 nsPoint(shadow
.base
.horizontal
.ToAppUnits(),
4349 shadow
.base
.vertical
.ToAppUnits()),
4350 appUnitsPerDevPixel
);
4352 LayoutDeviceRect deviceBox
=
4353 LayoutDeviceRect::FromAppUnits(shadowRect
, appUnitsPerDevPixel
);
4354 wr::LayoutRect deviceBoxRect
= wr::ToLayoutRect(deviceBox
);
4355 wr::LayoutRect deviceClipRect
= wr::ToLayoutRect(clipRect
);
4357 LayoutDeviceSize zeroSize
;
4358 wr::BorderRadius borderRadius
=
4359 wr::ToBorderRadius(zeroSize
, zeroSize
, zeroSize
, zeroSize
);
4360 if (hasBorderRadius
) {
4361 borderRadius
= wr::ToBorderRadius(
4362 LayoutDeviceSize::FromUnknownSize(borderRadii
.TopLeft()),
4363 LayoutDeviceSize::FromUnknownSize(borderRadii
.TopRight()),
4364 LayoutDeviceSize::FromUnknownSize(borderRadii
.BottomLeft()),
4365 LayoutDeviceSize::FromUnknownSize(borderRadii
.BottomRight()));
4368 float spreadRadius
=
4369 float(shadow
.spread
.ToAppUnits()) / float(appUnitsPerDevPixel
);
4371 aBuilder
.PushBoxShadow(deviceBoxRect
, deviceClipRect
, !BackfaceIsHidden(),
4372 deviceBoxRect
, wr::ToLayoutVector2D(shadowOffset
),
4373 wr::ToColorF(ToDeviceColor(shadowColor
)), blurRadius
,
4374 spreadRadius
, borderRadius
,
4375 wr::BoxShadowClipMode::Outset
);
4381 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4382 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
4383 nsRegion
* aInvalidRegion
) const {
4384 const auto* geometry
=
4385 static_cast<const nsDisplayItemGenericGeometry
*>(aGeometry
);
4387 if (!geometry
->mBounds
.IsEqualInterior(GetBounds(aBuilder
, &snap
)) ||
4388 !geometry
->mBorderRect
.IsEqualInterior(GetBorderRect())) {
4389 nsRegion oldShadow
, newShadow
;
4390 nscoord dontCare
[8];
4391 bool hasBorderRadius
= mFrame
->GetBorderRadii(dontCare
);
4392 if (hasBorderRadius
) {
4393 // If we have rounded corners then we need to invalidate the frame area
4394 // too since we paint into it.
4395 oldShadow
= geometry
->mBounds
;
4396 newShadow
= GetBounds(aBuilder
, &snap
);
4398 oldShadow
.Sub(geometry
->mBounds
, geometry
->mBorderRect
);
4399 newShadow
.Sub(GetBounds(aBuilder
, &snap
), GetBorderRect());
4401 aInvalidRegion
->Or(oldShadow
, newShadow
);
4405 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder
* aBuilder
,
4407 nsPoint offset
= ToReferenceFrame();
4408 nsRect borderRect
= nsRect(offset
, mFrame
->GetSize());
4409 nsPresContext
* presContext
= mFrame
->PresContext();
4411 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS
);
4413 nsCSSRendering::PaintBoxShadowInner(presContext
, *aCtx
, mFrame
, borderRect
);
4416 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4417 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
4418 const nsPoint
& aReferenceOffset
) {
4419 auto shadows
= aFrame
->StyleEffects()->mBoxShadow
.AsSpan();
4420 if (shadows
.IsEmpty()) {
4421 // Means we don't have to paint anything
4425 bool hasBorderRadius
;
4427 nsCSSRendering::HasBoxShadowNativeTheme(aFrame
, hasBorderRadius
);
4429 // We don't support native themed things yet like box shadows around
4431 return !nativeTheme
;
4435 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4436 wr::DisplayListBuilder
& aBuilder
, const StackingContextHelper
& aSc
,
4437 nsRect
& aVisibleRect
, nsIFrame
* aFrame
, const nsRect
& aBorderRect
) {
4438 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame
)) {
4442 int32_t appUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
4444 auto shadows
= aFrame
->StyleEffects()->mBoxShadow
.AsSpan();
4446 LayoutDeviceRect clipRect
=
4447 LayoutDeviceRect::FromAppUnits(aVisibleRect
, appUnitsPerDevPixel
);
4449 for (const auto& shadow
: Reversed(shadows
)) {
4450 if (!shadow
.inset
) {
4455 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame
, aBorderRect
);
4456 RectCornerRadii innerRadii
;
4457 nsCSSRendering::GetShadowInnerRadii(aFrame
, aBorderRect
, innerRadii
);
4459 // Now translate everything to device pixels.
4460 LayoutDeviceRect deviceBoxRect
=
4461 LayoutDeviceRect::FromAppUnits(shadowRect
, appUnitsPerDevPixel
);
4462 wr::LayoutRect deviceClipRect
= wr::ToLayoutRect(clipRect
);
4463 sRGBColor shadowColor
=
4464 nsCSSRendering::GetShadowColor(shadow
.base
, aFrame
, 1.0);
4466 LayoutDevicePoint shadowOffset
= LayoutDevicePoint::FromAppUnits(
4467 nsPoint(shadow
.base
.horizontal
.ToAppUnits(),
4468 shadow
.base
.vertical
.ToAppUnits()),
4469 appUnitsPerDevPixel
);
4472 float(shadow
.base
.blur
.ToAppUnits()) / float(appUnitsPerDevPixel
);
4474 wr::BorderRadius borderRadius
= wr::ToBorderRadius(
4475 LayoutDeviceSize::FromUnknownSize(innerRadii
.TopLeft()),
4476 LayoutDeviceSize::FromUnknownSize(innerRadii
.TopRight()),
4477 LayoutDeviceSize::FromUnknownSize(innerRadii
.BottomLeft()),
4478 LayoutDeviceSize::FromUnknownSize(innerRadii
.BottomRight()));
4479 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4480 float spreadRadius
=
4481 float(shadow
.spread
.ToAppUnits()) / float(appUnitsPerDevPixel
);
4483 aBuilder
.PushBoxShadow(
4484 wr::ToLayoutRect(deviceBoxRect
), deviceClipRect
,
4485 !aFrame
->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect
),
4486 wr::ToLayoutVector2D(shadowOffset
),
4487 wr::ToColorF(ToDeviceColor(shadowColor
)), blurRadius
, spreadRadius
,
4488 borderRadius
, wr::BoxShadowClipMode::Inset
);
4492 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4493 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4494 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4495 nsDisplayListBuilder
* aDisplayListBuilder
) {
4496 if (!CanCreateWebRenderCommands(aDisplayListBuilder
, mFrame
,
4497 ToReferenceFrame())) {
4502 nsRect visible
= GetBounds(aDisplayListBuilder
, &snap
);
4503 nsPoint offset
= ToReferenceFrame();
4504 nsRect borderRect
= nsRect(offset
, mFrame
->GetSize());
4505 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4506 aBuilder
, aSc
, visible
, mFrame
, borderRect
);
4511 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder
* aBuilder
,
4512 nsIFrame
* aFrame
, nsDisplayList
* aList
)
4513 : nsDisplayWrapList(aBuilder
, aFrame
, aList
,
4514 aBuilder
->CurrentActiveScrolledRoot(), false) {}
4516 nsDisplayWrapList::nsDisplayWrapList(
4517 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
4518 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aClearClipChain
)
4519 : nsPaintedDisplayItem(aBuilder
, aFrame
, aActiveScrolledRoot
),
4521 mFrameActiveScrolledRoot(aBuilder
->CurrentActiveScrolledRoot()),
4523 mHasZIndexOverride(false),
4524 mClearingClipChain(aClearClipChain
) {
4525 MOZ_COUNT_CTOR(nsDisplayWrapList
);
4527 mBaseBuildingRect
= GetBuildingRect();
4530 mListPtr
->AppendToTop(aList
);
4531 mOriginalClipChain
= mClipChain
;
4532 nsDisplayWrapList::UpdateBounds(aBuilder
);
4535 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder
* aBuilder
,
4536 nsIFrame
* aFrame
, nsDisplayItem
* aItem
)
4537 : nsPaintedDisplayItem(aBuilder
, aFrame
,
4538 aBuilder
->CurrentActiveScrolledRoot()),
4541 mHasZIndexOverride(false) {
4542 MOZ_COUNT_CTOR(nsDisplayWrapList
);
4544 mBaseBuildingRect
= GetBuildingRect();
4547 mListPtr
->AppendToTop(aItem
);
4548 mOriginalClipChain
= mClipChain
;
4549 nsDisplayWrapList::UpdateBounds(aBuilder
);
4551 if (!aFrame
|| !aFrame
->IsTransformed()) {
4555 // See the previous nsDisplayWrapList constructor
4556 if (aItem
->Frame() == aFrame
) {
4557 mToReferenceFrame
= aItem
->ToReferenceFrame();
4560 nsRect visible
= aBuilder
->GetVisibleRect() +
4561 aBuilder
->GetCurrentFrameOffsetToReferenceFrame();
4563 SetBuildingRect(visible
);
4566 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList
); }
4568 void nsDisplayWrapList::HitTest(nsDisplayListBuilder
* aBuilder
,
4569 const nsRect
& aRect
, HitTestState
* aState
,
4570 nsTArray
<nsIFrame
*>* aOutFrames
) {
4571 mListPtr
->HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
4574 nsRect
nsDisplayWrapList::GetBounds(nsDisplayListBuilder
* aBuilder
,
4575 bool* aSnap
) const {
4580 nsRegion
nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
4581 bool* aSnap
) const {
4584 return ::mozilla::GetOpaqueRegion(aBuilder
, GetChildren(),
4585 GetBounds(aBuilder
, &snap
));
4588 Maybe
<nscolor
> nsDisplayWrapList::IsUniform(
4589 nsDisplayListBuilder
* aBuilder
) const {
4590 // We could try to do something but let's conservatively just return Nothing.
4594 void nsDisplayWrapper::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
4595 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4598 nsRect
nsDisplayWrapList::GetComponentAlphaBounds(
4599 nsDisplayListBuilder
* aBuilder
) const {
4600 return mListPtr
->GetComponentAlphaBounds(aBuilder
);
4603 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4604 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4605 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4606 nsDisplayListBuilder
* aDisplayListBuilder
, bool aNewClipList
) {
4607 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4608 GetChildren(), this, aDisplayListBuilder
, aSc
, aBuilder
, aResources
,
4613 static nsresult
WrapDisplayList(nsDisplayListBuilder
* aBuilder
,
4614 nsIFrame
* aFrame
, nsDisplayList
* aList
,
4615 nsDisplayItemWrapper
* aWrapper
) {
4616 if (!aList
->GetTop()) {
4619 nsDisplayItem
* item
= aWrapper
->WrapList(aBuilder
, aFrame
, aList
);
4621 return NS_ERROR_OUT_OF_MEMORY
;
4623 // aList was emptied
4624 aList
->AppendToTop(item
);
4628 static nsresult
WrapEachDisplayItem(nsDisplayListBuilder
* aBuilder
,
4629 nsDisplayList
* aList
,
4630 nsDisplayItemWrapper
* aWrapper
) {
4631 for (nsDisplayItem
* item
: aList
->TakeItems()) {
4632 item
= aWrapper
->WrapItem(aBuilder
, item
);
4634 return NS_ERROR_OUT_OF_MEMORY
;
4636 aList
->AppendToTop(item
);
4638 // aList was emptied
4642 nsresult
nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder
* aBuilder
,
4644 const nsDisplayListSet
& aIn
,
4645 const nsDisplayListSet
& aOut
) {
4646 nsresult rv
= WrapListsInPlace(aBuilder
, aFrame
, aIn
);
4647 NS_ENSURE_SUCCESS(rv
, rv
);
4649 if (&aOut
== &aIn
) {
4652 aOut
.BorderBackground()->AppendToTop(aIn
.BorderBackground());
4653 aOut
.BlockBorderBackgrounds()->AppendToTop(aIn
.BlockBorderBackgrounds());
4654 aOut
.Floats()->AppendToTop(aIn
.Floats());
4655 aOut
.Content()->AppendToTop(aIn
.Content());
4656 aOut
.PositionedDescendants()->AppendToTop(aIn
.PositionedDescendants());
4657 aOut
.Outlines()->AppendToTop(aIn
.Outlines());
4661 nsresult
nsDisplayItemWrapper::WrapListsInPlace(
4662 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
4663 const nsDisplayListSet
& aLists
) {
4665 if (WrapBorderBackground()) {
4666 // Our border-backgrounds are in-flow
4667 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.BorderBackground(), this);
4668 NS_ENSURE_SUCCESS(rv
, rv
);
4670 // Our block border-backgrounds are in-flow
4671 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.BlockBorderBackgrounds(), this);
4672 NS_ENSURE_SUCCESS(rv
, rv
);
4673 // The floats are not in flow
4674 rv
= WrapEachDisplayItem(aBuilder
, aLists
.Floats(), this);
4675 NS_ENSURE_SUCCESS(rv
, rv
);
4676 // Our child content is in flow
4677 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.Content(), this);
4678 NS_ENSURE_SUCCESS(rv
, rv
);
4679 // The positioned descendants may not be in-flow
4680 rv
= WrapEachDisplayItem(aBuilder
, aLists
.PositionedDescendants(), this);
4681 NS_ENSURE_SUCCESS(rv
, rv
);
4682 // The outlines may not be in-flow
4683 return WrapEachDisplayItem(aBuilder
, aLists
.Outlines(), this);
4686 nsDisplayOpacity::nsDisplayOpacity(
4687 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
4688 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aForEventsOnly
,
4689 bool aNeedsActiveLayer
, bool aWrapsBackdropFilter
)
4690 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
4691 mOpacity(aFrame
->StyleEffects()->mOpacity
),
4692 mForEventsOnly(aForEventsOnly
),
4693 mNeedsActiveLayer(aNeedsActiveLayer
),
4694 mChildOpacityState(ChildOpacityState::Unknown
),
4695 mWrapsBackdropFilter(aWrapsBackdropFilter
) {
4696 MOZ_COUNT_CTOR(nsDisplayOpacity
);
4699 void nsDisplayOpacity::HitTest(nsDisplayListBuilder
* aBuilder
,
4700 const nsRect
& aRect
,
4701 nsDisplayItem::HitTestState
* aState
,
4702 nsTArray
<nsIFrame
*>* aOutFrames
) {
4703 AutoRestore
<float> opacity(aState
->mCurrentOpacity
);
4704 aState
->mCurrentOpacity
*= mOpacity
;
4706 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4707 // only consider fully opaque items? Or make this configurable somehow?
4708 if (aBuilder
->HitTestIsForVisibility() && mOpacity
== 0.0f
) {
4711 nsDisplayWrapList::HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
4714 nsRegion
nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
4715 bool* aSnap
) const {
4717 // The only time where mOpacity == 1.0 should be when we have will-change.
4718 // We could report this as opaque then but when the will-change value starts
4719 // animating the element would become non opaque and could cause repaints.
4723 void nsDisplayOpacity::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
4724 if (GetOpacity() == 0.0f
) {
4728 if (GetOpacity() == 1.0f
) {
4729 GetChildren()->Paint(aBuilder
, aCtx
,
4730 mFrame
->PresContext()->AppUnitsPerDevPixel());
4734 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4736 aCtx
->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4737 GetChildren()->Paint(aBuilder
, aCtx
,
4738 mFrame
->PresContext()->AppUnitsPerDevPixel());
4739 aCtx
->GetDrawTarget()->PopLayer();
4743 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder
* aBuilder
,
4745 return EffectCompositor::HasAnimationsForCompositor(
4746 aFrame
, DisplayItemType::TYPE_OPACITY
) ||
4747 (ActiveLayerTracker::IsStyleAnimated(
4748 aBuilder
, aFrame
, nsCSSPropertyIDSet::OpacityProperties()));
4751 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager
* aManager
,
4752 nsDisplayListBuilder
* aBuilder
) const {
4753 return !EffectCompositor::HasAnimationsForCompositor(
4754 mFrame
, DisplayItemType::TYPE_OPACITY
);
4757 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4758 // children that don't overlap and can all apply the opacity to themselves.
4759 static const size_t kOpacityMaxChildCount
= 3;
4761 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4762 // are likely have more child items than |kOpacityMaxChildCount|.
4763 static const size_t kOpacityMaxListSize
= kOpacityMaxChildCount
* 2;
4766 * Recursively iterates through |aList| and collects at most
4767 * |kOpacityMaxChildCount| display item pointers to items that return true for
4768 * CanApplyOpacity(). The item pointers are added to |aArray|.
4770 * LayerEventRegions and WrapList items are ignored.
4772 * We need to do this recursively, because the child display items might contain
4773 * nested nsDisplayWrapLists.
4775 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4776 * item that returns false for CanApplyOpacity() is encountered.
4777 * Otherwise returns true.
4779 static bool CollectItemsWithOpacity(WebRenderLayerManager
* aManager
,
4780 nsDisplayListBuilder
* aBuilder
,
4781 nsDisplayList
* aList
,
4782 nsTArray
<nsPaintedDisplayItem
*>& aArray
) {
4783 if (aList
->Length() > kOpacityMaxListSize
) {
4784 // Exit early, since |aList| will likely contain more than
4785 // |kOpacityMaxChildCount| items.
4789 for (nsDisplayItem
* i
: *aList
) {
4790 const DisplayItemType type
= i
->GetType();
4792 if (type
== DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
) {
4796 // Descend only into wraplists.
4797 if (type
== DisplayItemType::TYPE_WRAP_LIST
||
4798 type
== DisplayItemType::TYPE_CONTAINER
) {
4799 // The current display item has children, process them first.
4800 if (!CollectItemsWithOpacity(aManager
, aBuilder
, i
->GetChildren(),
4808 if (aArray
.Length() == kOpacityMaxChildCount
) {
4812 auto* item
= i
->AsPaintedDisplayItem();
4813 if (!item
|| !item
->CanApplyOpacity(aManager
, aBuilder
)) {
4817 aArray
.AppendElement(item
);
4823 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager
* aManager
,
4824 nsDisplayListBuilder
* aBuilder
) {
4825 if (mChildOpacityState
== ChildOpacityState::Deferred
) {
4829 // Iterate through the child display list and copy at most
4830 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4831 AutoTArray
<nsPaintedDisplayItem
*, kOpacityMaxChildCount
> items
;
4832 if (!CollectItemsWithOpacity(aManager
, aBuilder
, &mList
, items
)) {
4833 mChildOpacityState
= ChildOpacityState::Deferred
;
4838 nsPaintedDisplayItem
* item
{};
4840 } children
[kOpacityMaxChildCount
];
4843 size_t childCount
= 0;
4844 for (nsPaintedDisplayItem
* item
: items
) {
4845 children
[childCount
].item
= item
;
4846 children
[childCount
].bounds
= item
->GetBounds(aBuilder
, &snap
);
4850 for (size_t i
= 0; i
< childCount
; i
++) {
4851 for (size_t j
= i
+ 1; j
< childCount
; j
++) {
4852 if (children
[i
].bounds
.Intersects(children
[j
].bounds
)) {
4853 mChildOpacityState
= ChildOpacityState::Deferred
;
4859 mChildOpacityState
= ChildOpacityState::Applied
;
4864 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4865 * that has the same frame as the opacity item, and that supports painting with
4866 * opacity. In this case the opacity item can be optimized away.
4868 bool nsDisplayOpacity::ApplyToMask() {
4869 if (mList
.Length() != 1) {
4873 nsDisplayItem
* item
= mList
.GetBottom();
4874 if (item
->Frame() != mFrame
) {
4875 // The effect item needs to have the same frame as the opacity item.
4879 const DisplayItemType type
= item
->GetType();
4880 if (type
== DisplayItemType::TYPE_MASK
) {
4887 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4888 WebRenderLayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
,
4889 float aInheritedOpacity
) {
4890 if (mFrame
->GetPrevContinuation() || mFrame
->GetNextContinuation() ||
4891 mFrame
->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT
)) {
4892 // If we've been split, then we might need to merge, so
4893 // don't flatten us away.
4897 if (mNeedsActiveLayer
|| mOpacity
== 0.0) {
4898 // If our opacity is zero then we'll discard all descendant display items
4899 // except for layer event regions, so there's no point in doing this
4900 // optimization (and if we do do it, then invalidations of those descendants
4901 // might trigger repainting).
4905 if (mList
.IsEmpty()) {
4909 // We can only flatten opacity items into a mask if we haven't
4910 // already flattened an earlier ancestor, since the SVG code pulls the opacity
4911 // from style directly, and won't know about the outer opacity value.
4912 if (aInheritedOpacity
== 1.0f
&& ApplyToMask()) {
4913 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame
));
4914 mChildOpacityState
= ChildOpacityState::Applied
;
4918 // Return true if we successfully applied opacity to child items.
4919 return CanApplyToChildren(aManager
, aBuilder
);
4922 void nsDisplayOpacity::ComputeInvalidationRegion(
4923 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
4924 nsRegion
* aInvalidRegion
) const {
4925 const auto* geometry
=
4926 static_cast<const nsDisplayOpacityGeometry
*>(aGeometry
);
4929 if (mOpacity
!= geometry
->mOpacity
) {
4930 aInvalidRegion
->Or(GetBounds(aBuilder
, &snap
), geometry
->mBounds
);
4934 void nsDisplayOpacity::WriteDebugInfo(std::stringstream
& aStream
) {
4935 aStream
<< " (opacity " << mOpacity
<< ", mChildOpacityState: ";
4936 switch (mChildOpacityState
) {
4937 case ChildOpacityState::Unknown
:
4938 aStream
<< "Unknown";
4940 case ChildOpacityState::Applied
:
4941 aStream
<< "Applied";
4943 case ChildOpacityState::Deferred
:
4944 aStream
<< "Deferred";
4953 bool nsDisplayOpacity::CreateWebRenderCommands(
4954 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4955 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4956 nsDisplayListBuilder
* aDisplayListBuilder
) {
4957 MOZ_ASSERT(mChildOpacityState
!= ChildOpacityState::Applied
);
4958 float oldOpacity
= aBuilder
.GetInheritedOpacity();
4959 const DisplayItemClipChain
* oldClipChain
= aBuilder
.GetInheritedClipChain();
4960 aBuilder
.SetInheritedOpacity(1.0f
);
4961 aBuilder
.SetInheritedClipChain(nullptr);
4962 float opacity
= mOpacity
* oldOpacity
;
4963 float* opacityForSC
= &opacity
;
4965 uint64_t animationsId
=
4966 AddAnimationsForWebRender(this, aManager
, aDisplayListBuilder
);
4967 wr::WrAnimationProperty prop
{
4968 wr::WrAnimationType::Opacity
,
4972 wr::StackingContextParams params
;
4973 params
.animation
= animationsId
? &prop
: nullptr;
4974 params
.opacity
= opacityForSC
;
4976 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
4977 if (mWrapsBackdropFilter
) {
4978 params
.flags
|= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER
;
4980 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
4983 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4984 &mList
, this, aDisplayListBuilder
, sc
, aBuilder
, aResources
);
4985 aBuilder
.SetInheritedOpacity(oldOpacity
);
4986 aBuilder
.SetInheritedClipChain(oldClipChain
);
4990 nsDisplayBlendMode::nsDisplayBlendMode(
4991 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
4992 StyleBlend aBlendMode
, const ActiveScrolledRoot
* aActiveScrolledRoot
,
4993 const bool aIsForBackground
)
4994 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
4995 mBlendMode(aBlendMode
),
4996 mIsForBackground(aIsForBackground
) {
4997 MOZ_COUNT_CTOR(nsDisplayBlendMode
);
5000 nsRegion
nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
5001 bool* aSnap
) const {
5003 // We are never considered opaque
5007 bool nsDisplayBlendMode::CreateWebRenderCommands(
5008 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5009 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5010 nsDisplayListBuilder
* aDisplayListBuilder
) {
5011 wr::StackingContextParams params
;
5012 params
.mix_blend_mode
=
5013 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode
));
5015 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
5016 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
5019 return nsDisplayWrapList::CreateWebRenderCommands(
5020 aBuilder
, aResources
, sc
, aManager
, aDisplayListBuilder
);
5023 void nsDisplayBlendMode::Paint(nsDisplayListBuilder
* aBuilder
,
5025 // This should be switched to use PushLayerWithBlend, once it's
5026 // been implemented for all DrawTarget backends.
5027 DrawTarget
* dt
= aCtx
->GetDrawTarget();
5028 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
5029 Rect rect
= NSRectToRect(GetPaintRect(aBuilder
, aCtx
), appUnitsPerDevPixel
);
5032 // Create a temporary DrawTarget that is clipped to the area that
5033 // we're going to draw to. This will include the same transform as
5034 // is currently on |dt|.
5035 RefPtr
<DrawTarget
> temp
=
5036 dt
->CreateClippedDrawTarget(rect
, SurfaceFormat::B8G8R8A8
);
5041 gfxContext
ctx(temp
, /* aPreserveTransform */ true);
5043 GetChildren()->Paint(aBuilder
, &ctx
,
5044 mFrame
->PresContext()->AppUnitsPerDevPixel());
5046 // Draw the temporary DT to the real destination, applying the blend mode, but
5049 RefPtr
<SourceSurface
> surface
= temp
->Snapshot();
5050 gfxContextMatrixAutoSaveRestore
saveMatrix(aCtx
);
5051 dt
->SetTransform(Matrix());
5053 surface
, Rect(surface
->GetRect()), Rect(surface
->GetRect()),
5054 DrawSurfaceOptions(),
5055 DrawOptions(1.0f
, nsCSSRendering::GetGFXBlendMode(mBlendMode
)));
5058 gfx::CompositionOp
nsDisplayBlendMode::BlendMode() {
5059 return nsCSSRendering::GetGFXBlendMode(mBlendMode
);
5062 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem
* aItem
) const {
5063 // Items for the same content element should be merged into a single
5064 // compositing group.
5065 if (!HasDifferentFrame(aItem
) || !HasSameTypeAndClip(aItem
) ||
5066 !HasSameContent(aItem
)) {
5070 const auto* item
= static_cast<const nsDisplayBlendMode
*>(aItem
);
5071 if (mIsForBackground
|| item
->mIsForBackground
) {
5072 // Don't merge background-blend-mode items
5080 nsDisplayBlendContainer
* nsDisplayBlendContainer::CreateForMixBlendMode(
5081 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5082 const ActiveScrolledRoot
* aActiveScrolledRoot
) {
5083 return MakeDisplayItem
<nsDisplayBlendContainer
>(aBuilder
, aFrame
, aList
,
5084 aActiveScrolledRoot
, false);
5088 nsDisplayBlendContainer
* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5089 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
5090 nsDisplayList
* aList
, const ActiveScrolledRoot
* aActiveScrolledRoot
) {
5091 if (aSecondaryFrame
) {
5092 auto type
= GetTableTypeFromFrame(aFrame
);
5093 auto index
= static_cast<uint16_t>(type
);
5095 return MakeDisplayItemWithIndex
<nsDisplayTableBlendContainer
>(
5096 aBuilder
, aSecondaryFrame
, index
, aList
, aActiveScrolledRoot
, true,
5100 return MakeDisplayItemWithIndex
<nsDisplayBlendContainer
>(
5101 aBuilder
, aFrame
, 1, aList
, aActiveScrolledRoot
, true);
5104 nsDisplayBlendContainer::nsDisplayBlendContainer(
5105 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5106 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aIsForBackground
)
5107 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
5108 mIsForBackground(aIsForBackground
) {
5109 MOZ_COUNT_CTOR(nsDisplayBlendContainer
);
5112 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder
* aBuilder
,
5114 aCtx
->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5115 GetChildren()->Paint(aBuilder
, aCtx
,
5116 mFrame
->PresContext()->AppUnitsPerDevPixel());
5117 aCtx
->GetDrawTarget()->PopLayer();
5120 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5121 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5122 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5123 nsDisplayListBuilder
* aDisplayListBuilder
) {
5124 wr::StackingContextParams params
;
5125 params
.flags
|= wr::StackingContextFlags::IS_BLEND_CONTAINER
;
5127 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
5128 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
5131 return nsDisplayWrapList::CreateWebRenderCommands(
5132 aBuilder
, aResources
, sc
, aManager
, aDisplayListBuilder
);
5135 nsDisplayOwnLayer::nsDisplayOwnLayer(
5136 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5137 const ActiveScrolledRoot
* aActiveScrolledRoot
,
5138 nsDisplayOwnLayerFlags aFlags
, const ScrollbarData
& aScrollbarData
,
5139 bool aForceActive
, bool aClearClipChain
)
5140 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
,
5143 mScrollbarData(aScrollbarData
),
5144 mForceActive(aForceActive
),
5146 MOZ_COUNT_CTOR(nsDisplayOwnLayer
);
5149 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5150 return mScrollbarData
.mScrollbarLayerType
== ScrollbarLayerType::Thumb
;
5153 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5154 return mScrollbarData
.mScrollbarLayerType
== ScrollbarLayerType::Container
;
5157 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5158 return IsScrollbarContainer() && IsScrollbarLayerForRoot();
5161 bool nsDisplayOwnLayer::IsScrollbarLayerForRoot() const {
5162 return mFrame
->PresContext()->IsRootContentDocumentCrossProcess() &&
5163 mScrollbarData
.mTargetViewId
==
5164 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame
->PresContext());
5167 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5168 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM
;
5171 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5172 return GetType() == DisplayItemType::TYPE_FIXED_POSITION
;
5175 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5176 return GetType() == DisplayItemType::TYPE_STICKY_POSITION
;
5179 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5180 if (!mFrame
->PresContext()->IsRootContentDocumentCrossProcess()) {
5183 return mFrame
->PresContext()->HasDynamicToolbar() ||
5184 // For tests on Android, this pref is set to simulate the dynamic
5186 StaticPrefs::apz_fixed_margin_override_enabled();
5189 bool nsDisplayOwnLayer::ShouldFixedAndStickyContentGetAnimationIds() const {
5190 #if defined(MOZ_WIDGET_ANDROID)
5191 return mFrame
->PresContext()->IsRootContentDocumentCrossProcess();
5197 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5198 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5199 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5200 nsDisplayListBuilder
* aDisplayListBuilder
) {
5201 Maybe
<wr::WrAnimationProperty
> prop
;
5202 bool needsProp
= aManager
->LayerManager()->AsyncPanZoomEnabled() &&
5203 (IsScrollThumbLayer() || IsZoomingLayer() ||
5204 (IsFixedPositionLayer() &&
5205 ShouldFixedAndStickyContentGetAnimationIds()) ||
5206 (IsStickyPositionLayer() &&
5207 ShouldFixedAndStickyContentGetAnimationIds()) ||
5208 (IsRootScrollbarContainer() && HasDynamicToolbar()));
5211 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5212 // to create and set an animation id. That way APZ can adjust the position/
5213 // zoom of this content asynchronously as needed.
5214 RefPtr
<WebRenderAPZAnimationData
> animationData
=
5215 aManager
->CommandBuilder()
5216 .CreateOrRecycleWebRenderUserData
<WebRenderAPZAnimationData
>(this);
5217 mWrAnimationId
= animationData
->GetAnimationId();
5220 prop
->id
= mWrAnimationId
;
5221 prop
->key
= wr::SpatialKey(uint64_t(mFrame
), GetPerFrameKey(),
5222 wr::SpatialKeyKind::APZ
);
5223 prop
->effect_type
= wr::WrAnimationType::Transform
;
5226 wr::StackingContextParams params
;
5227 params
.animation
= prop
.ptrOr(nullptr);
5229 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
5230 if (IsScrollbarContainer() && IsRootScrollbarContainer()) {
5231 params
.prim_flags
|= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER
;
5233 if (IsZoomingLayer() ||
5234 ((IsFixedPositionLayer() &&
5235 ShouldFixedAndStickyContentGetAnimationIds()) ||
5236 (IsStickyPositionLayer() &&
5237 ShouldFixedAndStickyContentGetAnimationIds()) ||
5238 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5239 params
.is_2d_scale_translation
= true;
5240 params
.should_snap
= true;
5243 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
5246 nsDisplayWrapList::CreateWebRenderCommands(aBuilder
, aResources
, sc
, aManager
,
5247 aDisplayListBuilder
);
5251 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData
* aData
,
5252 WebRenderLayerScrollData
* aLayerData
) {
5253 bool isRelevantToApz
=
5254 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5255 (IsFixedPositionLayer() &&
5256 ShouldFixedAndStickyContentGetAnimationIds()) ||
5257 (IsStickyPositionLayer() &&
5258 ShouldFixedAndStickyContentGetAnimationIds()));
5260 if (!isRelevantToApz
) {
5268 if (IsZoomingLayer()) {
5269 aLayerData
->SetZoomAnimationId(mWrAnimationId
);
5273 if (IsFixedPositionLayer() && ShouldFixedAndStickyContentGetAnimationIds()) {
5274 aLayerData
->SetFixedPositionAnimationId(mWrAnimationId
);
5278 if (IsStickyPositionLayer() && ShouldFixedAndStickyContentGetAnimationIds()) {
5279 aLayerData
->SetStickyPositionAnimationId(mWrAnimationId
);
5283 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5285 aLayerData
->SetScrollbarData(mScrollbarData
);
5287 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5288 aLayerData
->SetScrollbarAnimationId(mWrAnimationId
);
5292 if (IsScrollThumbLayer()) {
5293 aLayerData
->SetScrollbarAnimationId(mWrAnimationId
);
5294 LayoutDeviceRect bounds
= LayoutDeviceIntRect::FromAppUnits(
5295 mBounds
, mFrame
->PresContext()->AppUnitsPerDevPixel());
5296 // Subframe scrollbars are subject to the pinch-zoom scale,
5297 // but root scrollbars are not because they are outside of the
5298 // region that is zoomed.
5299 const float resolution
=
5300 IsScrollbarLayerForRoot()
5302 : mFrame
->PresContext()->PresShell()->GetCumulativeResolution();
5303 LayerIntRect layerBounds
=
5304 RoundedOut(bounds
* LayoutDeviceToLayerScale(resolution
));
5305 aLayerData
->SetVisibleRegion(LayerIntRegion(layerBounds
));
5310 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream
& aStream
) {
5311 aStream
<< nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64
")",
5312 (int)mFlags
, mScrollbarData
.mTargetViewId
)
5316 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder
* aBuilder
,
5318 nsSubDocumentFrame
* aSubDocFrame
,
5319 nsDisplayList
* aList
,
5320 nsDisplayOwnLayerFlags aFlags
)
5321 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
,
5322 aBuilder
->CurrentActiveScrolledRoot(), aFlags
),
5323 mScrollParentId(aBuilder
->GetCurrentScrollParentId()),
5324 mShouldFlatten(false),
5325 mSubDocFrame(aSubDocFrame
) {
5326 MOZ_COUNT_CTOR(nsDisplaySubDocument
);
5328 if (mSubDocFrame
&& mSubDocFrame
!= mFrame
) {
5329 mSubDocFrame
->AddDisplayItem(this);
5333 nsDisplaySubDocument::~nsDisplaySubDocument() {
5334 MOZ_COUNT_DTOR(nsDisplaySubDocument
);
5336 mSubDocFrame
->RemoveDisplayItem(this);
5340 nsIFrame
* nsDisplaySubDocument::FrameForInvalidation() const {
5341 return mSubDocFrame
? mSubDocFrame
: mFrame
;
5344 void nsDisplaySubDocument::RemoveFrame(nsIFrame
* aFrame
) {
5345 if (aFrame
== mSubDocFrame
) {
5346 mSubDocFrame
= nullptr;
5349 nsDisplayOwnLayer::RemoveFrame(aFrame
);
5352 static bool UseDisplayPortForViewport(nsDisplayListBuilder
* aBuilder
,
5354 return aBuilder
->IsPaintingToWindow() &&
5355 DisplayPortUtils::ViewportHasDisplayPort(aFrame
->PresContext());
5358 nsRect
nsDisplaySubDocument::GetBounds(nsDisplayListBuilder
* aBuilder
,
5359 bool* aSnap
) const {
5360 bool usingDisplayPort
= UseDisplayPortForViewport(aBuilder
, mFrame
);
5362 if ((mFlags
& nsDisplayOwnLayerFlags::GenerateScrollableLayer
) &&
5365 return mFrame
->GetRect() + aBuilder
->ToReferenceFrame(mFrame
);
5368 return nsDisplayOwnLayer::GetBounds(aBuilder
, aSnap
);
5371 nsRegion
nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
5372 bool* aSnap
) const {
5373 bool usingDisplayPort
= UseDisplayPortForViewport(aBuilder
, mFrame
);
5375 if ((mFlags
& nsDisplayOwnLayerFlags::GenerateScrollableLayer
) &&
5381 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder
, aSnap
);
5385 nsDisplayFixedPosition
* nsDisplayFixedPosition::CreateForFixedBackground(
5386 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
5387 nsDisplayBackgroundImage
* aImage
, const uint16_t aIndex
,
5388 const ActiveScrolledRoot
* aScrollTargetASR
) {
5389 nsDisplayList
temp(aBuilder
);
5390 temp
.AppendToTop(aImage
);
5392 if (aSecondaryFrame
) {
5393 auto tableType
= GetTableTypeFromFrame(aFrame
);
5394 const uint16_t index
= CalculateTablePerFrameKey(aIndex
+ 1, tableType
);
5395 return MakeDisplayItemWithIndex
<nsDisplayTableFixedPosition
>(
5396 aBuilder
, aSecondaryFrame
, index
, &temp
, aFrame
, aScrollTargetASR
);
5399 return MakeDisplayItemWithIndex
<nsDisplayFixedPosition
>(
5400 aBuilder
, aFrame
, aIndex
+ 1, &temp
, aScrollTargetASR
);
5403 nsDisplayFixedPosition::nsDisplayFixedPosition(
5404 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5405 const ActiveScrolledRoot
* aActiveScrolledRoot
,
5406 const ActiveScrolledRoot
* aScrollTargetASR
)
5407 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
),
5408 mScrollTargetASR(aScrollTargetASR
),
5409 mIsFixedBackground(false) {
5410 MOZ_COUNT_CTOR(nsDisplayFixedPosition
);
5413 nsDisplayFixedPosition::nsDisplayFixedPosition(
5414 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5415 const ActiveScrolledRoot
* aScrollTargetASR
)
5416 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
,
5417 aBuilder
->CurrentActiveScrolledRoot()),
5418 // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5419 mScrollTargetASR(aScrollTargetASR
),
5420 mIsFixedBackground(true) {
5421 MOZ_COUNT_CTOR(nsDisplayFixedPosition
);
5424 ScrollableLayerGuid::ViewID
nsDisplayFixedPosition::GetScrollTargetId() {
5425 if (mScrollTargetASR
&&
5426 (mIsFixedBackground
|| !nsLayoutUtils::IsReallyFixedPos(mFrame
))) {
5427 return mScrollTargetASR
->GetViewId();
5429 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame
->PresContext());
5432 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5433 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5434 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5435 nsDisplayListBuilder
* aDisplayListBuilder
) {
5436 SideBits sides
= SideBits::eNone
;
5437 if (!mIsFixedBackground
) {
5438 sides
= nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame
);
5441 // We install this RAII scrolltarget tracker so that any
5442 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5443 // share the same ASR as this item) use the correct scroll target. That way
5444 // attempts to scroll on those items will scroll the root scroll frame.
5445 wr::DisplayListBuilder::FixedPosScrollTargetTracker
tracker(
5446 aBuilder
, GetActiveScrolledRoot(), GetScrollTargetId(), sides
);
5447 return nsDisplayOwnLayer::CreateWebRenderCommands(
5448 aBuilder
, aResources
, aSc
, aManager
, aDisplayListBuilder
);
5451 bool nsDisplayFixedPosition::UpdateScrollData(
5452 WebRenderScrollData
* aData
, WebRenderLayerScrollData
* aLayerData
) {
5454 if (!mIsFixedBackground
) {
5455 aLayerData
->SetFixedPositionSides(
5456 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame
));
5458 aLayerData
->SetFixedPositionScrollContainerId(GetScrollTargetId());
5460 nsDisplayOwnLayer::UpdateScrollData(aData
, aLayerData
);
5464 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream
& aStream
) {
5465 aStream
<< nsPrintfCString(
5466 " (containerASR %s) (scrolltarget %" PRIu64
")",
5467 ActiveScrolledRoot::ToString(mScrollTargetASR
).get(),
5468 GetScrollTargetId())
5472 TableType
GetTableTypeFromFrame(nsIFrame
* aFrame
) {
5473 if (aFrame
->IsTableFrame()) {
5474 return TableType::Table
;
5477 if (aFrame
->IsTableColFrame()) {
5478 return TableType::TableCol
;
5481 if (aFrame
->IsTableColGroupFrame()) {
5482 return TableType::TableColGroup
;
5485 if (aFrame
->IsTableRowFrame()) {
5486 return TableType::TableRow
;
5489 if (aFrame
->IsTableRowGroupFrame()) {
5490 return TableType::TableRowGroup
;
5493 if (aFrame
->IsTableCellFrame()) {
5494 return TableType::TableCell
;
5497 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5498 return TableType::Table
;
5501 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5502 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5503 nsIFrame
* aAncestorFrame
, const ActiveScrolledRoot
* aScrollTargetASR
)
5504 : nsDisplayFixedPosition(aBuilder
, aFrame
, aList
, aScrollTargetASR
),
5505 mAncestorFrame(aAncestorFrame
) {
5506 if (aBuilder
->IsRetainingDisplayList()) {
5507 mAncestorFrame
->AddDisplayItem(this);
5511 nsDisplayStickyPosition::nsDisplayStickyPosition(
5512 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5513 const ActiveScrolledRoot
* aActiveScrolledRoot
,
5514 const ActiveScrolledRoot
* aContainerASR
, bool aClippedToDisplayPort
)
5515 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
),
5516 mContainerASR(aContainerASR
),
5517 mClippedToDisplayPort(aClippedToDisplayPort
),
5518 mShouldFlatten(false) {
5519 MOZ_COUNT_CTOR(nsDisplayStickyPosition
);
5522 // Returns the smallest distance from "0" to the range [min, max] where
5523 // min <= max. Despite the name, the return value is actually a 1-D vector,
5524 // and so may be negative if max < 0.
5525 static nscoord
DistanceToRange(nscoord min
, nscoord max
) {
5526 MOZ_ASSERT(min
<= max
);
5533 MOZ_ASSERT(min
<= 0 && max
>= 0);
5537 // Returns the magnitude of the part of the range [min, max] that is greater
5538 // than zero. The return value is always non-negative.
5539 static nscoord
PositivePart(nscoord min
, nscoord max
) {
5540 MOZ_ASSERT(min
<= max
);
5550 // Returns the magnitude of the part of the range [min, max] that is less
5551 // than zero. The return value is always non-negative.
5552 static nscoord
NegativePart(nscoord min
, nscoord max
) {
5553 MOZ_ASSERT(min
<= max
);
5563 StickyScrollContainer
* nsDisplayStickyPosition::GetStickyScrollContainer() {
5564 StickyScrollContainer
* stickyScrollContainer
=
5565 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame
);
5566 if (stickyScrollContainer
) {
5567 // If there's no ASR for the scrollframe that this sticky item is attached
5568 // to, then don't create a WR sticky item for it either. Trying to do so
5569 // will end in sadness because WR will interpret some coordinates as
5570 // relative to the nearest enclosing scrollframe, which will correspond
5571 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5572 // same as the scrollframe this sticky item is actually supposed to be
5573 // attached to, thus the sadness.
5574 // Not sending WR the sticky item is ok, because the enclosing scrollframe
5575 // will never be asynchronously scrolled. Instead we will always position
5576 // the sticky items correctly on the gecko side and WR will never need to
5577 // adjust their position itself.
5579 stickyScrollContainer
->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5580 if (!stickyScrollContainer
->ScrollFrame()
5581 ->IsMaybeAsynchronouslyScrolled()) {
5582 stickyScrollContainer
= nullptr;
5585 return stickyScrollContainer
;
5588 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5589 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5590 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5591 nsDisplayListBuilder
* aDisplayListBuilder
) {
5592 StickyScrollContainer
* stickyScrollContainer
= GetStickyScrollContainer();
5594 Maybe
<wr::SpaceAndClipChainHelper
> saccHelper
;
5596 if (stickyScrollContainer
) {
5597 float auPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
5600 nsRect itemBounds
= GetBounds(aDisplayListBuilder
, &snap
);
5602 Maybe
<float> topMargin
;
5603 Maybe
<float> rightMargin
;
5604 Maybe
<float> bottomMargin
;
5605 Maybe
<float> leftMargin
;
5606 wr::StickyOffsetBounds vBounds
= {0.0, 0.0};
5607 wr::StickyOffsetBounds hBounds
= {0.0, 0.0};
5608 nsPoint appliedOffset
;
5610 nsRectAbsolute outer
;
5611 nsRectAbsolute inner
;
5612 stickyScrollContainer
->GetScrollRanges(mFrame
, &outer
, &inner
);
5614 nsIFrame
* scrollFrame
= do_QueryFrame(stickyScrollContainer
->ScrollFrame());
5616 scrollFrame
->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5618 // Adjust the scrollPort coordinates to be relative to the reference frame,
5619 // so that it is in the same space as everything else.
5621 stickyScrollContainer
->ScrollFrame()->GetScrollPortRect();
5622 scrollPort
+= offset
;
5624 // The following computations make more sense upon understanding the
5625 // semantics of "inner" and "outer", which is explained in the comment on
5626 // SetStickyPositionData in Layers.h.
5628 if (outer
.YMost() != inner
.YMost()) {
5629 // Question: How far will itemBounds.y be from the top of the scrollport
5630 // when we have scrolled from the current scroll position of "0" to
5631 // reach the range [inner.YMost(), outer.YMost()] where the item gets
5633 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5634 // needs to be adjusted by the distance to the range, less any other
5635 // sticky ranges that fall between 0 and the range. If the distance is
5636 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5637 // scrolling upwards (decreasing scroll offset) to reach that range,
5638 // which would increase itemBounds.y and make it farther away from the
5639 // top of the scrollport. So in that case the adjustment is -distance.
5640 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5641 // we would be scrolling downwards, itemBounds.y would decrease, and we
5642 // again need to adjust by -distance. If we are already in the range
5643 // then no adjustment is needed and distance is 0 so again using
5644 // -distance works. If the distance is positive, and the item has both
5645 // top and bottom sticky ranges, then the bottom sticky range may fall
5646 // (entirely[1] or partly[2]) between the current scroll position.
5647 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5648 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5649 // In these cases, the item doesn't actually move for that part of the
5650 // distance, so we need to subtract out that bit, which can be computed
5651 // as the positive portion of the range [outer.Y(), inner.Y()].
5652 nscoord distance
= DistanceToRange(inner
.YMost(), outer
.YMost());
5654 distance
-= PositivePart(outer
.Y(), inner
.Y());
5656 topMargin
= Some(NSAppUnitsToFloatPixels(
5657 itemBounds
.y
- scrollPort
.y
- distance
, auPerDevPixel
));
5658 // Question: What is the maximum positive ("downward") offset that WR
5659 // will have to apply to this item in order to prevent the item from
5661 // Answer: Since the item is "sticky" in the range [inner.YMost(),
5662 // outer.YMost()], the maximum offset will be the size of the range, which
5663 // is outer.YMost() - inner.YMost().
5665 NSAppUnitsToFloatPixels(outer
.YMost() - inner
.YMost(), auPerDevPixel
);
5666 // Question: how much of an offset has layout already applied to the item?
5667 // Answer: if we are
5668 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5669 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5670 // then layout has already applied some offset to the position of the
5671 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5672 // and |outer.YMost() - inner.YMost()| in case (b).
5673 if (inner
.YMost() < 0) {
5674 appliedOffset
.y
= std::min(0, outer
.YMost()) - inner
.YMost();
5675 MOZ_ASSERT(appliedOffset
.y
> 0);
5678 if (outer
.Y() != inner
.Y()) {
5679 // Similar logic as in the previous section, but this time we care about
5680 // the distance from itemBounds.YMost() to scrollPort.YMost().
5681 nscoord distance
= DistanceToRange(outer
.Y(), inner
.Y());
5683 distance
+= NegativePart(inner
.YMost(), outer
.YMost());
5685 bottomMargin
= Some(NSAppUnitsToFloatPixels(
5686 scrollPort
.YMost() - itemBounds
.YMost() + distance
, auPerDevPixel
));
5687 // And here WR will be moving the item upwards rather than downwards so
5688 // again things are inverted from the previous block.
5690 NSAppUnitsToFloatPixels(outer
.Y() - inner
.Y(), auPerDevPixel
);
5691 // We can't have appliedOffset be both positive and negative, and the top
5692 // adjustment takes priority. So here we only update appliedOffset.y if
5693 // it wasn't set by the top-sticky case above.
5694 if (appliedOffset
.y
== 0 && inner
.Y() > 0) {
5695 appliedOffset
.y
= std::max(0, outer
.Y()) - inner
.Y();
5696 MOZ_ASSERT(appliedOffset
.y
< 0);
5699 // Same as above, but for the x-axis
5700 if (outer
.XMost() != inner
.XMost()) {
5701 nscoord distance
= DistanceToRange(inner
.XMost(), outer
.XMost());
5703 distance
-= PositivePart(outer
.X(), inner
.X());
5705 leftMargin
= Some(NSAppUnitsToFloatPixels(
5706 itemBounds
.x
- scrollPort
.x
- distance
, auPerDevPixel
));
5708 NSAppUnitsToFloatPixels(outer
.XMost() - inner
.XMost(), auPerDevPixel
);
5709 if (inner
.XMost() < 0) {
5710 appliedOffset
.x
= std::min(0, outer
.XMost()) - inner
.XMost();
5711 MOZ_ASSERT(appliedOffset
.x
> 0);
5714 if (outer
.X() != inner
.X()) {
5715 nscoord distance
= DistanceToRange(outer
.X(), inner
.X());
5717 distance
+= NegativePart(inner
.XMost(), outer
.XMost());
5719 rightMargin
= Some(NSAppUnitsToFloatPixels(
5720 scrollPort
.XMost() - itemBounds
.XMost() + distance
, auPerDevPixel
));
5722 NSAppUnitsToFloatPixels(outer
.X() - inner
.X(), auPerDevPixel
);
5723 if (appliedOffset
.x
== 0 && inner
.X() > 0) {
5724 appliedOffset
.x
= std::max(0, outer
.X()) - inner
.X();
5725 MOZ_ASSERT(appliedOffset
.x
< 0);
5729 LayoutDeviceRect bounds
=
5730 LayoutDeviceRect::FromAppUnits(itemBounds
, auPerDevPixel
);
5731 wr::LayoutVector2D applied
= {
5732 NSAppUnitsToFloatPixels(appliedOffset
.x
, auPerDevPixel
),
5733 NSAppUnitsToFloatPixels(appliedOffset
.y
, auPerDevPixel
)};
5734 wr::WrSpatialId spatialId
= aBuilder
.DefineStickyFrame(
5735 wr::ToLayoutRect(bounds
), topMargin
.ptrOr(nullptr),
5736 rightMargin
.ptrOr(nullptr), bottomMargin
.ptrOr(nullptr),
5737 leftMargin
.ptrOr(nullptr), vBounds
, hBounds
, applied
,
5738 wr::SpatialKey(uint64_t(mFrame
), GetPerFrameKey(),
5739 wr::SpatialKeyKind::Sticky
));
5741 saccHelper
.emplace(aBuilder
, spatialId
);
5742 aManager
->CommandBuilder().PushOverrideForASR(mContainerASR
, spatialId
);
5746 wr::StackingContextParams params
;
5748 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
5749 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this,
5751 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder
, aResources
, sc
,
5752 aManager
, aDisplayListBuilder
);
5755 if (stickyScrollContainer
) {
5756 aManager
->CommandBuilder().PopOverrideForASR(mContainerASR
);
5762 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5763 StickyScrollContainer
* aStickyScrollContainer
, float aAppUnitsPerDevPixel
,
5764 float aScaleX
, float aScaleY
, LayerRectAbsolute
& aStickyOuter
,
5765 LayerRectAbsolute
& aStickyInner
) {
5766 nsRectAbsolute outer
;
5767 nsRectAbsolute inner
;
5768 aStickyScrollContainer
->GetScrollRanges(mFrame
, &outer
, &inner
);
5769 aStickyOuter
.SetBox(
5770 NSAppUnitsToFloatPixels(outer
.X(), aAppUnitsPerDevPixel
) * aScaleX
,
5771 NSAppUnitsToFloatPixels(outer
.Y(), aAppUnitsPerDevPixel
) * aScaleY
,
5772 NSAppUnitsToFloatPixels(outer
.XMost(), aAppUnitsPerDevPixel
) * aScaleX
,
5773 NSAppUnitsToFloatPixels(outer
.YMost(), aAppUnitsPerDevPixel
) * aScaleY
);
5774 aStickyInner
.SetBox(
5775 NSAppUnitsToFloatPixels(inner
.X(), aAppUnitsPerDevPixel
) * aScaleX
,
5776 NSAppUnitsToFloatPixels(inner
.Y(), aAppUnitsPerDevPixel
) * aScaleY
,
5777 NSAppUnitsToFloatPixels(inner
.XMost(), aAppUnitsPerDevPixel
) * aScaleX
,
5778 NSAppUnitsToFloatPixels(inner
.YMost(), aAppUnitsPerDevPixel
) * aScaleY
);
5781 bool nsDisplayStickyPosition::UpdateScrollData(
5782 WebRenderScrollData
* aData
, WebRenderLayerScrollData
* aLayerData
) {
5783 bool hasDynamicToolbar
= HasDynamicToolbar();
5784 if (aLayerData
&& hasDynamicToolbar
) {
5785 StickyScrollContainer
* stickyScrollContainer
= GetStickyScrollContainer();
5786 if (stickyScrollContainer
) {
5787 float auPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
5788 float cumulativeResolution
=
5789 mFrame
->PresShell()->GetCumulativeResolution();
5790 LayerRectAbsolute stickyOuter
;
5791 LayerRectAbsolute stickyInner
;
5792 CalculateLayerScrollRanges(stickyScrollContainer
, auPerDevPixel
,
5793 cumulativeResolution
, cumulativeResolution
,
5794 stickyOuter
, stickyInner
);
5795 aLayerData
->SetStickyScrollRangeOuter(stickyOuter
);
5796 aLayerData
->SetStickyScrollRangeInner(stickyInner
);
5799 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame
);
5800 aLayerData
->SetFixedPositionSides(sides
);
5802 ScrollableLayerGuid::ViewID scrollId
=
5803 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer
->ScrollFrame()
5804 ->GetScrolledFrame()
5806 aLayerData
->SetStickyPositionScrollContainerId(scrollId
);
5809 // Return true if either there is a dynamic toolbar affecting this sticky
5810 // item or the OwnLayer base implementation returns true for some other
5812 bool ret
= hasDynamicToolbar
;
5813 ret
|= nsDisplayOwnLayer::UpdateScrollData(aData
, aLayerData
);
5817 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5818 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aScrolledFrame
,
5819 nsIFrame
* aScrollFrame
, const CompositorHitTestInfo
& aHitInfo
,
5820 const nsRect
& aHitArea
)
5821 : nsDisplayWrapList(aBuilder
, aScrollFrame
),
5822 mScrollFrame(aScrollFrame
),
5823 mScrolledFrame(aScrolledFrame
),
5824 mScrollParentId(aBuilder
->GetCurrentScrollParentId()),
5826 mHitArea(aHitArea
) {
5827 #ifdef NS_BUILD_REFCNT_LOGGING
5828 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer
);
5832 UniquePtr
<ScrollMetadata
> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5833 nsDisplayListBuilder
* aBuilder
, WebRenderLayerManager
* aLayerManager
) {
5834 ScrollMetadata metadata
= nsLayoutUtils::ComputeScrollMetadata(
5835 mScrolledFrame
, mScrollFrame
, mScrollFrame
->GetContent(), Frame(),
5836 ToReferenceFrame(), aLayerManager
, mScrollParentId
,
5837 mScrollFrame
->GetSize(), false);
5838 metadata
.GetMetrics().SetIsScrollInfoLayer(true);
5839 nsIScrollableFrame
* scrollableFrame
= mScrollFrame
->GetScrollTargetFrame();
5840 if (scrollableFrame
) {
5841 aBuilder
->AddScrollFrameToNotify(scrollableFrame
);
5844 return UniquePtr
<ScrollMetadata
>(new ScrollMetadata(metadata
));
5847 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5848 WebRenderScrollData
* aData
, WebRenderLayerScrollData
* aLayerData
) {
5850 UniquePtr
<ScrollMetadata
> metadata
=
5851 ComputeScrollMetadata(aData
->GetBuilder(), aData
->GetManager());
5853 MOZ_ASSERT(metadata
);
5854 aLayerData
->AppendScrollMetadata(*aData
, *metadata
);
5859 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5860 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5861 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5862 nsDisplayListBuilder
* aDisplayListBuilder
) {
5863 ScrollableLayerGuid::ViewID scrollId
=
5864 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame
->GetContent());
5866 const LayoutDeviceRect devRect
= LayoutDeviceRect::FromAppUnits(
5867 mHitArea
, mScrollFrame
->PresContext()->AppUnitsPerDevPixel());
5869 const wr::LayoutRect rect
= wr::ToLayoutRect(devRect
);
5871 aBuilder
.PushHitTest(rect
, rect
, !BackfaceIsHidden(), scrollId
, mHitInfo
,
5877 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream
& aStream
) {
5878 aStream
<< " (scrollframe " << mScrollFrame
<< " scrolledFrame "
5879 << mScrolledFrame
<< ")";
5882 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
5883 nsSubDocumentFrame
* aSubDocFrame
,
5884 nsDisplayList
* aList
, int32_t aAPD
,
5885 int32_t aParentAPD
, nsDisplayOwnLayerFlags aFlags
)
5886 : nsDisplaySubDocument(aBuilder
, aFrame
, aSubDocFrame
, aList
, aFlags
),
5888 mParentAPD(aParentAPD
) {
5889 MOZ_COUNT_CTOR(nsDisplayZoom
);
5892 nsRect
nsDisplayZoom::GetBounds(nsDisplayListBuilder
* aBuilder
,
5893 bool* aSnap
) const {
5894 nsRect bounds
= nsDisplaySubDocument::GetBounds(aBuilder
, aSnap
);
5896 return bounds
.ScaleToOtherAppUnitsRoundOut(mAPD
, mParentAPD
);
5899 void nsDisplayZoom::HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
5900 HitTestState
* aState
,
5901 nsTArray
<nsIFrame
*>* aOutFrames
) {
5903 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5904 // rect as well instead of possibly rounding the width or height to zero.
5905 if (aRect
.width
== 1 && aRect
.height
== 1) {
5906 rect
.MoveTo(aRect
.TopLeft().ScaleToOtherAppUnits(mParentAPD
, mAPD
));
5907 rect
.width
= rect
.height
= 1;
5909 rect
= aRect
.ScaleToOtherAppUnitsRoundOut(mParentAPD
, mAPD
);
5911 mList
.HitTest(aBuilder
, rect
, aState
, aOutFrames
);
5914 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5915 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5916 const ActiveScrolledRoot
* aActiveScrolledRoot
, FrameMetrics::ViewID aViewID
)
5917 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
),
5919 MOZ_COUNT_CTOR(nsDisplayAsyncZoom
);
5922 #ifdef NS_BUILD_REFCNT_LOGGING
5923 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5924 MOZ_COUNT_DTOR(nsDisplayAsyncZoom
);
5928 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder
* aBuilder
,
5929 const nsRect
& aRect
, HitTestState
* aState
,
5930 nsTArray
<nsIFrame
*>* aOutFrames
) {
5932 nsIScrollableFrame
* scrollFrame
= do_QueryFrame(mFrame
);
5933 MOZ_ASSERT(scrollFrame
&& ViewportUtils::IsZoomedContentRoot(
5934 scrollFrame
->GetScrolledFrame()));
5936 nsRect rect
= ViewportUtils::VisualToLayout(aRect
, mFrame
->PresShell());
5937 mList
.HitTest(aBuilder
, rect
, aState
, aOutFrames
);
5940 bool nsDisplayAsyncZoom::UpdateScrollData(
5941 WebRenderScrollData
* aData
, WebRenderLayerScrollData
* aLayerData
) {
5942 bool ret
= nsDisplayOwnLayer::UpdateScrollData(aData
, aLayerData
);
5945 aLayerData
->SetAsyncZoomContainerId(mViewID
);
5950 ///////////////////////////////////////////////////
5951 // nsDisplayTransform Implementation
5955 static_assert(sizeof(nsDisplayTransform
) <= 512,
5956 "nsDisplayTransform has grown");
5959 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder
* aBuilder
,
5960 nsIFrame
* aFrame
, nsDisplayList
* aList
,
5961 const nsRect
& aChildrenBuildingRect
)
5962 : nsPaintedDisplayItem(aBuilder
, aFrame
),
5963 mChildren(aBuilder
),
5964 mTransform(Some(Matrix4x4())),
5965 mChildrenBuildingRect(aChildrenBuildingRect
),
5966 mPrerenderDecision(PrerenderDecision::No
),
5967 mIsTransformSeparator(true),
5968 mHasTransformGetter(false),
5969 mHasAssociatedPerspective(false) {
5970 MOZ_COUNT_CTOR(nsDisplayTransform
);
5971 MOZ_ASSERT(aFrame
, "Must have a frame!");
5972 Init(aBuilder
, aList
);
5975 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder
* aBuilder
,
5976 nsIFrame
* aFrame
, nsDisplayList
* aList
,
5977 const nsRect
& aChildrenBuildingRect
,
5978 PrerenderDecision aPrerenderDecision
)
5979 : nsPaintedDisplayItem(aBuilder
, aFrame
),
5980 mChildren(aBuilder
),
5981 mChildrenBuildingRect(aChildrenBuildingRect
),
5982 mPrerenderDecision(aPrerenderDecision
),
5983 mIsTransformSeparator(false),
5984 mHasTransformGetter(false),
5985 mHasAssociatedPerspective(false) {
5986 MOZ_COUNT_CTOR(nsDisplayTransform
);
5987 MOZ_ASSERT(aFrame
, "Must have a frame!");
5988 SetReferenceFrameToAncestor(aBuilder
);
5989 Init(aBuilder
, aList
);
5992 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder
* aBuilder
,
5993 nsIFrame
* aFrame
, nsDisplayList
* aList
,
5994 const nsRect
& aChildrenBuildingRect
,
5995 decltype(WithTransformGetter
))
5996 : nsPaintedDisplayItem(aBuilder
, aFrame
),
5997 mChildren(aBuilder
),
5998 mChildrenBuildingRect(aChildrenBuildingRect
),
5999 mPrerenderDecision(PrerenderDecision::No
),
6000 mIsTransformSeparator(false),
6001 mHasTransformGetter(true),
6002 mHasAssociatedPerspective(false) {
6003 MOZ_COUNT_CTOR(nsDisplayTransform
);
6004 MOZ_ASSERT(aFrame
, "Must have a frame!");
6005 MOZ_ASSERT(aFrame
->GetTransformGetter());
6006 Init(aBuilder
, aList
);
6009 void nsDisplayTransform::SetReferenceFrameToAncestor(
6010 nsDisplayListBuilder
* aBuilder
) {
6011 if (mFrame
== aBuilder
->RootReferenceFrame()) {
6014 // We manually recompute mToReferenceFrame without going through the
6015 // builder, since this won't apply the 'additional offset'. Our
6016 // children will already be painting with that applied, and we don't
6017 // want to include it a second time in our transform. We don't recompute
6018 // our visible/building rects, since those should still include the additional
6020 // TODO: Are there are things computed using our ToReferenceFrame that should
6021 // have the additional offset applied? Should we instead just manually remove
6022 // the offset from our transform instead of this more general value?
6023 // Can we instead apply the additional offset to us and not our children, like
6024 // we do for all other offsets (and how reference frames are supposed to
6026 nsIFrame
* outerFrame
= nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame
);
6027 const nsIFrame
* referenceFrame
= aBuilder
->FindReferenceFrameFor(outerFrame
);
6028 mToReferenceFrame
= mFrame
->GetOffsetToCrossDoc(referenceFrame
);
6031 void nsDisplayTransform::Init(nsDisplayListBuilder
* aBuilder
,
6032 nsDisplayList
* aChildren
) {
6033 mChildren
.AppendToTop(aChildren
);
6034 UpdateBounds(aBuilder
);
6037 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) {
6041 /* Returns the delta specified by the transform-origin property.
6042 * This is a positive delta, meaning that it indicates the direction to move
6043 * to get from (0, 0) of the frame to the transform origin. This function is
6044 * called off the main thread.
6047 Point3D
nsDisplayTransform::GetDeltaToTransformOrigin(
6048 const nsIFrame
* aFrame
, TransformReferenceBox
& aRefBox
,
6049 float aAppUnitsPerPixel
) {
6050 MOZ_ASSERT(aFrame
, "Can't get delta for a null frame!");
6051 MOZ_ASSERT(aFrame
->IsTransformed() || aFrame
->BackfaceIsHidden() ||
6052 aFrame
->Combines3DTransformWithAncestors(),
6053 "Shouldn't get a delta for an untransformed frame!");
6055 if (!aFrame
->IsTransformed()) {
6059 /* For both of the coordinates, if the value of transform is a
6060 * percentage, it's relative to the size of the frame. Otherwise, if it's
6061 * a distance, it's already computed for us!
6063 const nsStyleDisplay
* display
= aFrame
->StyleDisplay();
6065 const StyleTransformOrigin
& transformOrigin
= display
->mTransformOrigin
;
6066 CSSPoint origin
= nsStyleTransformMatrix::Convert2DPosition(
6067 transformOrigin
.horizontal
, transformOrigin
.vertical
, aRefBox
);
6070 // 1. SVG frames have a reference box that can be (and typically is) offset
6071 // from the TopLeft() of the frame. We need to account for that here.
6072 // 2. If we are using transform-box:content-box in CSS layout, we have the
6073 // offset from TopLeft() of the frame as well.
6074 origin
.x
+= CSSPixel::FromAppUnits(aRefBox
.X());
6075 origin
.y
+= CSSPixel::FromAppUnits(aRefBox
.Y());
6077 float scale
= AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel
);
6078 float z
= transformOrigin
.depth
._0
;
6079 return Point3D(origin
.x
* scale
, origin
.y
* scale
, z
* scale
);
6083 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame
* aFrame
,
6084 float aAppUnitsPerPixel
,
6085 Matrix4x4
& aOutMatrix
) {
6086 MOZ_ASSERT(aFrame
, "Can't get delta for a null frame!");
6087 MOZ_ASSERT(aFrame
->IsTransformed() || aFrame
->BackfaceIsHidden() ||
6088 aFrame
->Combines3DTransformWithAncestors(),
6089 "Shouldn't get a delta for an untransformed frame!");
6090 MOZ_ASSERT(aOutMatrix
.IsIdentity(), "Must have a blank output matrix");
6092 if (!aFrame
->IsTransformed()) {
6096 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6097 // correctly yet (similar to the aBoundsOverride case for
6098 // GetResultingTransformMatrix)?
6099 nsIFrame
* perspectiveFrame
=
6100 aFrame
->GetClosestFlattenedTreeAncestorPrimaryFrame();
6101 if (!perspectiveFrame
) {
6105 /* Grab the values for perspective and perspective-origin (if present) */
6106 const nsStyleDisplay
* perspectiveDisplay
= perspectiveFrame
->StyleDisplay();
6107 if (perspectiveDisplay
->mChildPerspective
.IsNone()) {
6111 MOZ_ASSERT(perspectiveDisplay
->mChildPerspective
.IsLength());
6113 perspectiveDisplay
->mChildPerspective
.AsLength().ToCSSPixels();
6114 perspective
= std::max(1.0f
, perspective
);
6115 if (perspective
< std::numeric_limits
<Float
>::epsilon()) {
6119 TransformReferenceBox
refBox(perspectiveFrame
);
6121 Point perspectiveOrigin
= nsStyleTransformMatrix::Convert2DPosition(
6122 perspectiveDisplay
->mPerspectiveOrigin
.horizontal
,
6123 perspectiveDisplay
->mPerspectiveOrigin
.vertical
, refBox
,
6126 /* GetOffsetTo computes the offset required to move from 0,0 in
6127 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6128 * this, it's faster to compute this way.
6130 nsPoint frameToPerspectiveOffset
= -aFrame
->GetOffsetTo(perspectiveFrame
);
6131 Point
frameToPerspectiveGfxOffset(
6132 NSAppUnitsToFloatPixels(frameToPerspectiveOffset
.x
, aAppUnitsPerPixel
),
6133 NSAppUnitsToFloatPixels(frameToPerspectiveOffset
.y
, aAppUnitsPerPixel
));
6135 /* Move the perspective origin to be relative to aFrame, instead of relative
6136 * to the containing block which is how it was specified in the style system.
6138 perspectiveOrigin
+= frameToPerspectiveGfxOffset
;
6141 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective
),
6144 aOutMatrix
.ChangeBasis(Point3D(perspectiveOrigin
.x
, perspectiveOrigin
.y
, 0));
6148 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6149 const nsIFrame
* aFrame
, TransformReferenceBox
& aRefBox
,
6150 float aAppUnitsPerPixel
)
6152 mTranslate(aFrame
->StyleDisplay()->mTranslate
),
6153 mRotate(aFrame
->StyleDisplay()->mRotate
),
6154 mScale(aFrame
->StyleDisplay()->mScale
),
6155 mTransform(aFrame
->StyleDisplay()->mTransform
),
6156 mMotion(aFrame
->StyleDisplay()->mOffsetPath
.IsNone()
6158 : MotionPathUtils::ResolveMotionPath(aFrame
, aRefBox
)),
6160 GetDeltaToTransformOrigin(aFrame
, aRefBox
, aAppUnitsPerPixel
)) {}
6162 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6163 * translates from local coordinate space to transform coordinate space, then
6166 Matrix4x4
nsDisplayTransform::GetResultingTransformMatrix(
6167 const FrameTransformProperties
& aProperties
, TransformReferenceBox
& aRefBox
,
6168 float aAppUnitsPerPixel
) {
6169 return GetResultingTransformMatrixInternal(aProperties
, aRefBox
, nsPoint(),
6170 aAppUnitsPerPixel
, 0);
6173 Matrix4x4
nsDisplayTransform::GetResultingTransformMatrix(
6174 const nsIFrame
* aFrame
, const nsPoint
& aOrigin
, float aAppUnitsPerPixel
,
6176 TransformReferenceBox
refBox(aFrame
);
6177 FrameTransformProperties
props(aFrame
, refBox
, aAppUnitsPerPixel
);
6178 return GetResultingTransformMatrixInternal(props
, refBox
, aOrigin
,
6179 aAppUnitsPerPixel
, aFlags
);
6182 Matrix4x4
nsDisplayTransform::GetResultingTransformMatrixInternal(
6183 const FrameTransformProperties
& aProperties
, TransformReferenceBox
& aRefBox
,
6184 const nsPoint
& aOrigin
, float aAppUnitsPerPixel
, uint32_t aFlags
) {
6185 const nsIFrame
* frame
= aProperties
.mFrame
;
6186 NS_ASSERTION(frame
|| !(aFlags
& INCLUDE_PERSPECTIVE
),
6187 "Must have a frame to compute perspective!");
6189 // Get the underlying transform matrix:
6191 /* Get the matrix, then change its basis to factor in the origin. */
6193 // Call IsSVGTransformed() regardless of the value of
6194 // aProperties.HasTransform(), since we still need any
6195 // potential parentsChildrenOnlyTransform.
6196 Matrix svgTransform
, parentsChildrenOnlyTransform
;
6197 const bool hasSVGTransforms
=
6198 frame
&& frame
->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED
) &&
6199 frame
->IsSVGTransformed(&svgTransform
, &parentsChildrenOnlyTransform
);
6200 bool shouldRound
= nsLayoutUtils::ShouldSnapToGrid(frame
);
6202 /* Transformed frames always have a transform, or are preserving 3d (and might
6203 * still have perspective!) */
6204 if (aProperties
.HasTransform()) {
6205 result
= nsStyleTransformMatrix::ReadTransforms(
6206 aProperties
.mTranslate
, aProperties
.mRotate
, aProperties
.mScale
,
6207 aProperties
.mMotion
.ptrOr(nullptr), aProperties
.mTransform
, aRefBox
,
6209 } else if (hasSVGTransforms
) {
6210 // Correct the translation components for zoom:
6211 float pixelsPerCSSPx
= AppUnitsPerCSSPixel() / aAppUnitsPerPixel
;
6212 svgTransform
._31
*= pixelsPerCSSPx
;
6213 svgTransform
._32
*= pixelsPerCSSPx
;
6214 result
= Matrix4x4::From2D(svgTransform
);
6217 // Apply any translation due to 'transform-origin' and/or 'transform-box':
6218 result
.ChangeBasis(aProperties
.mToTransformOrigin
);
6220 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
6221 // an explanation of what children-only transforms are.
6222 const bool parentHasChildrenOnlyTransform
=
6223 hasSVGTransforms
&& !parentsChildrenOnlyTransform
.IsIdentity();
6225 if (parentHasChildrenOnlyTransform
) {
6226 float pixelsPerCSSPx
= AppUnitsPerCSSPixel() / aAppUnitsPerPixel
;
6227 parentsChildrenOnlyTransform
._31
*= pixelsPerCSSPx
;
6228 parentsChildrenOnlyTransform
._32
*= pixelsPerCSSPx
;
6230 Point3D
frameOffset(
6231 NSAppUnitsToFloatPixels(-frame
->GetPosition().x
, aAppUnitsPerPixel
),
6232 NSAppUnitsToFloatPixels(-frame
->GetPosition().y
, aAppUnitsPerPixel
), 0);
6233 Matrix4x4 parentsChildrenOnlyTransform3D
=
6234 Matrix4x4::From2D(parentsChildrenOnlyTransform
)
6235 .ChangeBasis(frameOffset
);
6237 result
*= parentsChildrenOnlyTransform3D
;
6240 Matrix4x4 perspectiveMatrix
;
6241 bool hasPerspective
= aFlags
& INCLUDE_PERSPECTIVE
;
6242 if (hasPerspective
) {
6243 if (ComputePerspectiveMatrix(frame
, aAppUnitsPerPixel
, perspectiveMatrix
)) {
6244 result
*= perspectiveMatrix
;
6248 if ((aFlags
& INCLUDE_PRESERVE3D_ANCESTORS
) && frame
&&
6249 frame
->Combines3DTransformWithAncestors()) {
6250 // Include the transform set on our parent
6251 nsIFrame
* parentFrame
=
6252 frame
->GetClosestFlattenedTreeAncestorPrimaryFrame();
6253 NS_ASSERTION(parentFrame
&& parentFrame
->IsTransformed() &&
6254 parentFrame
->Extend3DContext(),
6255 "Preserve3D mismatch!");
6256 TransformReferenceBox
refBox(parentFrame
);
6257 FrameTransformProperties
props(parentFrame
, refBox
, aAppUnitsPerPixel
);
6260 aFlags
& (INCLUDE_PRESERVE3D_ANCESTORS
| INCLUDE_PERSPECTIVE
);
6262 // If this frame isn't transformed (but we exist for backface-visibility),
6263 // then we're not a reference frame so no offset to origin will be added.
6264 // Otherwise we need to manually translate into our parent's coordinate
6266 if (frame
->IsTransformed()) {
6267 nsLayoutUtils::PostTranslate(result
, frame
->GetPosition(),
6268 aAppUnitsPerPixel
, shouldRound
);
6270 Matrix4x4 parent
= GetResultingTransformMatrixInternal(
6271 props
, refBox
, nsPoint(0, 0), aAppUnitsPerPixel
, flags
);
6272 result
= result
* parent
;
6275 if (aFlags
& OFFSET_BY_ORIGIN
) {
6276 nsLayoutUtils::PostTranslate(result
, aOrigin
, aAppUnitsPerPixel
,
6283 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder
* aBuilder
) {
6284 static constexpr nsCSSPropertyIDSet opacitySet
=
6285 nsCSSPropertyIDSet::OpacityProperties();
6286 if (ActiveLayerTracker::IsStyleAnimated(aBuilder
, mFrame
, opacitySet
)) {
6290 EffectCompositor::SetPerformanceWarning(
6292 AnimationPerformanceWarning(
6293 AnimationPerformanceWarning::Type::OpacityFrameInactive
));
6298 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder
* aBuilder
) {
6299 return mPrerenderDecision
!= PrerenderDecision::No
;
6302 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
6303 nsDisplayListBuilder
* aBuilder
) {
6304 return StaticPrefs::gfx_omta_background_color();
6307 static bool IsInStickyPositionedSubtree(const nsIFrame
* aFrame
) {
6308 for (const nsIFrame
* frame
= aFrame
; frame
;
6309 frame
= nsLayoutUtils::GetCrossDocParentFrameInProcess(frame
)) {
6310 if (frame
->IsStickyPositioned()) {
6317 static bool ShouldUsePartialPrerender(const nsIFrame
* aFrame
) {
6318 return StaticPrefs::layout_animation_prerender_partial() &&
6319 // Bug 1642547: Support partial prerender for position:sticky elements.
6320 !IsInStickyPositionedSubtree(aFrame
);
6324 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
6325 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsRect
* aDirtyRect
)
6327 PrerenderInfo result
;
6328 // If we are in a preserve-3d tree, and we've disallowed async animations, we
6329 // return No prerender decision directly.
6330 if ((aFrame
->Extend3DContext() ||
6331 aFrame
->Combines3DTransformWithAncestors()) &&
6332 !aBuilder
->GetPreserves3DAllowAsyncAnimation()) {
6336 // Elements whose transform has been modified recently, or which
6337 // have a compositor-animated transform, can be prerendered. An element
6338 // might have only just had its transform animated in which case
6339 // the ActiveLayerManager may not have been notified yet.
6340 static constexpr nsCSSPropertyIDSet transformSet
=
6341 nsCSSPropertyIDSet::TransformLikeProperties();
6342 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame
) &&
6343 !EffectCompositor::HasAnimationsForCompositor(
6344 aFrame
, DisplayItemType::TYPE_TRANSFORM
)) {
6345 EffectCompositor::SetPerformanceWarning(
6346 aFrame
, transformSet
,
6347 AnimationPerformanceWarning(
6348 AnimationPerformanceWarning::Type::TransformFrameInactive
));
6350 // This case happens when we're sure that the frame is not animated and its
6351 // preserve-3d ancestors are not, either. So we don't need to pre-render.
6352 // However, this decision shouldn't affect the decisions for other frames in
6353 // the preserve-3d context. We need this flag to determine whether we should
6354 // block async animations on other frames in the current preserve-3d tree.
6355 result
.mHasAnimations
= false;
6359 // We should not allow prerender if any ancestor container element has
6360 // mask/clip-path effects.
6362 // With prerender and async transform animation, we do not need to restyle an
6363 // animated element to respect position changes, since that transform is done
6364 // by layer animation. As a result, the container element is not aware of
6365 // position change of that containing element and loses the chance to update
6366 // the content of mask/clip-path.
6368 // Why do we need to update a mask? This is relative to how we generate a
6369 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
6370 // mask layer, to reduce memory usage, we did not choose the size of the
6371 // masked element as mask size. Instead, we read the union of bounds of all
6372 // children display items by nsDisplayWrapList::GetBounds, which is smaller
6373 // than or equal to the masked element's boundary, and use it as the position
6374 // size of the mask layer. That union bounds is actually affected by the
6375 // geometry of the animated element. To keep the content of mask up to date,
6376 // forbidding of prerender is required.
6377 for (nsIFrame
* container
=
6378 nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame
);
6380 container
= nsLayoutUtils::GetCrossDocParentFrameInProcess(container
)) {
6381 const nsStyleSVGReset
* svgReset
= container
->StyleSVGReset();
6382 if (svgReset
->HasMask() || svgReset
->HasClipPath()) {
6387 // If the incoming dirty rect already contains the entire overflow area,
6388 // we are already rendering the entire content.
6389 nsRect overflow
= aFrame
->InkOverflowRectRelativeToSelf();
6390 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
6391 // in cases of non-invertible transforms, so we set `untransformedRect` to
6392 // `aDirtyRect` as an initial value for such cases.
6393 nsRect untransformedDirtyRect
= *aDirtyRect
;
6394 UntransformRect(*aDirtyRect
, overflow
, aFrame
, &untransformedDirtyRect
);
6395 if (untransformedDirtyRect
.Contains(overflow
)) {
6396 *aDirtyRect
= untransformedDirtyRect
;
6397 result
.mDecision
= PrerenderDecision::Full
;
6401 float viewportRatio
=
6402 StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
6403 uint32_t absoluteLimitX
=
6404 StaticPrefs::layout_animation_prerender_absolute_limit_x();
6405 uint32_t absoluteLimitY
=
6406 StaticPrefs::layout_animation_prerender_absolute_limit_y();
6407 nsSize refSize
= aBuilder
->RootReferenceFrame()->GetSize();
6409 float resolution
= aFrame
->PresShell()->GetCumulativeResolution();
6410 if (resolution
< 1.0f
) {
6412 NSCoordSaturatingNonnegativeMultiply(refSize
.width
, 1.0f
/ resolution
),
6413 NSCoordSaturatingNonnegativeMultiply(refSize
.height
,
6414 1.0f
/ resolution
));
6417 // Only prerender if the transformed frame's size is <= a multiple of the
6418 // reference frame size (~viewport), and less than an absolute limit.
6419 // Both the ratio and the absolute limit are configurable.
6420 nscoord maxLength
= std::max(nscoord(refSize
.width
* viewportRatio
),
6421 nscoord(refSize
.height
* viewportRatio
));
6422 nsSize
relativeLimit(maxLength
, maxLength
);
6423 nsSize
absoluteLimit(
6424 aFrame
->PresContext()->DevPixelsToAppUnits(absoluteLimitX
),
6425 aFrame
->PresContext()->DevPixelsToAppUnits(absoluteLimitY
));
6426 nsSize maxSize
= Min(relativeLimit
, absoluteLimit
);
6428 const auto transform
= nsLayoutUtils::GetTransformToAncestor(
6430 RelativeTo
{nsLayoutUtils::GetDisplayRootFrame(aFrame
)});
6431 const gfxRect transformedBounds
= transform
.TransformAndClipBounds(
6432 gfxRect(overflow
.x
, overflow
.y
, overflow
.width
, overflow
.height
),
6433 gfxRect::MaxIntRect());
6434 const nsSize frameSize
=
6435 nsSize(transformedBounds
.width
, transformedBounds
.height
);
6437 uint64_t maxLimitArea
= uint64_t(maxSize
.width
) * maxSize
.height
;
6438 uint64_t frameArea
= uint64_t(frameSize
.width
) * frameSize
.height
;
6439 if (frameArea
<= maxLimitArea
&& frameSize
<= absoluteLimit
) {
6440 *aDirtyRect
= overflow
;
6441 result
.mDecision
= PrerenderDecision::Full
;
6445 if (ShouldUsePartialPrerender(aFrame
)) {
6446 *aDirtyRect
= nsLayoutUtils::ComputePartialPrerenderArea(
6447 aFrame
, untransformedDirtyRect
, overflow
, maxSize
);
6448 result
.mDecision
= PrerenderDecision::Partial
;
6452 if (frameArea
> maxLimitArea
) {
6453 uint64_t appUnitsPerPixel
= AppUnitsPerCSSPixel();
6454 EffectCompositor::SetPerformanceWarning(
6455 aFrame
, transformSet
,
6456 AnimationPerformanceWarning(
6457 AnimationPerformanceWarning::Type::ContentTooLargeArea
,
6459 int(frameArea
/ (appUnitsPerPixel
* appUnitsPerPixel
)),
6460 int(maxLimitArea
/ (appUnitsPerPixel
* appUnitsPerPixel
)),
6463 EffectCompositor::SetPerformanceWarning(
6464 aFrame
, transformSet
,
6465 AnimationPerformanceWarning(
6466 AnimationPerformanceWarning::Type::ContentTooLarge
,
6468 nsPresContext::AppUnitsToIntCSSPixels(frameSize
.width
),
6469 nsPresContext::AppUnitsToIntCSSPixels(frameSize
.height
),
6470 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit
.width
),
6471 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit
.height
),
6472 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit
.width
),
6473 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit
.height
),
6480 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
6481 * visible or hit. */
6482 static bool IsFrameVisible(nsIFrame
* aFrame
, const Matrix4x4
& aMatrix
) {
6483 if (aMatrix
.IsSingular()) {
6486 if (aFrame
->BackfaceIsHidden() && aMatrix
.IsBackfaceVisible()) {
6492 const Matrix4x4Flagged
& nsDisplayTransform::GetTransform() const {
6497 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6499 if (mHasTransformGetter
) {
6500 mTransform
.emplace((mFrame
->GetTransformGetter())(mFrame
, scale
));
6502 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame
.x
, scale
),
6503 NSAppUnitsToFloatPixels(mToReferenceFrame
.y
, scale
), 0.0f
);
6504 mTransform
->ChangeBasis(newOrigin
.x
, newOrigin
.y
, newOrigin
.z
);
6505 } else if (!mIsTransformSeparator
) {
6506 DebugOnly
<bool> isReference
= mFrame
->IsTransformed() ||
6507 mFrame
->Combines3DTransformWithAncestors() ||
6508 mFrame
->Extend3DContext();
6509 MOZ_ASSERT(isReference
);
6511 GetResultingTransformMatrix(mFrame
, ToReferenceFrame(), scale
,
6512 INCLUDE_PERSPECTIVE
| OFFSET_BY_ORIGIN
));
6514 // Use identity matrix
6515 mTransform
.emplace();
6521 const Matrix4x4Flagged
& nsDisplayTransform::GetInverseTransform() const {
6522 if (mInverseTransform
) {
6523 return *mInverseTransform
;
6526 MOZ_ASSERT(!GetTransform().IsSingular());
6528 mInverseTransform
.emplace(GetTransform().Inverse());
6530 return *mInverseTransform
;
6533 Matrix4x4
nsDisplayTransform::GetTransformForRendering(
6534 LayoutDevicePoint
* aOutOrigin
) const {
6535 if (!mFrame
->HasPerspective() || mHasTransformGetter
||
6536 mIsTransformSeparator
) {
6537 if (!mHasTransformGetter
&& !mIsTransformSeparator
&& aOutOrigin
) {
6538 // If aOutOrigin is provided, put the offset to origin into it, because
6539 // we need to keep it separate for webrender. The combination of
6540 // *aOutOrigin and the returned matrix here should always be equivalent
6541 // to what GetTransform() would have returned.
6542 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6543 *aOutOrigin
= LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale
);
6545 // The rounding behavior should also be the same as GetTransform().
6546 if (nsLayoutUtils::ShouldSnapToGrid(mFrame
)) {
6547 aOutOrigin
->Round();
6549 return GetResultingTransformMatrix(mFrame
, nsPoint(0, 0), scale
,
6550 INCLUDE_PERSPECTIVE
);
6552 return GetTransform().GetMatrix();
6554 MOZ_ASSERT(!mHasTransformGetter
);
6556 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6557 // Don't include perspective transform, or the offset to origin, since
6558 // nsDisplayPerspective will handle both of those.
6559 return GetResultingTransformMatrix(mFrame
, ToReferenceFrame(), scale
, 0);
6562 const Matrix4x4
& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
6563 nsDisplayListBuilder
* aBuilder
) {
6564 MOZ_ASSERT(!mFrame
->Extend3DContext() || IsLeafOf3DContext());
6566 if (!IsLeafOf3DContext()) {
6567 return GetTransform().GetMatrix();
6570 if (!mTransformPreserves3D
) {
6571 const nsIFrame
* establisher
; // Establisher of the 3D rendering context.
6572 for (establisher
= mFrame
;
6573 establisher
&& establisher
->Combines3DTransformWithAncestors();
6575 establisher
->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
6577 const nsIFrame
* establisherReference
= aBuilder
->FindReferenceFrameFor(
6578 nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher
));
6580 nsPoint offset
= establisher
->GetOffsetToCrossDoc(establisherReference
);
6581 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6583 INCLUDE_PRESERVE3D_ANCESTORS
| INCLUDE_PERSPECTIVE
| OFFSET_BY_ORIGIN
;
6584 mTransformPreserves3D
= MakeUnique
<Matrix4x4
>(
6585 GetResultingTransformMatrix(mFrame
, offset
, scale
, flags
));
6588 return *mTransformPreserves3D
;
6591 bool nsDisplayTransform::CreateWebRenderCommands(
6592 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
6593 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
6594 nsDisplayListBuilder
* aDisplayListBuilder
) {
6595 // We want to make sure we don't pollute the transform property in the WR
6596 // stacking context by including the position of this frame (relative to the
6597 // parent reference frame). We need to keep those separate; the position of
6598 // this frame goes into the stacking context bounds while the transform goes
6599 // into the transform.
6600 LayoutDevicePoint position
;
6601 Matrix4x4 newTransformMatrix
= GetTransformForRendering(&position
);
6603 gfx::Matrix4x4
* transformForSC
= &newTransformMatrix
;
6604 if (newTransformMatrix
.IsIdentity()) {
6605 // If the transform is an identity transform, strip it out so that WR
6606 // doesn't turn this stacking context into a reference frame, as it
6607 // affects positioning. Bug 1345577 tracks a better fix.
6608 transformForSC
= nullptr;
6610 // In ChooseScaleAndSetTransform, we round the offset from the reference
6611 // frame used to adjust the transform, if there is no transform, or it
6612 // is just a translation. We need to do the same here.
6613 if (nsLayoutUtils::ShouldSnapToGrid(mFrame
)) {
6618 auto key
= wr::SpatialKey(uint64_t(mFrame
), GetPerFrameKey(),
6619 wr::SpatialKeyKind::Transform
);
6621 // We don't send animations for transform separator display items.
6622 uint64_t animationsId
=
6623 mIsTransformSeparator
6625 : AddAnimationsForWebRender(
6626 this, aManager
, aDisplayListBuilder
,
6627 IsPartialPrerender() ? Some(position
) : Nothing());
6628 wr::WrAnimationProperty prop
{wr::WrAnimationType::Transform
, animationsId
,
6631 nsDisplayTransform
* deferredTransformItem
= nullptr;
6632 if (!mFrame
->ChildrenHavePerspective()) {
6633 // If it has perspective, we create a new scroll data via the
6634 // UpdateScrollData call because that scenario is more complex. Otherwise
6635 // we can just stash the transform on the StackingContextHelper and
6636 // apply it to any scroll data that are created inside this
6637 // nsDisplayTransform.
6638 deferredTransformItem
= this;
6641 // Determine if we're possibly animated (= would need an active layer in FLB).
6642 bool animated
= !mIsTransformSeparator
&&
6643 ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
6645 wr::StackingContextParams params
;
6646 params
.mBoundTransform
= &newTransformMatrix
;
6647 params
.animation
= animationsId
? &prop
: nullptr;
6649 wr::WrTransformInfo transform_info
;
6650 if (transformForSC
) {
6651 transform_info
.transform
= wr::ToLayoutTransform(newTransformMatrix
);
6652 transform_info
.key
= key
;
6653 params
.mTransformPtr
= &transform_info
;
6655 params
.mTransformPtr
= nullptr;
6658 params
.prim_flags
= !BackfaceIsHidden()
6659 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
6660 : wr::PrimitiveFlags
{0};
6661 params
.paired_with_perspective
= mHasAssociatedPerspective
;
6662 params
.mDeferredTransformItem
= deferredTransformItem
;
6663 params
.mAnimated
= animated
;
6664 // Determine if we would have to rasterize any items in local raster space
6665 // (i.e. disable subpixel AA). We don't always need to rasterize locally even
6666 // if the stacking context is possibly animated (at the cost of potentially
6667 // some false negatives with respect to will-change handling), so we pass in
6668 // this determination separately to accurately match with when FLB would
6669 // normally disable subpixel AA.
6670 params
.mRasterizeLocally
= animated
&& Frame()->HasAnimationOfTransform();
6671 params
.SetPreserve3D(mFrame
->Extend3DContext() && !mIsTransformSeparator
);
6673 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
6675 LayoutDeviceSize boundsSize
= LayoutDeviceSize::FromAppUnits(
6676 mChildBounds
.Size(), mFrame
->PresContext()->AppUnitsPerDevPixel());
6678 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
6679 params
, LayoutDeviceRect(position
, boundsSize
));
6681 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6682 GetChildren(), this, aDisplayListBuilder
, sc
, aBuilder
, aResources
);
6686 bool nsDisplayTransform::UpdateScrollData(
6687 WebRenderScrollData
* aData
, WebRenderLayerScrollData
* aLayerData
) {
6688 if (!mFrame
->ChildrenHavePerspective()) {
6689 // This case is handled in CreateWebRenderCommands by stashing the transform
6690 // on the stacking context.
6694 aLayerData
->SetTransform(GetTransform().GetMatrix());
6695 aLayerData
->SetTransformIsPerspective(true);
6700 bool nsDisplayTransform::ShouldSkipTransform(
6701 nsDisplayListBuilder
* aBuilder
) const {
6702 return (aBuilder
->RootReferenceFrame() == mFrame
) &&
6703 aBuilder
->IsForGenerateGlyphMask();
6706 void nsDisplayTransform::Collect3DTransformLeaves(
6707 nsDisplayListBuilder
* aBuilder
, nsTArray
<nsDisplayTransform
*>& aLeaves
) {
6708 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6709 aLeaves
.AppendElement(this);
6713 FlattenedDisplayListIterator
iter(aBuilder
, &mChildren
);
6714 while (iter
.HasNext()) {
6715 nsDisplayItem
* item
= iter
.GetNextItem();
6716 if (item
->GetType() == DisplayItemType::TYPE_PERSPECTIVE
) {
6717 auto* perspective
= static_cast<nsDisplayPerspective
*>(item
);
6718 if (!perspective
->GetChildren()->GetTop()) {
6721 item
= perspective
->GetChildren()->GetTop();
6723 if (item
->GetType() != DisplayItemType::TYPE_TRANSFORM
) {
6724 gfxCriticalError() << "Invalid child item within 3D transform of type: "
6728 static_cast<nsDisplayTransform
*>(item
)->Collect3DTransformLeaves(aBuilder
,
6733 static RefPtr
<gfx::Path
> BuildPathFromPolygon(const RefPtr
<DrawTarget
>& aDT
,
6734 const gfx::Polygon
& aPolygon
) {
6735 MOZ_ASSERT(!aPolygon
.IsEmpty());
6737 RefPtr
<PathBuilder
> pathBuilder
= aDT
->CreatePathBuilder();
6738 const nsTArray
<Point4D
>& points
= aPolygon
.GetPoints();
6740 pathBuilder
->MoveTo(points
[0].As2DPoint());
6742 for (size_t i
= 1; i
< points
.Length(); ++i
) {
6743 pathBuilder
->LineTo(points
[i
].As2DPoint());
6746 pathBuilder
->Close();
6747 return pathBuilder
->Finish();
6750 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6751 nsDisplayListBuilder
* aBuilder
, nsTArray
<TransformPolygon
>& aLeaves
) {
6752 std::list
<TransformPolygon
> inputLayers
;
6754 nsTArray
<nsDisplayTransform
*> leaves
;
6755 Collect3DTransformLeaves(aBuilder
, leaves
);
6756 for (nsDisplayTransform
* item
: leaves
) {
6757 auto bounds
= LayoutDeviceRect::FromAppUnits(
6758 item
->mChildBounds
, item
->mFrame
->PresContext()->AppUnitsPerDevPixel());
6759 Matrix4x4 transform
= item
->GetAccumulatedPreserved3DTransform(aBuilder
);
6761 if (!IsFrameVisible(item
->mFrame
, transform
)) {
6764 gfx::Polygon polygon
=
6765 gfx::Polygon::FromRect(gfx::Rect(bounds
.ToUnknownRect()));
6767 polygon
.TransformToScreenSpace(transform
);
6769 if (polygon
.GetPoints().Length() >= 3) {
6770 inputLayers
.push_back(TransformPolygon(item
, std::move(polygon
)));
6774 if (inputLayers
.empty()) {
6778 BSPTree
<nsDisplayTransform
> tree(inputLayers
);
6779 nsTArray
<TransformPolygon
> orderedLayers(tree
.GetDrawOrder());
6781 for (TransformPolygon
& polygon
: orderedLayers
) {
6783 polygon
.data
->GetAccumulatedPreserved3DTransform(aBuilder
).Inverse();
6785 MOZ_ASSERT(polygon
.geometry
);
6786 polygon
.geometry
->TransformToLayerSpace(inverse
);
6789 aLeaves
= std::move(orderedLayers
);
6792 void nsDisplayTransform::Paint(nsDisplayListBuilder
* aBuilder
,
6794 Paint(aBuilder
, aCtx
, Nothing());
6797 void nsDisplayTransform::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
6798 const Maybe
<gfx::Polygon
>& aPolygon
) {
6799 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6800 MOZ_ASSERT(!aPolygon
);
6801 nsTArray
<TransformPolygon
> leaves
;
6802 CollectSorted3DTransformLeaves(aBuilder
, leaves
);
6803 for (TransformPolygon
& item
: leaves
) {
6804 item
.data
->Paint(aBuilder
, aCtx
, item
.geometry
);
6809 gfxContextMatrixAutoSaveRestore
saveMatrix(aCtx
);
6810 Matrix4x4 trans
= ShouldSkipTransform(aBuilder
)
6812 : GetAccumulatedPreserved3DTransform(aBuilder
);
6813 if (!IsFrameVisible(mFrame
, trans
)) {
6818 if (trans
.CanDraw2D(&trans2d
)) {
6819 aCtx
->Multiply(ThebesMatrix(trans2d
));
6822 RefPtr
<gfx::Path
> path
=
6823 BuildPathFromPolygon(aCtx
->GetDrawTarget(), *aPolygon
);
6824 aCtx
->GetDrawTarget()->PushClip(path
);
6827 GetChildren()->Paint(aBuilder
, aCtx
,
6828 mFrame
->PresContext()->AppUnitsPerDevPixel());
6831 aCtx
->GetDrawTarget()->PopClip();
6836 // TODO: Implement 3d transform handling, including plane splitting and
6837 // sorting. See BasicCompositor.
6838 auto pixelBounds
= LayoutDeviceRect::FromAppUnitsToOutside(
6839 mChildBounds
, mFrame
->PresContext()->AppUnitsPerDevPixel());
6840 RefPtr
<DrawTarget
> untransformedDT
=
6841 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6842 IntSize(pixelBounds
.Width(), pixelBounds
.Height()),
6843 SurfaceFormat::B8G8R8A8
, true);
6844 if (!untransformedDT
|| !untransformedDT
->IsValid()) {
6847 untransformedDT
->SetTransform(
6848 Matrix::Translation(-Point(pixelBounds
.X(), pixelBounds
.Y())));
6850 gfxContext
groupTarget(untransformedDT
, /* aPreserveTransform */ true);
6853 RefPtr
<gfx::Path
> path
=
6854 BuildPathFromPolygon(aCtx
->GetDrawTarget(), *aPolygon
);
6855 aCtx
->GetDrawTarget()->PushClip(path
);
6858 GetChildren()->Paint(aBuilder
, &groupTarget
,
6859 mFrame
->PresContext()->AppUnitsPerDevPixel());
6862 aCtx
->GetDrawTarget()->PopClip();
6865 RefPtr
<SourceSurface
> untransformedSurf
= untransformedDT
->Snapshot();
6867 trans
.PreTranslate(pixelBounds
.X(), pixelBounds
.Y(), 0);
6868 aCtx
->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf
, trans
);
6871 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder
* aBuilder
) const {
6872 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6873 // completely bypass the main thread for this animation, so it is always
6875 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6876 // already involved so there is less to be gained.
6877 // Therefore we check that the *post-transform* bounds of this item are
6878 // big enough to justify an active layer.
6879 return EffectCompositor::HasAnimationsForCompositor(
6880 mFrame
, DisplayItemType::TYPE_TRANSFORM
) ||
6881 (ActiveLayerTracker::IsTransformAnimated(aBuilder
, mFrame
));
6884 nsRect
nsDisplayTransform::TransformUntransformedBounds(
6885 nsDisplayListBuilder
* aBuilder
, const Matrix4x4Flagged
& aMatrix
) const {
6887 const nsRect untransformedBounds
= GetUntransformedBounds(aBuilder
, &snap
);
6888 // GetTransform always operates in dev pixels.
6889 const float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6890 return nsLayoutUtils::MatrixTransformRect(untransformedBounds
, aMatrix
,
6895 * Returns the bounds for this transform. The bounds are calculated during
6896 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6898 nsRect
nsDisplayTransform::GetBounds(nsDisplayListBuilder
* aBuilder
,
6899 bool* aSnap
) const {
6904 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder
* aBuilder
) {
6905 MOZ_ASSERT(mFrame
->Extend3DContext() || IsLeafOf3DContext());
6907 /* Some transforms can get empty bounds in 2D, but might get transformed again
6908 * and get non-empty bounds. A simple example of this would be a 180 degree
6909 * rotation getting applied twice.
6910 * We should not depend on transforming bounds level by level.
6912 * This function collects the bounds of this transform and stores it in
6913 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6914 * down and include the bounds of the child transforms.
6915 * The bounds are transformed with the accumulated transformation matrix up to
6916 * the 3D context root coordinate space.
6918 nsDisplayListBuilder::AutoAccumulateTransform
accTransform(aBuilder
);
6919 accTransform
.Accumulate(GetTransform().GetMatrix());
6921 // Do not dive into another 3D context.
6922 if (!IsLeafOf3DContext()) {
6923 for (nsDisplayItem
* i
: *GetChildren()) {
6924 i
->DoUpdateBoundsPreserves3D(aBuilder
);
6928 /* The child transforms that extend 3D context further will have empty bounds,
6929 * so the untransformed bounds here is the bounds of all the non-preserve-3d
6930 * content under this transform.
6932 const nsRect rect
= TransformUntransformedBounds(
6933 aBuilder
, accTransform
.GetCurrentTransform());
6934 aBuilder
->AccumulateRect(rect
);
6937 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6938 nsDisplayListBuilder
* aBuilder
) {
6939 MOZ_ASSERT(mFrame
->Combines3DTransformWithAncestors() ||
6940 IsTransformSeparator());
6941 // Updating is not going through to child 3D context.
6942 ComputeBounds(aBuilder
);
6945 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder
* aBuilder
) {
6946 UpdateUntransformedBounds(aBuilder
);
6948 if (IsTransformSeparator()) {
6949 MOZ_ASSERT(GetTransform().IsIdentity());
6950 mBounds
= mChildBounds
;
6954 if (mFrame
->Extend3DContext()) {
6955 if (!Combines3DTransformWithAncestors()) {
6956 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6957 // collect the bounds from the child transforms.
6958 UpdateBoundsFor3D(aBuilder
);
6960 // With nested 3D transforms, the 2D bounds might not be useful.
6967 MOZ_ASSERT(!mFrame
->Extend3DContext());
6969 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
6970 // but mix-blend-mode relies on having bounds set. See bug 1556956.
6972 // A stand-alone transform.
6973 mBounds
= TransformUntransformedBounds(aBuilder
, GetTransform());
6976 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder
* aBuilder
) {
6977 MOZ_ASSERT(mFrame
->Extend3DContext() &&
6978 !mFrame
->Combines3DTransformWithAncestors() &&
6979 !IsTransformSeparator());
6981 // Always start updating from an establisher of a 3D rendering context.
6982 nsDisplayListBuilder::AutoAccumulateRect
accRect(aBuilder
);
6983 nsDisplayListBuilder::AutoAccumulateTransform
accTransform(aBuilder
);
6984 accTransform
.StartRoot();
6985 ComputeBounds(aBuilder
);
6986 mBounds
= aBuilder
->GetAccumulatedRect();
6989 void nsDisplayTransform::UpdateUntransformedBounds(
6990 nsDisplayListBuilder
* aBuilder
) {
6991 mChildBounds
= GetChildren()->GetClippedBoundsWithRespectToASR(
6992 aBuilder
, mActiveScrolledRoot
);
6999 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
7000 void nsDisplayTransform::HitTest(nsDisplayListBuilder
* aBuilder
,
7001 const nsRect
& aRect
, HitTestState
* aState
,
7002 nsTArray
<nsIFrame
*>* aOutFrames
) {
7003 if (aState
->mInPreserves3D
) {
7004 GetChildren()->HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
7008 /* Here's how this works:
7009 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
7011 * 2. Invert the matrix.
7012 * 3. Use it to transform the rect into the correct space.
7013 * 4. Pass that rect down through to the list's version of HitTest.
7015 // GetTransform always operates in dev pixels.
7016 float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7017 Matrix4x4 matrix
= GetAccumulatedPreserved3DTransform(aBuilder
);
7019 if (!IsFrameVisible(mFrame
, matrix
)) {
7023 const bool oldHitOccludingItem
= aState
->mHitOccludingItem
;
7025 /* We want to go from transformed-space to regular space.
7026 * Thus we have to invert the matrix, which normally does
7027 * the reverse operation (e.g. regular->transformed)
7030 /* Now, apply the transform and pass it down the channel. */
7032 nsRect resultingRect
;
7033 // Magic width/height indicating we're hit testing a point, not a rect
7034 const bool testingPoint
= aRect
.width
== 1 && aRect
.height
== 1;
7037 matrix
.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect
.x
, factor
),
7038 NSAppUnitsToFloatPixels(aRect
.y
, factor
)));
7039 if (!point
.HasPositiveWCoord()) {
7043 Point point2d
= point
.As2DPoint();
7046 nsRect(NSFloatPixelsToAppUnits(float(point2d
.x
), factor
),
7047 NSFloatPixelsToAppUnits(float(point2d
.y
), factor
), 1, 1);
7050 Rect
originalRect(NSAppUnitsToFloatPixels(aRect
.x
, factor
),
7051 NSAppUnitsToFloatPixels(aRect
.y
, factor
),
7052 NSAppUnitsToFloatPixels(aRect
.width
, factor
),
7053 NSAppUnitsToFloatPixels(aRect
.height
, factor
));
7055 Rect
childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds
.x
, factor
),
7056 NSAppUnitsToFloatPixels(mChildBounds
.y
, factor
),
7057 NSAppUnitsToFloatPixels(mChildBounds
.width
, factor
),
7058 NSAppUnitsToFloatPixels(mChildBounds
.height
, factor
));
7060 Rect rect
= matrix
.ProjectRectBounds(originalRect
, childGfxBounds
);
7063 nsRect(NSFloatPixelsToAppUnits(float(rect
.X()), factor
),
7064 NSFloatPixelsToAppUnits(float(rect
.Y()), factor
),
7065 NSFloatPixelsToAppUnits(float(rect
.Width()), factor
),
7066 NSFloatPixelsToAppUnits(float(rect
.Height()), factor
));
7069 if (resultingRect
.IsEmpty()) {
7074 printf("Frame: %p\n", dynamic_cast<void*>(mFrame
));
7075 printf(" Untransformed point: (%f, %f)\n", resultingRect
.X(),
7077 uint32_t originalFrameCount
= aOutFrames
.Length();
7080 GetChildren()->HitTest(aBuilder
, resultingRect
, aState
, aOutFrames
);
7082 if (aState
->mHitOccludingItem
&& !testingPoint
&&
7083 !mChildBounds
.Contains(aRect
)) {
7084 MOZ_ASSERT(aBuilder
->HitTestIsForVisibility());
7085 // We're hit-testing a rect that's bigger than our child bounds, but
7086 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so
7087 // we can't stop hit-testing altogether.
7089 // FIXME(emilio): I think this means that theoretically we might include
7090 // some frames fully behind other transformed-but-opaque frames? Then again
7091 // that's our pre-existing behavior for other untransformed content that
7092 // doesn't fill the whole rect. To be fully correct I think we'd need proper
7093 // "known occluded region" tracking, but that might be overkill for our
7095 aState
->mHitOccludingItem
= oldHitOccludingItem
;
7099 if (originalFrameCount
!= aOutFrames
.Length())
7100 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7101 dynamic_cast<void*>(aOutFrames
.ElementAt(0)));
7102 printf("=== end of hit test ===\n");
7106 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder
* aBuilder
,
7107 const nsPoint
& aPoint
) {
7108 // GetTransform always operates in dev pixels.
7109 float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7110 Matrix4x4 matrix
= GetAccumulatedPreserved3DTransform(aBuilder
);
7112 NS_ASSERTION(IsFrameVisible(mFrame
, matrix
),
7113 "We can't have hit a frame that isn't visible!");
7115 Matrix4x4 inverse
= matrix
;
7118 inverse
.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint
.x
, factor
),
7119 NSAppUnitsToFloatPixels(aPoint
.y
, factor
)));
7121 Point point2d
= point
.As2DPoint();
7123 Point3D transformed
= matrix
.TransformPoint(Point3D(point2d
.x
, point2d
.y
, 0));
7124 return transformed
.z
;
7127 /* The transform is opaque iff the transform consists solely of scales and
7128 * translations and if the underlying content is opaque. Thus if the transform
7135 * We need b and c to be zero.
7137 * We also need to check whether the underlying opaque content completely fills
7138 * our visible rect. We use UntransformRect which expands to the axis-aligned
7139 * bounding rect, but that's OK since if
7140 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7141 * certainly contains the actual (non-axis-aligned) untransformed rect.
7143 nsRegion
nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
7144 bool* aSnap
) const {
7147 nsRect untransformedVisible
;
7148 if (!UntransformBuildingRect(aBuilder
, &untransformedVisible
)) {
7152 const Matrix4x4Flagged
& matrix
= GetTransform();
7154 if (!matrix
.Is2D(&matrix2d
) || !matrix2d
.PreservesAxisAlignedRectangles()) {
7161 const nsRect bounds
= GetUntransformedBounds(aBuilder
, &tmpSnap
);
7162 const nsRegion opaque
=
7163 ::mozilla::GetOpaqueRegion(aBuilder
, GetChildren(), bounds
);
7165 if (opaque
.Contains(untransformedVisible
)) {
7166 result
= GetBuildingRect().Intersect(GetBounds(aBuilder
, &tmpSnap
));
7171 nsRect
nsDisplayTransform::GetComponentAlphaBounds(
7172 nsDisplayListBuilder
* aBuilder
) const {
7173 if (GetChildren()->GetComponentAlphaBounds(aBuilder
).IsEmpty()) {
7178 return GetBounds(aBuilder
, &snap
);
7181 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7182 * the smallest rectangle (in app space) containing the transformed image of
7183 * that rectangle. That is, it takes the four corners of the rectangle,
7184 * transforms them according to the matrix associated with the specified frame,
7185 * then returns the smallest rectangle containing the four transformed points.
7187 * @param aUntransformedBounds The rectangle (in app units) to transform.
7188 * @param aFrame The frame whose transformation should be applied.
7189 * @param aOrigin The delta from the frame origin to the coordinate space origin
7190 * @return The smallest rectangle containing the image of the transformed
7193 nsRect
nsDisplayTransform::TransformRect(const nsRect
& aUntransformedBounds
,
7194 const nsIFrame
* aFrame
,
7195 TransformReferenceBox
& aRefBox
) {
7196 MOZ_ASSERT(aFrame
, "Can't take the transform based on a null frame!");
7198 float factor
= aFrame
->PresContext()->AppUnitsPerDevPixel();
7200 FrameTransformProperties
props(aFrame
, aRefBox
, factor
);
7201 return nsLayoutUtils::MatrixTransformRect(
7202 aUntransformedBounds
,
7203 GetResultingTransformMatrixInternal(props
, aRefBox
, nsPoint(), factor
,
7204 kTransformRectFlags
),
7208 bool nsDisplayTransform::UntransformRect(const nsRect
& aTransformedBounds
,
7209 const nsRect
& aChildBounds
,
7210 const nsIFrame
* aFrame
,
7212 MOZ_ASSERT(aFrame
, "Can't take the transform based on a null frame!");
7214 float factor
= aFrame
->PresContext()->AppUnitsPerDevPixel();
7215 Matrix4x4 transform
= GetResultingTransformMatrix(aFrame
, nsPoint(), factor
,
7216 kTransformRectFlags
);
7217 return UntransformRect(aTransformedBounds
, aChildBounds
, transform
, factor
,
7221 bool nsDisplayTransform::UntransformRect(const nsRect
& aTransformedBounds
,
7222 const nsRect
& aChildBounds
,
7223 const Matrix4x4
& aMatrix
,
7224 float aAppUnitsPerPixel
,
7226 if (aMatrix
.IsSingular()) {
7231 NSAppUnitsToFloatPixels(aTransformedBounds
.x
, aAppUnitsPerPixel
),
7232 NSAppUnitsToFloatPixels(aTransformedBounds
.y
, aAppUnitsPerPixel
),
7233 NSAppUnitsToFloatPixels(aTransformedBounds
.width
, aAppUnitsPerPixel
),
7234 NSAppUnitsToFloatPixels(aTransformedBounds
.height
, aAppUnitsPerPixel
));
7236 RectDouble
childGfxBounds(
7237 NSAppUnitsToFloatPixels(aChildBounds
.x
, aAppUnitsPerPixel
),
7238 NSAppUnitsToFloatPixels(aChildBounds
.y
, aAppUnitsPerPixel
),
7239 NSAppUnitsToFloatPixels(aChildBounds
.width
, aAppUnitsPerPixel
),
7240 NSAppUnitsToFloatPixels(aChildBounds
.height
, aAppUnitsPerPixel
));
7242 result
= aMatrix
.Inverse().ProjectRectBounds(result
, childGfxBounds
);
7243 *aOutRect
= nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result
),
7248 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder
* aBuilder
,
7249 const nsRect
& aRect
,
7250 nsRect
* aOutRect
) const {
7251 if (GetTransform().IsSingular()) {
7255 // GetTransform always operates in dev pixels.
7256 float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7257 RectDouble
result(NSAppUnitsToFloatPixels(aRect
.x
, factor
),
7258 NSAppUnitsToFloatPixels(aRect
.y
, factor
),
7259 NSAppUnitsToFloatPixels(aRect
.width
, factor
),
7260 NSAppUnitsToFloatPixels(aRect
.height
, factor
));
7263 nsRect childBounds
= GetUntransformedBounds(aBuilder
, &snap
);
7264 RectDouble
childGfxBounds(
7265 NSAppUnitsToFloatPixels(childBounds
.x
, factor
),
7266 NSAppUnitsToFloatPixels(childBounds
.y
, factor
),
7267 NSAppUnitsToFloatPixels(childBounds
.width
, factor
),
7268 NSAppUnitsToFloatPixels(childBounds
.height
, factor
));
7270 /* We want to untransform the matrix, so invert the transformation first! */
7271 result
= GetInverseTransform().ProjectRectBounds(result
, childGfxBounds
);
7273 *aOutRect
= nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result
), factor
);
7278 void nsDisplayTransform::WriteDebugInfo(std::stringstream
& aStream
) {
7279 aStream
<< GetTransform().GetMatrix();
7280 if (IsTransformSeparator()) {
7281 aStream
<< " transform-separator";
7283 if (IsLeafOf3DContext()) {
7284 aStream
<< " 3d-context-leaf";
7286 if (mFrame
->Extend3DContext()) {
7287 aStream
<< " extends-3d-context";
7289 if (mFrame
->Combines3DTransformWithAncestors()) {
7290 aStream
<< " combines-3d-with-ancestors";
7293 aStream
<< " prerender(";
7294 switch (mPrerenderDecision
) {
7295 case PrerenderDecision::No
:
7298 case PrerenderDecision::Partial
:
7299 aStream
<< "partial";
7301 case PrerenderDecision::Full
:
7306 aStream
<< " childrenBuildingRect" << mChildrenBuildingRect
;
7309 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder
* aBuilder
,
7311 nsDisplayList
* aList
)
7312 : nsPaintedDisplayItem(aBuilder
, aFrame
), mList(aBuilder
) {
7313 mList
.AppendToTop(aList
);
7314 MOZ_ASSERT(mList
.Length() == 1);
7315 MOZ_ASSERT(mList
.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM
);
7318 void nsDisplayPerspective::Paint(nsDisplayListBuilder
* aBuilder
,
7320 // Just directly recurse into children, since we'll include the persepctive
7321 // value in any nsDisplayTransform children.
7322 GetChildren()->Paint(aBuilder
, aCtx
,
7323 mFrame
->PresContext()->AppUnitsPerDevPixel());
7326 nsRegion
nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
7327 bool* aSnap
) const {
7328 if (!GetChildren()->GetTop()) {
7333 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder
, aSnap
);
7336 bool nsDisplayPerspective::CreateWebRenderCommands(
7337 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
7338 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
7339 nsDisplayListBuilder
* aDisplayListBuilder
) {
7340 float appUnitsPerPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7341 Matrix4x4 perspectiveMatrix
;
7342 DebugOnly
<bool> hasPerspective
= nsDisplayTransform::ComputePerspectiveMatrix(
7343 mFrame
, appUnitsPerPixel
, perspectiveMatrix
);
7344 MOZ_ASSERT(hasPerspective
, "Why did we create nsDisplayPerspective?");
7347 * ClipListToRange can remove our child after we were created.
7349 if (!GetChildren()->GetTop()) {
7354 * The resulting matrix is still in the coordinate space of the transformed
7355 * frame. Append a translation to the reference frame coordinates.
7357 nsDisplayTransform
* transform
=
7358 static_cast<nsDisplayTransform
*>(GetChildren()->GetTop());
7361 Point3D(NSAppUnitsToFloatPixels(transform
->ToReferenceFrame().x
,
7363 NSAppUnitsToFloatPixels(transform
->ToReferenceFrame().y
,
7366 Point3D
roundedOrigin(NS_round(newOrigin
.x
), NS_round(newOrigin
.y
), 0);
7368 perspectiveMatrix
.PostTranslate(roundedOrigin
);
7370 nsIFrame
* perspectiveFrame
=
7371 mFrame
->GetClosestFlattenedTreeAncestorPrimaryFrame();
7373 // Passing true here is always correct, since perspective always combines
7374 // transforms with the descendants. However that'd make WR do a lot of work
7375 // that it doesn't really need to do if there aren't other transforms forming
7376 // part of the 3D context.
7378 // WR knows how to treat perspective in that case, so the only thing we need
7379 // to do is to ensure we pass true when we're involved in a 3d context in any
7380 // other way via the transform-style property on either the transformed frame
7381 // or the perspective frame in order to not confuse WR's preserve-3d code in
7384 mFrame
->Extend3DContext() || perspectiveFrame
->Extend3DContext();
7386 wr::StackingContextParams params
;
7388 wr::WrTransformInfo transform_info
;
7389 transform_info
.transform
= wr::ToLayoutTransform(perspectiveMatrix
);
7390 transform_info
.key
= wr::SpatialKey(uint64_t(mFrame
), GetPerFrameKey(),
7391 wr::SpatialKeyKind::Perspective
);
7392 params
.mTransformPtr
= &transform_info
;
7394 params
.reference_frame_kind
= wr::WrReferenceFrameKind::Perspective
;
7395 params
.prim_flags
= !BackfaceIsHidden()
7396 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7397 : wr::PrimitiveFlags
{0};
7398 params
.SetPreserve3D(preserve3D
);
7400 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
7402 Maybe
<uint64_t> scrollingRelativeTo
;
7403 for (const auto* asr
= GetActiveScrolledRoot(); asr
; asr
= asr
->mParent
) {
7404 // In OOP documents, the root scrollable frame of the in-process root
7405 // document is always active, so using IsAncestorFrameCrossDocInProcess
7406 // should be fine here.
7407 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7408 asr
->mScrollableFrame
->GetScrolledFrame(), perspectiveFrame
)) {
7409 scrollingRelativeTo
.emplace(asr
->GetViewId());
7414 // We put the perspective reference frame wrapping the transformed frame,
7415 // even though there may be arbitrarily nested scroll frames in between.
7417 // We need to know how many ancestor scroll-frames are we nested in, in order
7418 // for the async scrolling code in WebRender to calculate the right
7419 // transformation for the perspective contents.
7420 params
.scrolling_relative_to
= scrollingRelativeTo
.ptrOr(nullptr);
7422 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
7425 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7426 GetChildren(), this, aDisplayListBuilder
, sc
, aBuilder
, aResources
);
7431 nsDisplayText::nsDisplayText(nsDisplayListBuilder
* aBuilder
,
7432 nsTextFrame
* aFrame
)
7433 : nsPaintedDisplayItem(aBuilder
, aFrame
),
7436 MOZ_COUNT_CTOR(nsDisplayText
);
7437 mBounds
= mFrame
->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7439 mBounds
.Inflate(mFrame
->PresContext()->AppUnitsPerDevPixel());
7440 mVisibleRect
= aBuilder
->GetVisibleRect() +
7441 aBuilder
->GetCurrentFrameOffsetToReferenceFrame();
7444 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager
* aManager
,
7445 nsDisplayListBuilder
* aBuilder
) const {
7446 auto* f
= static_cast<nsTextFrame
*>(mFrame
);
7448 if (f
->IsSelected()) {
7452 const nsStyleText
* textStyle
= f
->StyleText();
7453 if (textStyle
->HasTextShadow()) {
7457 nsTextFrame::TextDecorations decorations
;
7458 f
->GetTextDecorations(f
->PresContext(), nsTextFrame::eResolvedColors
,
7460 return !decorations
.HasDecorationLines();
7463 void nsDisplayText::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
7464 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS
);
7465 // We don't pass mVisibleRect here, since this can be called from within
7466 // the WebRender fallback painting path, and we don't want to issue
7467 // recorded commands that are dependent on the visible/building rect.
7468 RenderToContext(aCtx
, aBuilder
, GetPaintRect(aBuilder
, aCtx
));
7470 auto* textFrame
= static_cast<nsTextFrame
*>(mFrame
);
7471 LCPTextFrameHelper::MaybeUnionTextFrame(textFrame
,
7472 mBounds
- ToReferenceFrame());
7475 bool nsDisplayText::CreateWebRenderCommands(
7476 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
7477 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
7478 nsDisplayListBuilder
* aDisplayListBuilder
) {
7479 auto* f
= static_cast<nsTextFrame
*>(mFrame
);
7480 auto appUnitsPerDevPixel
= f
->PresContext()->AppUnitsPerDevPixel();
7482 nsRect bounds
= f
->WebRenderBounds() + ToReferenceFrame();
7484 bounds
.Inflate(appUnitsPerDevPixel
);
7486 if (bounds
.IsEmpty()) {
7490 // For large font sizes, punt to a blob image, to avoid the blurry rendering
7491 // that results from WR clamping the glyph size used for rasterization.
7493 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7495 // This is not strictly accurate, as final used font sizes might not be the
7496 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7497 // altering the used size of the font actually used).
7498 // It also fails to consider how transforms might affect the device-font-size
7499 // that webrender uses (and clamps).
7500 // But it should be near enough for practical purposes; the limitations just
7501 // mean we might sometimes end up with webrender still applying some bitmap
7502 // scaling, or bail out when we didn't really need to.
7503 constexpr float kWebRenderFontSizeLimit
= 320.0;
7504 f
->EnsureTextRun(nsTextFrame::eInflated
);
7505 gfxTextRun
* textRun
= f
->GetTextRun(nsTextFrame::eInflated
);
7507 textRun
->GetFontGroup()->GetStyle()->size
> kWebRenderFontSizeLimit
) {
7511 gfx::Point deviceOffset
=
7512 LayoutDevicePoint::FromAppUnits(bounds
.TopLeft(), appUnitsPerDevPixel
)
7515 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7516 // frames) lets us early reject a bunch of things.
7517 nsRect visible
= mVisibleRect
;
7519 // Add the "source rect" area from which the given shadows could intersect
7520 // with mVisibleRect, and which therefore needs to included in the paint
7521 // operation, to the `visible` rect that we will use to limit the bounds of
7522 // what we send to the renderer.
7523 auto addShadowSourceToVisible
= [&](Span
<const StyleSimpleShadow
> aShadows
) {
7524 for (const auto& shadow
: aShadows
) {
7525 nsRect sourceRect
= mVisibleRect
;
7526 // Negate the offsets, because we're looking for the "source" rect that
7527 // could cast a shadow into the visible rect, rather than a "target" area
7528 // onto which the visible rect would cast a shadow.
7529 sourceRect
.MoveBy(-shadow
.horizontal
.ToAppUnits(),
7530 -shadow
.vertical
.ToAppUnits());
7531 // Inflate to account for the shadow blur.
7532 sourceRect
.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
7533 shadow
.blur
.ToAppUnits(), appUnitsPerDevPixel
));
7534 visible
.OrWith(sourceRect
);
7538 // Shadows can translate things back into view, so we enlarge the notional
7539 // "visible" rect to ensure we don't skip painting relevant parts that might
7540 // cast a shadow within the visible area.
7541 addShadowSourceToVisible(f
->StyleText()->mTextShadow
.AsSpan());
7543 // Similarly for shadows that may be cast by ::selection.
7544 if (f
->IsSelected()) {
7545 nsTextPaintStyle
textPaint(f
);
7546 Span
<const StyleSimpleShadow
> shadows
;
7547 f
->GetSelectionTextShadow(SelectionType::eNormal
, textPaint
, &shadows
);
7548 addShadowSourceToVisible(shadows
);
7551 // Inflate a little extra to allow for potential antialiasing "blur".
7552 visible
.Inflate(3 * appUnitsPerDevPixel
);
7553 bounds
= bounds
.Intersect(visible
);
7555 gfxContext
* textDrawer
= aBuilder
.GetTextContext(aResources
, aSc
, aManager
,
7556 this, bounds
, deviceOffset
);
7558 LCPTextFrameHelper::MaybeUnionTextFrame(f
, bounds
- ToReferenceFrame());
7560 aBuilder
.StartGroup(this);
7562 RenderToContext(textDrawer
, aDisplayListBuilder
, mVisibleRect
,
7563 aBuilder
.GetInheritedOpacity(), true);
7564 const bool result
= textDrawer
->GetTextDrawer()->Finish();
7567 aBuilder
.FinishGroup();
7569 aBuilder
.CancelGroup(true);
7575 void nsDisplayText::RenderToContext(gfxContext
* aCtx
,
7576 nsDisplayListBuilder
* aBuilder
,
7577 const nsRect
& aVisibleRect
, float aOpacity
,
7578 bool aIsRecording
) {
7579 nsTextFrame
* f
= static_cast<nsTextFrame
*>(mFrame
);
7581 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7582 // antialiased pixels beyond the measured text extents.
7583 // This is temporary until we do this in the actual calculation of text
7585 auto A2D
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7586 LayoutDeviceRect extraVisible
=
7587 LayoutDeviceRect::FromAppUnits(aVisibleRect
, A2D
);
7588 extraVisible
.Inflate(1);
7590 gfxRect
pixelVisible(extraVisible
.x
, extraVisible
.y
, extraVisible
.width
,
7591 extraVisible
.height
);
7592 pixelVisible
.Inflate(2);
7593 pixelVisible
.RoundOut();
7595 gfxClipAutoSaveRestore
autoSaveClip(aCtx
);
7596 if (!aBuilder
->IsForGenerateGlyphMask() && !aIsRecording
) {
7597 autoSaveClip
.Clip(pixelVisible
);
7600 NS_ASSERTION(mVisIStartEdge
>= 0, "illegal start edge");
7601 NS_ASSERTION(mVisIEndEdge
>= 0, "illegal end edge");
7603 gfxContextMatrixAutoSaveRestore matrixSR
;
7605 nsPoint framePt
= ToReferenceFrame();
7606 if (f
->Style()->IsTextCombined()) {
7607 float scaleFactor
= nsTextFrame::GetTextCombineScaleFactor(f
);
7608 if (scaleFactor
!= 1.0f
) {
7609 if (auto* textDrawer
= aCtx
->GetTextDrawer()) {
7610 // WebRender doesn't support scaling text like this yet
7611 textDrawer
->FoundUnsupportedFeature();
7614 matrixSR
.SetContext(aCtx
);
7615 // Setup matrix to compress text for text-combine-upright if
7616 // necessary. This is done here because we want selection be
7617 // compressed at the same time as text.
7618 gfxPoint pt
= nsLayoutUtils::PointToGfxPoint(framePt
, A2D
);
7619 gfxTextRun
* textRun
= f
->GetTextRun(nsTextFrame::eInflated
);
7620 if (textRun
&& textRun
->IsRightToLeft()) {
7621 pt
.x
+= gfxFloat(f
->GetSize().width
) / A2D
;
7623 gfxMatrix mat
= aCtx
->CurrentMatrixDouble()
7625 .PreScale(scaleFactor
, 1.0)
7627 aCtx
->SetMatrixDouble(mat
);
7630 nsTextFrame::PaintTextParams
params(aCtx
);
7631 params
.framePt
= gfx::Point(framePt
.x
, framePt
.y
);
7632 params
.dirtyRect
= extraVisible
;
7634 if (aBuilder
->IsForGenerateGlyphMask()) {
7635 params
.state
= nsTextFrame::PaintTextParams::GenerateTextMask
;
7637 params
.state
= nsTextFrame::PaintTextParams::PaintText
;
7640 f
->PaintText(params
, mVisIStartEdge
, mVisIEndEdge
, ToReferenceFrame(),
7641 f
->IsSelected(), aOpacity
);
7644 // This could go to nsDisplayListInvalidation.h, but
7645 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7646 // would produce circular dependencies.
7647 class nsDisplayTextGeometry
: public nsDisplayItemGenericGeometry
{
7649 nsDisplayTextGeometry(nsDisplayText
* aItem
, nsDisplayListBuilder
* aBuilder
)
7650 : nsDisplayItemGenericGeometry(aItem
, aBuilder
),
7651 mVisIStartEdge(aItem
->VisIStartEdge()),
7652 mVisIEndEdge(aItem
->VisIEndEdge()) {
7653 nsTextFrame
* f
= static_cast<nsTextFrame
*>(aItem
->Frame());
7654 f
->GetTextDecorations(f
->PresContext(), nsTextFrame::eResolvedColors
,
7659 * We store the computed text decorations here since they are
7660 * computed using style data from parent frames. Any changes to these
7661 * styles will only invalidate the parent frame and not this frame.
7663 nsTextFrame::TextDecorations mDecorations
;
7664 nscoord mVisIStartEdge
;
7665 nscoord mVisIEndEdge
;
7668 nsDisplayItemGeometry
* nsDisplayText::AllocateGeometry(
7669 nsDisplayListBuilder
* aBuilder
) {
7670 return new nsDisplayTextGeometry(this, aBuilder
);
7673 void nsDisplayText::ComputeInvalidationRegion(
7674 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
7675 nsRegion
* aInvalidRegion
) const {
7676 const nsDisplayTextGeometry
* geometry
=
7677 static_cast<const nsDisplayTextGeometry
*>(aGeometry
);
7678 nsTextFrame
* f
= static_cast<nsTextFrame
*>(mFrame
);
7680 nsTextFrame::TextDecorations decorations
;
7681 f
->GetTextDecorations(f
->PresContext(), nsTextFrame::eResolvedColors
,
7685 const nsRect
& newRect
= geometry
->mBounds
;
7686 nsRect oldRect
= GetBounds(aBuilder
, &snap
);
7687 if (decorations
!= geometry
->mDecorations
||
7688 mVisIStartEdge
!= geometry
->mVisIStartEdge
||
7689 mVisIEndEdge
!= geometry
->mVisIEndEdge
||
7690 !oldRect
.IsEqualInterior(newRect
) ||
7691 !geometry
->mBorderRect
.IsEqualInterior(GetBorderRect())) {
7692 aInvalidRegion
->Or(oldRect
, newRect
);
7696 void nsDisplayText::WriteDebugInfo(std::stringstream
& aStream
) {
7700 nsTextFrame
* f
= static_cast<nsTextFrame
*>(mFrame
);
7704 aStream
<< buf
.get() << "\")";
7708 nsDisplayEffectsBase::nsDisplayEffectsBase(
7709 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
7710 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aClearClipChain
)
7711 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
,
7713 MOZ_COUNT_CTOR(nsDisplayEffectsBase
);
7716 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder
* aBuilder
,
7718 nsDisplayList
* aList
)
7719 : nsDisplayWrapList(aBuilder
, aFrame
, aList
) {
7720 MOZ_COUNT_CTOR(nsDisplayEffectsBase
);
7723 nsRegion
nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
7724 bool* aSnap
) const {
7729 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder
* aBuilder
,
7730 const nsRect
& aRect
, HitTestState
* aState
,
7731 nsTArray
<nsIFrame
*>* aOutFrames
) {
7732 nsPoint
rectCenter(aRect
.x
+ aRect
.width
/ 2, aRect
.y
+ aRect
.height
/ 2);
7733 if (SVGIntegrationUtils::HitTestFrameForEffects(
7734 mFrame
, rectCenter
- ToReferenceFrame())) {
7735 mList
.HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
7739 gfxRect
nsDisplayEffectsBase::BBoxInUserSpace() const {
7740 return SVGUtils::GetBBox(mFrame
);
7743 gfxPoint
nsDisplayEffectsBase::UserSpaceOffset() const {
7744 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame
);
7747 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7748 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
7749 nsRegion
* aInvalidRegion
) const {
7750 const auto* geometry
=
7751 static_cast<const nsDisplaySVGEffectGeometry
*>(aGeometry
);
7753 nsRect bounds
= GetBounds(aBuilder
, &snap
);
7754 if (geometry
->mFrameOffsetToReferenceFrame
!= ToReferenceFrame() ||
7755 geometry
->mUserSpaceOffset
!= UserSpaceOffset() ||
7756 !geometry
->mBBox
.IsEqualInterior(BBoxInUserSpace())) {
7757 // Filter and mask output can depend on the location of the frame's user
7758 // space and on the frame's BBox. We need to invalidate if either of these
7759 // change relative to the reference frame.
7760 // Invalidations from our inactive layer manager are not enough to catch
7761 // some of these cases because filters can produce output even if there's
7762 // nothing in the filter input.
7763 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
7767 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7768 if (mFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
7769 ISVGDisplayableFrame
* svgFrame
= do_QueryFrame(mFrame
);
7773 if (auto* svgElement
= SVGElement::FromNode(mFrame
->GetContent())) {
7774 // The SVG spec says only to draw filters if the element
7775 // has valid dimensions.
7776 return svgElement
->HasValidDimensions();
7784 using PaintFramesParams
= SVGIntegrationUtils::PaintFramesParams
;
7786 static void ComputeMaskGeometry(PaintFramesParams
& aParams
) {
7787 // Properties are added lazily and may have been removed by a restyle, so
7788 // make sure all applicable ones are set again.
7789 nsIFrame
* firstFrame
=
7790 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams
.frame
);
7792 const nsStyleSVGReset
* svgReset
= firstFrame
->StyleSVGReset();
7794 nsTArray
<SVGMaskFrame
*> maskFrames
;
7795 // XXX check return value?
7796 SVGObserverUtils::GetAndObserveMasks(firstFrame
, &maskFrames
);
7798 if (maskFrames
.Length() == 0) {
7802 gfxContext
& ctx
= aParams
.ctx
;
7803 nsIFrame
* frame
= aParams
.frame
;
7805 nsPoint offsetToUserSpace
=
7806 nsLayoutUtils::ComputeOffsetToUserSpace(aParams
.builder
, aParams
.frame
);
7808 auto cssToDevScale
= frame
->PresContext()->CSSToDevPixelScale();
7809 int32_t appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
7811 gfxPoint devPixelOffsetToUserSpace
=
7812 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace
, appUnitsPerDevPixel
);
7814 gfxContextMatrixAutoSaveRestore
matSR(&ctx
);
7815 ctx
.SetMatrixDouble(
7816 ctx
.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace
));
7818 // Convert boaderArea and dirtyRect to user space.
7819 nsRect userSpaceBorderArea
= aParams
.borderArea
- offsetToUserSpace
;
7820 nsRect userSpaceDirtyRect
= aParams
.dirtyRect
- offsetToUserSpace
;
7822 // Union all mask layer rectangles in user space.
7823 LayoutDeviceRect maskInUserSpace
;
7824 for (size_t i
= 0; i
< maskFrames
.Length(); i
++) {
7825 SVGMaskFrame
* maskFrame
= maskFrames
[i
];
7826 LayoutDeviceRect currentMaskSurfaceRect
;
7829 auto rect
= maskFrame
->GetMaskArea(aParams
.frame
);
7830 currentMaskSurfaceRect
=
7831 CSSRect::FromUnknownRect(ToRect(rect
)) * cssToDevScale
;
7833 nsCSSRendering::ImageLayerClipState clipState
;
7834 nsCSSRendering::GetImageLayerClip(
7835 svgReset
->mMask
.mLayers
[i
], frame
, *frame
->StyleBorder(),
7836 userSpaceBorderArea
, userSpaceDirtyRect
,
7837 /* aWillPaintBorder = */ false, appUnitsPerDevPixel
, &clipState
);
7838 currentMaskSurfaceRect
= LayoutDeviceRect::FromUnknownRect(
7839 ToRect(clipState
.mDirtyRectInDevPx
));
7842 maskInUserSpace
= maskInUserSpace
.Union(currentMaskSurfaceRect
);
7845 if (!maskInUserSpace
.IsEmpty()) {
7846 aParams
.maskRect
= Some(maskInUserSpace
);
7848 aParams
.maskRect
= Nothing();
7852 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7853 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
7854 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aWrapsBackdropFilter
)
7855 : nsDisplayEffectsBase(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
7856 mWrapsBackdropFilter(aWrapsBackdropFilter
) {
7857 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths
);
7859 nsPresContext
* presContext
= mFrame
->PresContext();
7861 aBuilder
->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE
;
7862 const nsStyleSVGReset
* svgReset
= aFrame
->StyleSVGReset();
7863 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i
, svgReset
->mMask
) {
7864 const auto& layer
= svgReset
->mMask
.mLayers
[i
];
7865 if (!layer
.mImage
.IsResolved()) {
7868 const nsRect
& borderArea
= mFrame
->GetRectRelativeToSelf();
7869 // NOTE(emilio): We only care about the dest rect so we don't bother
7870 // computing a clip.
7871 bool isTransformedFixed
= false;
7872 nsBackgroundLayerState state
= nsCSSRendering::PrepareImageLayer(
7873 presContext
, aFrame
, flags
, borderArea
, borderArea
, layer
,
7874 &isTransformedFixed
);
7875 mDestRects
.AppendElement(state
.mDestArea
);
7879 static bool CanMergeDisplayMaskFrame(nsIFrame
* aFrame
) {
7880 // Do not merge items for box-decoration-break:clone elements,
7881 // since each box should have its own mask in that case.
7882 if (aFrame
->StyleBorder()->mBoxDecorationBreak
==
7883 StyleBoxDecorationBreak::Clone
) {
7887 // Do not merge if either frame has a mask. Continuation frames should apply
7888 // the mask independently (just like nsDisplayBackgroundImage).
7889 if (aFrame
->StyleSVGReset()->HasMask()) {
7896 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem
* aItem
) const {
7897 // Items for the same content element should be merged into a single
7898 // compositing group.
7899 if (!HasDifferentFrame(aItem
) || !HasSameTypeAndClip(aItem
) ||
7900 !HasSameContent(aItem
)) {
7904 return CanMergeDisplayMaskFrame(mFrame
) &&
7905 CanMergeDisplayMaskFrame(aItem
->Frame());
7908 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7909 if (!ValidateSVGFrame()) {
7913 return SVGUtils::DetermineMaskUsage(mFrame
, false).UsingMaskOrClipPath();
7916 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder
* aBuilder
,
7917 gfxContext
* aMaskContext
,
7918 bool aHandleOpacity
,
7919 bool* aMaskPainted
) {
7920 MOZ_ASSERT(aMaskContext
->GetDrawTarget()->GetFormat() == SurfaceFormat::A8
);
7922 imgDrawingParams
imgParams(aBuilder
->GetImageDecodeFlags());
7923 nsRect borderArea
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
7924 PaintFramesParams
params(*aMaskContext
, mFrame
, mBounds
, borderArea
, aBuilder
,
7925 aHandleOpacity
, imgParams
);
7926 ComputeMaskGeometry(params
);
7927 bool maskIsComplete
= false;
7928 bool painted
= SVGIntegrationUtils::PaintMask(params
, maskIsComplete
);
7930 *aMaskPainted
= painted
;
7933 return maskIsComplete
&&
7934 (imgParams
.result
== ImgDrawResult::SUCCESS
||
7935 imgParams
.result
== ImgDrawResult::SUCCESS_NOT_COMPLETE
||
7936 imgParams
.result
== ImgDrawResult::WRONG_SIZE
);
7939 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7940 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
7941 nsRegion
* aInvalidRegion
) const {
7942 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder
, aGeometry
,
7945 const auto* geometry
=
7946 static_cast<const nsDisplayMasksAndClipPathsGeometry
*>(aGeometry
);
7948 nsRect bounds
= GetBounds(aBuilder
, &snap
);
7950 if (mDestRects
.Length() != geometry
->mDestRects
.Length()) {
7951 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
7953 for (size_t i
= 0; i
< mDestRects
.Length(); i
++) {
7954 if (!mDestRects
[i
].IsEqualInterior(geometry
->mDestRects
[i
])) {
7955 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
7962 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7963 nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
7964 const std::function
<void()>& aPaintChildren
) {
7965 // Clip the drawing target by mVisibleRect, which contains the visible
7966 // region of the target frame and its out-of-flow and inflow descendants.
7967 Rect bounds
= NSRectToRect(GetPaintRect(aBuilder
, aCtx
),
7968 mFrame
->PresContext()->AppUnitsPerDevPixel());
7970 gfxClipAutoSaveRestore
autoSaveClip(aCtx
);
7971 autoSaveClip
.Clip(bounds
);
7973 imgDrawingParams
imgParams(aBuilder
->GetImageDecodeFlags());
7974 nsRect borderArea
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
7975 PaintFramesParams
params(*aCtx
, mFrame
, GetPaintRect(aBuilder
, aCtx
),
7976 borderArea
, aBuilder
, false, imgParams
);
7978 ComputeMaskGeometry(params
);
7980 SVGIntegrationUtils::PaintMaskAndClipPath(params
, aPaintChildren
);
7983 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder
* aBuilder
,
7985 if (!IsValidMask()) {
7988 PaintWithContentsPaintCallback(aBuilder
, aCtx
, [&] {
7989 GetChildren()->Paint(aBuilder
, aCtx
,
7990 mFrame
->PresContext()->AppUnitsPerDevPixel());
7994 static Maybe
<wr::WrClipChainId
> CreateSimpleClipRegion(
7995 const nsDisplayMasksAndClipPaths
& aDisplayItem
,
7996 wr::DisplayListBuilder
& aBuilder
) {
7997 nsIFrame
* frame
= aDisplayItem
.Frame();
7998 const auto* style
= frame
->StyleSVGReset();
7999 MOZ_ASSERT(style
->HasClipPath() || style
->HasMask());
8000 if (!SVGUtils::DetermineMaskUsage(frame
, false).IsSimpleClipShape()) {
8004 const auto& clipPath
= style
->mClipPath
;
8005 const auto& shape
= *clipPath
.AsShape()._0
;
8007 auto appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
8008 const nsRect refBox
=
8009 nsLayoutUtils::ComputeClipPathGeometryBox(frame
, clipPath
.AsShape()._1
);
8011 wr::WrClipId clipId
{};
8013 switch (shape
.tag
) {
8014 case StyleBasicShape::Tag::Rect
: {
8016 ShapeUtils::ComputeInsetRect(shape
.AsRect().rect
, refBox
) +
8017 aDisplayItem
.ToReferenceFrame();
8019 nscoord radii
[8] = {0};
8020 if (ShapeUtils::ComputeRectRadii(shape
.AsRect().round
, refBox
, rect
,
8022 clipId
= aBuilder
.DefineRoundedRectClip(
8024 wr::ToComplexClipRegion(rect
, radii
, appUnitsPerDevPixel
));
8026 clipId
= aBuilder
.DefineRectClip(
8027 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8028 rect
, appUnitsPerDevPixel
)));
8033 case StyleBasicShape::Tag::Ellipse
:
8034 case StyleBasicShape::Tag::Circle
: {
8035 nsPoint center
= ShapeUtils::ComputeCircleOrEllipseCenter(shape
, refBox
);
8038 if (shape
.IsEllipse()) {
8039 radii
= ShapeUtils::ComputeEllipseRadii(shape
, center
, refBox
);
8041 nscoord radius
= ShapeUtils::ComputeCircleRadius(shape
, center
, refBox
);
8042 radii
= {radius
, radius
};
8045 nsRect
ellipseRect(aDisplayItem
.ToReferenceFrame() + center
-
8046 nsPoint(radii
.width
, radii
.height
),
8049 nscoord ellipseRadii
[8];
8050 for (const auto corner
: AllPhysicalHalfCorners()) {
8051 ellipseRadii
[corner
] =
8052 HalfCornerIsX(corner
) ? radii
.width
: radii
.height
;
8055 clipId
= aBuilder
.DefineRoundedRectClip(
8056 Nothing(), wr::ToComplexClipRegion(ellipseRect
, ellipseRadii
,
8057 appUnitsPerDevPixel
));
8062 // Please don't add more exceptions, try to find a way to define the clip
8063 // without using a mask image.
8065 // And if you _really really_ need to add an exception, add it to
8066 // SVGUtils::DetermineMaskUsage
8067 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8071 wr::WrClipChainId clipChainId
= aBuilder
.DefineClipChain({clipId
}, true);
8073 return Some(clipChainId
);
8076 static void FillPolygonDataForDisplayItem(
8077 const nsDisplayMasksAndClipPaths
& aDisplayItem
,
8078 nsTArray
<wr::LayoutPoint
>& aPoints
, wr::FillRule
& aFillRule
) {
8079 nsIFrame
* frame
= aDisplayItem
.Frame();
8080 const auto* style
= frame
->StyleSVGReset();
8081 bool isPolygon
= style
->HasClipPath() && style
->mClipPath
.IsShape() &&
8082 style
->mClipPath
.AsShape()._0
->IsPolygon();
8087 const auto& clipPath
= style
->mClipPath
;
8088 const auto& shape
= *clipPath
.AsShape()._0
;
8089 const nsRect refBox
=
8090 nsLayoutUtils::ComputeClipPathGeometryBox(frame
, clipPath
.AsShape()._1
);
8092 // We only fill polygon data for polygons that are below a complexity
8094 nsTArray
<nsPoint
> vertices
=
8095 ShapeUtils::ComputePolygonVertices(shape
, refBox
);
8096 if (vertices
.Length() > wr::POLYGON_CLIP_VERTEX_MAX
) {
8100 auto appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
8102 for (size_t i
= 0; i
< vertices
.Length(); ++i
) {
8103 wr::LayoutPoint point
= wr::ToLayoutPoint(
8104 LayoutDevicePoint::FromAppUnits(vertices
[i
], appUnitsPerDevPixel
));
8105 aPoints
.AppendElement(point
);
8108 aFillRule
= (shape
.AsPolygon().fill
== StyleFillRule::Nonzero
)
8109 ? wr::FillRule::Nonzero
8110 : wr::FillRule::Evenodd
;
8113 static Maybe
<wr::WrClipChainId
> CreateWRClipPathAndMasks(
8114 nsDisplayMasksAndClipPaths
* aDisplayItem
, const LayoutDeviceRect
& aBounds
,
8115 wr::IpcResourceUpdateQueue
& aResources
, wr::DisplayListBuilder
& aBuilder
,
8116 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8117 nsDisplayListBuilder
* aDisplayListBuilder
) {
8118 if (auto clip
= CreateSimpleClipRegion(*aDisplayItem
, aBuilder
)) {
8122 Maybe
<wr::ImageMask
> mask
= aManager
->CommandBuilder().BuildWrMaskImage(
8123 aDisplayItem
, aBuilder
, aResources
, aSc
, aDisplayListBuilder
, aBounds
);
8128 // We couldn't create a simple clip region, but before we create an image
8129 // mask clip, see if we can get a polygon clip to add to it.
8130 nsTArray
<wr::LayoutPoint
> points
;
8131 wr::FillRule fillRule
= wr::FillRule::Nonzero
;
8132 FillPolygonDataForDisplayItem(*aDisplayItem
, points
, fillRule
);
8134 wr::WrClipId clipId
=
8135 aBuilder
.DefineImageMaskClip(mask
.ref(), points
, fillRule
);
8137 wr::WrClipChainId clipChainId
= aBuilder
.DefineClipChain({clipId
}, true);
8139 return Some(clipChainId
);
8142 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8143 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
8144 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8145 nsDisplayListBuilder
* aDisplayListBuilder
) {
8147 auto appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8148 nsRect displayBounds
= GetBounds(aDisplayListBuilder
, &snap
);
8149 LayoutDeviceRect bounds
=
8150 LayoutDeviceRect::FromAppUnits(displayBounds
, appUnitsPerDevPixel
);
8152 Maybe
<wr::WrClipChainId
> clip
= CreateWRClipPathAndMasks(
8153 this, bounds
, aResources
, aBuilder
, aSc
, aManager
, aDisplayListBuilder
);
8155 float oldOpacity
= aBuilder
.GetInheritedOpacity();
8157 Maybe
<StackingContextHelper
> layer
;
8158 const StackingContextHelper
* sc
= &aSc
;
8160 // Create a new stacking context to attach the mask to, ensuring the mask is
8161 // applied to the aggregate, and not the individual elements.
8163 // The stacking context shouldn't have any offset.
8164 bounds
.MoveTo(0, 0);
8166 Maybe
<float> opacity
=
8167 (SVGUtils::DetermineMaskUsage(mFrame
, false).IsSimpleClipShape() &&
8168 aBuilder
.GetInheritedOpacity() != 1.0f
)
8169 ? Some(aBuilder
.GetInheritedOpacity())
8172 wr::StackingContextParams params
;
8173 params
.clip
= wr::WrStackingContextClip::ClipChain(clip
->id
);
8174 params
.opacity
= opacity
.ptrOr(nullptr);
8175 if (mWrapsBackdropFilter
) {
8176 params
.flags
|= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER
;
8178 layer
.emplace(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
, params
,
8183 aBuilder
.SetInheritedOpacity(1.0f
);
8184 const DisplayItemClipChain
* oldClipChain
= aBuilder
.GetInheritedClipChain();
8185 aBuilder
.SetInheritedClipChain(nullptr);
8186 CreateWebRenderCommandsNewClipListOption(aBuilder
, aResources
, *sc
, aManager
,
8187 aDisplayListBuilder
, layer
.isSome());
8188 aBuilder
.SetInheritedOpacity(oldOpacity
);
8189 aBuilder
.SetInheritedClipChain(oldClipChain
);
8194 Maybe
<nsRect
> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8195 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
) const {
8196 if (const DisplayItemClip
* clip
=
8197 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR
)) {
8198 return Some(clip
->GetClipRect());
8200 // This item does not have a clip with respect to |aASR|. However, we
8201 // might still have finite bounds with respect to |aASR|. Check our
8203 nsDisplayList
* childList
= GetSameCoordinateSystemChildren();
8205 return Some(childList
->GetClippedBoundsWithRespectToASR(aBuilder
, aASR
));
8208 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
8213 #ifdef MOZ_DUMP_PAINTING
8214 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString
& aTo
) {
8215 nsIFrame
* firstFrame
=
8216 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame
);
8218 aTo
+= " effects=(";
8219 SVGClipPathFrame
* clipPathFrame
;
8220 // XXX Check return value?
8221 SVGObserverUtils::GetAndObserveClipPath(firstFrame
, &clipPathFrame
);
8222 if (clipPathFrame
) {
8226 aTo
+= nsPrintfCString(
8227 "clip(%s)", clipPathFrame
->IsTrivial() ? "trivial" : "non-trivial");
8229 } else if (mFrame
->StyleSVGReset()->HasClipPath()) {
8233 aTo
+= "clip(basic-shape)";
8237 nsTArray
<SVGMaskFrame
*> masks
;
8238 // XXX check return value?
8239 SVGObserverUtils::GetAndObserveMasks(firstFrame
, &masks
);
8240 if (!masks
.IsEmpty() && masks
[0]) {
8250 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8251 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
8252 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8253 nsDisplayListBuilder
* aDisplayListBuilder
) {
8254 WrFiltersHolder wrFilters
;
8255 const ComputedStyle
& style
= mStyle
? *mStyle
: *mFrame
->Style();
8256 auto filterChain
= style
.StyleEffects()->mBackdropFilters
.AsSpan();
8257 bool initialized
= true;
8258 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain
, mFrame
,
8260 !SVGIntegrationUtils::BuildWebRenderFilters(
8261 mFrame
, filterChain
, StyleFilterType::BackdropFilter
, wrFilters
,
8263 // TODO: If painting backdrop-filters on the content side is implemented,
8264 // consider returning false to fall back to that.
8272 nsCSSRendering::ImageLayerClipState clip
;
8273 nsCSSRendering::GetImageLayerClip(
8274 style
.StyleBackground()->BottomLayer(), mFrame
, *style
.StyleBorder(),
8275 mBackdropRect
, mBackdropRect
, false,
8276 mFrame
->PresContext()->AppUnitsPerDevPixel(), &clip
);
8278 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
8279 mBackdropRect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
8281 wr::ComplexClipRegion region
=
8282 wr::ToComplexClipRegion(clip
.mBGClipArea
, clip
.mRadii
,
8283 mFrame
->PresContext()->AppUnitsPerDevPixel());
8285 aBuilder
.PushBackdropFilter(wr::ToLayoutRect(bounds
), region
,
8286 wrFilters
.filters
, wrFilters
.filter_datas
,
8287 !BackfaceIsHidden());
8289 wr::StackingContextParams params
;
8291 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
8292 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
8295 nsDisplayWrapList::CreateWebRenderCommands(aBuilder
, aResources
, sc
, aManager
,
8296 aDisplayListBuilder
);
8300 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder
* aBuilder
,
8302 // TODO: Implement backdrop filters
8303 GetChildren()->Paint(aBuilder
, aCtx
,
8304 mFrame
->PresContext()->AppUnitsPerDevPixel());
8307 nsRect
nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder
* aBuilder
,
8308 bool* aSnap
) const {
8309 nsRect childBounds
= nsDisplayWrapList::GetBounds(aBuilder
, aSnap
);
8313 return mBackdropRect
.Union(childBounds
);
8317 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder
* aBuilder
,
8318 nsIFrame
* aFrame
, nsDisplayList
* aList
,
8319 nsIFrame
* aStyleFrame
,
8320 bool aWrapsBackdropFilter
)
8321 : nsDisplayEffectsBase(aBuilder
, aFrame
, aList
),
8322 mStyle(aFrame
== aStyleFrame
? nullptr : aStyleFrame
->Style()),
8323 mEffectsBounds(aFrame
->InkOverflowRectRelativeToSelf()),
8324 mWrapsBackdropFilter(aWrapsBackdropFilter
) {
8325 MOZ_COUNT_CTOR(nsDisplayFilters
);
8326 mVisibleRect
= aBuilder
->GetVisibleRect() +
8327 aBuilder
->GetCurrentFrameOffsetToReferenceFrame();
8330 void nsDisplayFilters::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
8331 PaintWithContentsPaintCallback(aBuilder
, aCtx
, [&](gfxContext
* aContext
) {
8332 GetChildren()->Paint(aBuilder
, aContext
,
8333 mFrame
->PresContext()->AppUnitsPerDevPixel());
8337 void nsDisplayFilters::PaintWithContentsPaintCallback(
8338 nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
8339 const std::function
<void(gfxContext
* aContext
)>& aPaintChildren
) {
8340 imgDrawingParams
imgParams(aBuilder
->GetImageDecodeFlags());
8341 nsRect borderArea
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
8342 PaintFramesParams
params(*aCtx
, mFrame
, mVisibleRect
, borderArea
, aBuilder
,
8345 gfxPoint userSpaceToFrameSpaceOffset
=
8346 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame
, params
);
8348 auto filterChain
= mStyle
? mStyle
->StyleEffects()->mFilters
.AsSpan()
8349 : mFrame
->StyleEffects()->mFilters
.AsSpan();
8350 SVGIntegrationUtils::PaintFilter(
8351 params
, filterChain
,
8352 [&](gfxContext
& aContext
, imgDrawingParams
&, const gfxMatrix
*,
8354 gfxContextMatrixAutoSaveRestore
autoSR(&aContext
);
8355 aContext
.SetMatrixDouble(aContext
.CurrentMatrixDouble().PreTranslate(
8356 -userSpaceToFrameSpaceOffset
));
8357 aPaintChildren(&aContext
);
8361 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8362 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame
);
8365 bool nsDisplayFilters::CreateWebRenderCommands(
8366 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
8367 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8368 nsDisplayListBuilder
* aDisplayListBuilder
) {
8369 WrFiltersHolder wrFilters
;
8370 const ComputedStyle
& style
= mStyle
? *mStyle
: *mFrame
->Style();
8371 auto filterChain
= style
.StyleEffects()->mFilters
.AsSpan();
8372 bool initialized
= true;
8373 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain
, mFrame
,
8375 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame
, filterChain
,
8376 StyleFilterType::Filter
,
8377 wrFilters
, initialized
)) {
8379 // TODO(bug 1769223): Support fallback filters in the root code-path,
8380 // perhaps. For now treat it the same way as invalid filters.
8383 // Draw using fallback.
8389 // https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
8391 // If the filter references a non-existent object or the referenced object
8392 // is not a filter element, then the whole filter chain is ignored. No
8393 // filter is applied to the object.
8395 // Note that other engines have a weird discrepancy between SVG and HTML
8396 // content here, but the spec is clear.
8400 uint64_t clipChainId
;
8401 if (wrFilters
.post_filters_clip
) {
8402 auto devPxRect
= LayoutDeviceRect::FromAppUnits(
8403 wrFilters
.post_filters_clip
.value() + ToReferenceFrame(),
8404 mFrame
->PresContext()->AppUnitsPerDevPixel());
8406 aBuilder
.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect
));
8407 clipChainId
= aBuilder
.DefineClipChain({clipId
}, true).id
;
8409 clipChainId
= aBuilder
.CurrentClipChainId();
8411 wr::WrStackingContextClip clip
=
8412 wr::WrStackingContextClip::ClipChain(clipChainId
);
8414 float opacity
= aBuilder
.GetInheritedOpacity();
8415 aBuilder
.SetInheritedOpacity(1.0f
);
8416 const DisplayItemClipChain
* oldClipChain
= aBuilder
.GetInheritedClipChain();
8417 aBuilder
.SetInheritedClipChain(nullptr);
8418 wr::StackingContextParams params
;
8419 params
.mFilters
= std::move(wrFilters
.filters
);
8420 params
.mFilterDatas
= std::move(wrFilters
.filter_datas
);
8421 params
.opacity
= opacity
!= 1.0f
? &opacity
: nullptr;
8423 if (mWrapsBackdropFilter
) {
8424 params
.flags
|= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER
;
8426 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
8429 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder
, aResources
, sc
,
8430 aManager
, aDisplayListBuilder
);
8431 aBuilder
.SetInheritedOpacity(opacity
);
8432 aBuilder
.SetInheritedClipChain(oldClipChain
);
8437 #ifdef MOZ_DUMP_PAINTING
8438 void nsDisplayFilters::PrintEffects(nsACString
& aTo
) {
8439 nsIFrame
* firstFrame
=
8440 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame
);
8442 aTo
+= " effects=(";
8443 // We may exist for a mix of CSS filter functions and/or references to SVG
8444 // filters. If we have invalid references to SVG filters then we paint
8445 // nothing, but otherwise we will apply one or more filters.
8446 if (SVGObserverUtils::GetAndObserveFilters(firstFrame
, nullptr) !=
8447 SVGObserverUtils::eHasRefsSomeInvalid
) {
8457 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder
* aBuilder
,
8458 nsIFrame
* aFrame
, nsDisplayList
* aList
)
8459 : nsDisplayWrapList(aBuilder
, aFrame
, aList
) {
8460 MOZ_COUNT_CTOR(nsDisplaySVGWrapper
);
8463 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) {
8464 return !aBuilder
->GetWidgetLayerManager();
8467 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8468 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
8469 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8470 nsDisplayListBuilder
* aDisplayListBuilder
) {
8471 return CreateWebRenderCommandsNewClipListOption(
8472 aBuilder
, aResources
, aSc
, aManager
, aDisplayListBuilder
, false);
8475 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder
* aBuilder
,
8477 nsDisplayList
* aList
)
8478 : nsDisplayWrapList(aBuilder
, aFrame
, aList
) {
8479 MOZ_COUNT_CTOR(nsDisplayForeignObject
);
8482 #ifdef NS_BUILD_REFCNT_LOGGING
8483 nsDisplayForeignObject::~nsDisplayForeignObject() {
8484 MOZ_COUNT_DTOR(nsDisplayForeignObject
);
8488 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) {
8489 return !aBuilder
->GetWidgetLayerManager();
8492 bool nsDisplayForeignObject::CreateWebRenderCommands(
8493 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
8494 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8495 nsDisplayListBuilder
* aDisplayListBuilder
) {
8496 AutoRestore
<bool> restoreDoGrouping(aManager
->CommandBuilder().mDoGrouping
);
8497 aManager
->CommandBuilder().mDoGrouping
= false;
8498 return CreateWebRenderCommandsNewClipListOption(
8499 aBuilder
, aResources
, aSc
, aManager
, aDisplayListBuilder
, false);
8502 void nsDisplayLink::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
8503 auto appPerDev
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8504 aCtx
->GetDrawTarget()->Link(
8505 mLinkSpec
.get(), NSRectToRect(GetPaintRect(aBuilder
, aCtx
), appPerDev
));
8508 void nsDisplayDestination::Paint(nsDisplayListBuilder
* aBuilder
,
8510 auto appPerDev
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8511 aCtx
->GetDrawTarget()->Destination(
8512 mDestinationName
.get(),
8513 NSPointToPoint(GetPaintRect(aBuilder
, aCtx
).TopLeft(), appPerDev
));
8516 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8517 nsDisplayList
* aOutResultList
, nsIContent
* aContent
) {
8518 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
8519 // in content document order and SortByZOrder is a stable sort which
8520 // guarantees that boxes produced by the same element are placed together
8521 // in the sort. Consider a position:relative inline element that breaks
8522 // across lines and has absolutely positioned children; all the abs-pos
8523 // children should be z-ordered after all the boxes for the position:relative
8525 PositionedDescendants()->SortByZOrder();
8527 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8528 // 1,2: backgrounds and borders
8529 aOutResultList
->AppendToTop(BorderBackground());
8530 // 3: negative z-index children.
8531 for (auto* item
: PositionedDescendants()->TakeItems()) {
8532 if (item
->ZIndex() < 0) {
8533 aOutResultList
->AppendToTop(item
);
8535 PositionedDescendants()->AppendToTop(item
);
8539 // 4: block backgrounds
8540 aOutResultList
->AppendToTop(BlockBorderBackgrounds());
8542 aOutResultList
->AppendToTop(Floats());
8543 // 7: general content
8544 aOutResultList
->AppendToTop(Content());
8545 // 7.5: outlines, in content tree order. We need to sort by content order
8546 // because an element with outline that breaks and has children with outline
8547 // might have placed child outline items between its own outline items.
8548 // The element's outline items need to all come before any child outline
8551 Outlines()->SortByContentOrder(aContent
);
8553 aOutResultList
->AppendToTop(Outlines());
8554 // 8, 9: non-negative z-index children
8555 aOutResultList
->AppendToTop(PositionedDescendants());
8558 uint32_t PaintTelemetry::sPaintLevel
= 0;
8560 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8561 // Don't record nested paints.
8562 if (sPaintLevel
++ > 0) {
8566 mStart
= TimeStamp::Now();
8569 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8570 MOZ_ASSERT(sPaintLevel
!= 0);
8571 if (--sPaintLevel
> 0) {
8575 // If we're in multi-process mode, don't include paint times for the parent
8577 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8581 double totalMs
= (TimeStamp::Now() - mStart
).ToMilliseconds();
8583 // Record the total time.
8584 Telemetry::Accumulate(Telemetry::CONTENT_PAINT_TIME
,
8585 static_cast<uint32_t>(totalMs
));
8588 static nsIFrame
* GetSelfOrPlaceholderFor(nsIFrame
* aFrame
) {
8589 if (aFrame
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
8593 if (aFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
) &&
8594 !aFrame
->GetPrevInFlow()) {
8595 return aFrame
->GetPlaceholderFrame();
8601 static nsIFrame
* GetAncestorFor(nsIFrame
* aFrame
) {
8602 nsIFrame
* f
= GetSelfOrPlaceholderFor(aFrame
);
8604 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f
);
8607 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8608 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aForChild
,
8609 const nsRect
& aVisibleRect
, const nsRect
& aDirtyRect
,
8610 const bool aIsTransformed
)
8611 : mBuilder(aBuilder
),
8612 mPrevFrame(aBuilder
->mCurrentFrame
),
8613 mPrevReferenceFrame(aBuilder
->mCurrentReferenceFrame
),
8614 mPrevOffset(aBuilder
->mCurrentOffsetToReferenceFrame
),
8615 mPrevAdditionalOffset(aBuilder
->mAdditionalOffset
),
8616 mPrevVisibleRect(aBuilder
->mVisibleRect
),
8617 mPrevDirtyRect(aBuilder
->mDirtyRect
),
8618 mPrevCompositorHitTestInfo(aBuilder
->mCompositorHitTestInfo
),
8619 mPrevAncestorHasApzAwareEventHandler(
8620 aBuilder
->mAncestorHasApzAwareEventHandler
),
8621 mPrevBuildingInvisibleItems(aBuilder
->mBuildingInvisibleItems
),
8622 mPrevInInvalidSubtree(aBuilder
->mInInvalidSubtree
) {
8623 if (aIsTransformed
) {
8624 aBuilder
->mCurrentOffsetToReferenceFrame
=
8625 aBuilder
->AdditionalOffset().refOr(nsPoint());
8626 aBuilder
->mCurrentReferenceFrame
= aForChild
;
8627 } else if (aBuilder
->mCurrentFrame
== aForChild
->GetParent()) {
8628 aBuilder
->mCurrentOffsetToReferenceFrame
+= aForChild
->GetPosition();
8630 aBuilder
->mCurrentReferenceFrame
= aBuilder
->FindReferenceFrameFor(
8631 aForChild
, &aBuilder
->mCurrentOffsetToReferenceFrame
);
8634 // If aForChild is being visited from a frame other than it's ancestor frame,
8635 // mInInvalidSubtree will need to be recalculated the slow way.
8636 if (aForChild
== mPrevFrame
|| GetAncestorFor(aForChild
) == mPrevFrame
) {
8637 aBuilder
->mInInvalidSubtree
=
8638 aBuilder
->mInInvalidSubtree
|| aForChild
->IsFrameModified();
8640 aBuilder
->mInInvalidSubtree
= AnyContentAncestorModified(aForChild
);
8643 aBuilder
->mCurrentFrame
= aForChild
;
8644 aBuilder
->mVisibleRect
= aVisibleRect
;
8645 aBuilder
->mDirtyRect
=
8646 aBuilder
->mInInvalidSubtree
? aVisibleRect
: aDirtyRect
;
8649 } // namespace mozilla