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/glean/GleanMetrics.h"
73 #include "mozilla/HashTable.h"
74 #include "mozilla/LookAndFeel.h"
75 #include "mozilla/OperatorNewExtensions.h"
76 #include "mozilla/Preferences.h"
77 #include "mozilla/ProfilerLabels.h"
78 #include "mozilla/ProfilerMarkers.h"
79 #include "mozilla/StyleAnimationValue.h"
80 #include "mozilla/ServoBindings.h"
81 #include "mozilla/SVGClipPathFrame.h"
82 #include "mozilla/SVGMaskFrame.h"
83 #include "mozilla/SVGObserverUtils.h"
84 #include "mozilla/Telemetry.h"
85 #include "mozilla/UniquePtr.h"
86 #include "mozilla/Unused.h"
87 #include "mozilla/ViewportFrame.h"
88 #include "mozilla/gfx/gfxVars.h"
89 #include "ActiveLayerTracker.h"
91 #include "nsPrintfCString.h"
92 #include "UnitTransforms.h"
93 #include "LayerAnimationInfo.h"
94 #include "mozilla/EventStateManager.h"
96 #include "nsDOMTokenList.h"
97 #include "nsCSSProps.h"
98 #include "nsTableCellFrame.h"
99 #include "nsTableColFrame.h"
100 #include "nsTextFrame.h"
101 #include "nsTextPaintStyle.h"
102 #include "nsSliderFrame.h"
103 #include "nsFocusManager.h"
104 #include "TextDrawTarget.h"
105 #include "mozilla/layers/AnimationHelper.h"
106 #include "mozilla/layers/CompositorThread.h"
107 #include "mozilla/layers/InputAPZContext.h"
108 #include "mozilla/layers/RenderRootStateManager.h"
109 #include "mozilla/layers/StackingContextHelper.h"
110 #include "mozilla/layers/TreeTraversal.h"
111 #include "mozilla/layers/WebRenderBridgeChild.h"
112 #include "mozilla/layers/WebRenderLayerManager.h"
113 #include "mozilla/layers/WebRenderMessages.h"
114 #include "mozilla/layers/WebRenderScrollData.h"
120 using namespace layout
;
121 using namespace layers
;
122 using namespace image
;
124 LazyLogModule
sContentDisplayListLog("dl.content");
125 LazyLogModule
sParentDisplayListLog("dl.parent");
127 LazyLogModule
& GetLoggerByProcess() {
128 return XRE_IsContentProcess() ? sContentDisplayListLog
129 : sParentDisplayListLog
;
132 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
133 void AssertUniqueItem(nsDisplayItem
* aItem
) {
134 for (nsDisplayItem
* i
: aItem
->Frame()->DisplayItems()) {
135 if (i
!= aItem
&& !i
->HasDeletedFrame() && i
->Frame() == aItem
->Frame() &&
136 i
->GetPerFrameKey() == aItem
->GetPerFrameKey()) {
137 if (i
->IsPreProcessedItem() || i
->IsPreProcessed()) {
140 MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
146 bool ShouldBuildItemForEvents(const DisplayItemType aType
) {
147 return aType
== DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
||
148 (GetDisplayItemFlagsForType(aType
) & TYPE_IS_CONTAINER
);
151 static bool ItemTypeSupportsHitTesting(const DisplayItemType aType
) {
153 case DisplayItemType::TYPE_BACKGROUND
:
154 case DisplayItemType::TYPE_BACKGROUND_COLOR
:
155 case DisplayItemType::TYPE_THEMED_BACKGROUND
:
162 void InitializeHitTestInfo(nsDisplayListBuilder
* aBuilder
,
163 nsPaintedDisplayItem
* aItem
,
164 const DisplayItemType aType
) {
165 if (ItemTypeSupportsHitTesting(aType
)) {
166 aItem
->InitializeHitTestInfo(aBuilder
);
171 already_AddRefed
<ActiveScrolledRoot
> ActiveScrolledRoot::CreateASRForFrame(
172 const ActiveScrolledRoot
* aParent
, nsIScrollableFrame
* aScrollableFrame
,
174 nsIFrame
* f
= do_QueryFrame(aScrollableFrame
);
176 RefPtr
<ActiveScrolledRoot
> asr
;
178 asr
= f
->GetProperty(ActiveScrolledRootCache());
182 asr
= new ActiveScrolledRoot();
185 RefPtr
<ActiveScrolledRoot
> ref
= asr
;
186 f
->SetProperty(ActiveScrolledRootCache(), ref
.forget().take());
189 asr
->mParent
= aParent
;
190 asr
->mScrollableFrame
= aScrollableFrame
;
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
);
276 // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
277 // are no active animations.
278 uint64_t animationsId
= animationInfo
.GetCompositorAnimationsId();
279 if (!animationInfo
.GetAnimations().IsEmpty()) {
280 OpAddCompositorAnimations
anim(
281 CompositorAnimations(animationInfo
.GetAnimations(), animationsId
));
282 aManager
->WrBridge()->AddWebRenderParentCommand(anim
);
283 aManager
->AddActiveCompositorAnimationId(animationsId
);
284 } else if (animationsId
) {
285 aManager
->AddCompositorAnimationsIdForDiscard(animationsId
);
292 static bool GenerateAndPushTextMask(nsIFrame
* aFrame
, gfxContext
* aContext
,
293 const nsRect
& aFillRect
,
294 nsDisplayListBuilder
* aBuilder
) {
295 if (aBuilder
->IsForGenerateGlyphMask()) {
299 SVGObserverUtils::GetAndObserveBackgroundClip(aFrame
);
301 // The main function of enabling background-clip:text property value.
302 // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
304 // 1. Generate a mask by all descendant text frames
305 // 2. Push the generated mask into aContext.
307 gfxContext
* sourceCtx
= aContext
;
308 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
309 aFillRect
, aFrame
->PresContext()->AppUnitsPerDevPixel());
311 // Create a mask surface.
312 RefPtr
<DrawTarget
> sourceTarget
= sourceCtx
->GetDrawTarget();
313 RefPtr
<DrawTarget
> maskDT
= sourceTarget
->CreateClippedDrawTarget(
314 bounds
.ToUnknownRect(), SurfaceFormat::A8
);
315 if (!maskDT
|| !maskDT
->IsValid()) {
318 gfxContext
maskCtx(maskDT
, /* aPreserveTransform */ true);
319 maskCtx
.Multiply(Matrix::Translation(bounds
.TopLeft().ToUnknownPoint()));
321 // Shade text shape into mask A8 surface.
322 nsLayoutUtils::PaintFrame(
323 &maskCtx
, aFrame
, nsRect(nsPoint(0, 0), aFrame
->GetSize()),
324 NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph
);
326 // Push the generated mask into aContext, so that the caller can pop and
329 Matrix currentMatrix
= sourceCtx
->CurrentMatrix();
330 Matrix invCurrentMatrix
= currentMatrix
;
331 invCurrentMatrix
.Invert();
333 RefPtr
<SourceSurface
> maskSurface
= maskDT
->Snapshot();
334 sourceCtx
->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA
, 1.0,
335 maskSurface
, invCurrentMatrix
);
340 nsDisplayWrapper
* nsDisplayWrapList::CreateShallowCopy(
341 nsDisplayListBuilder
* aBuilder
) {
342 const nsDisplayWrapList
* wrappedItem
= AsDisplayWrapList();
343 MOZ_ASSERT(wrappedItem
);
345 // Create a new nsDisplayWrapList using a copy-constructor. This is done
346 // to preserve the information about bounds.
347 nsDisplayWrapper
* wrapper
=
348 new (aBuilder
) nsDisplayWrapper(aBuilder
, *wrappedItem
);
349 wrapper
->SetType(nsDisplayWrapper::ItemType());
352 // Set the display list pointer of the new wrapper item to the display list
353 // of the wrapped item.
354 wrapper
->mListPtr
= wrappedItem
->mListPtr
;
358 nsDisplayWrapList
* nsDisplayListBuilder::MergeItems(
359 nsTArray
<nsDisplayItem
*>& aItems
) {
360 // For merging, we create a temporary item by cloning the last item of the
361 // mergeable items list. This ensures that the temporary item will have the
362 // correct frame and bounds.
363 nsDisplayWrapList
* last
= aItems
.PopLastElement()->AsDisplayWrapList();
365 nsDisplayWrapList
* merged
= last
->Clone(this);
367 AddTemporaryItem(merged
);
369 // Create nsDisplayWrappers that point to the internal display lists of the
370 // items we are merging. These nsDisplayWrappers are added to the display list
371 // of the temporary item.
372 for (nsDisplayItem
* item
: aItems
) {
374 MOZ_ASSERT(merged
->CanMerge(item
));
376 MOZ_ASSERT(item
->AsDisplayWrapList());
377 merged
->GetChildren()->AppendToTop(
378 static_cast<nsDisplayWrapList
*>(item
)->CreateShallowCopy(this));
381 merged
->GetChildren()->AppendToTop(last
->CreateShallowCopy(this));
386 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
387 SetCurrentActiveScrolledRoot(
388 const ActiveScrolledRoot
* aActiveScrolledRoot
) {
391 // Set the builder's mCurrentActiveScrolledRoot.
392 mBuilder
->mCurrentActiveScrolledRoot
= aActiveScrolledRoot
;
394 // We also need to adjust the builder's mCurrentContainerASR.
395 // mCurrentContainerASR needs to be an ASR that all the container's
396 // contents have finite bounds with respect to. If aActiveScrolledRoot
397 // is an ancestor ASR of mCurrentContainerASR, that means we need to
398 // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
399 // the items that will be created with aActiveScrolledRoot wouldn't
400 // have finite bounds with respect to mCurrentContainerASR. There's one
401 // exception, in the case where there's a content clip on the builder
402 // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
403 // content clip will clip all items that are created while this
404 // AutoCurrentActiveScrolledRootSetter exists. This means that the items
405 // created during our lifetime will have finite bounds with respect to
406 // the content clip's ASR, even if the items' actual ASR is an ancestor
407 // of that. And it also means that mCurrentContainerASR only needs to be
408 // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
409 // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
410 // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
412 // finiteBoundsASR is the leafmost ASR that all items created during
413 // object's lifetime have finite bounds with respect to.
414 const ActiveScrolledRoot
* finiteBoundsASR
=
415 ActiveScrolledRoot::PickDescendant(mContentClipASR
, aActiveScrolledRoot
);
417 // mCurrentContainerASR is adjusted so that it's still an ancestor of
419 mBuilder
->mCurrentContainerASR
= ActiveScrolledRoot::PickAncestor(
420 mBuilder
->mCurrentContainerASR
, finiteBoundsASR
);
422 // If we are entering out-of-flow content inside a CSS filter, mark
423 // scroll frames wrt. which the content is fixed as containing such content.
424 if (mBuilder
->mFilterASR
&& ActiveScrolledRoot::IsAncestor(
425 aActiveScrolledRoot
, mBuilder
->mFilterASR
)) {
426 for (const ActiveScrolledRoot
* asr
= mBuilder
->mFilterASR
;
427 asr
&& asr
!= aActiveScrolledRoot
; asr
= asr
->mParent
) {
428 asr
->mScrollableFrame
->SetHasOutOfFlowContentInsideFilter();
435 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
436 InsertScrollFrame(nsIScrollableFrame
* aScrollableFrame
) {
438 size_t descendantsEndIndex
= mBuilder
->mActiveScrolledRoots
.Length();
439 const ActiveScrolledRoot
* parentASR
= mBuilder
->mCurrentActiveScrolledRoot
;
440 const ActiveScrolledRoot
* asr
=
441 mBuilder
->AllocateActiveScrolledRoot(parentASR
, aScrollableFrame
);
442 mBuilder
->mCurrentActiveScrolledRoot
= asr
;
444 // All child ASRs of parentASR that were created while this
445 // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
446 // now. Reparent them to asr.
447 for (size_t i
= mDescendantsStartIndex
; i
< descendantsEndIndex
; i
++) {
448 ActiveScrolledRoot
* descendantASR
= mBuilder
->mActiveScrolledRoots
[i
];
449 if (ActiveScrolledRoot::IsAncestor(parentASR
, descendantASR
)) {
450 descendantASR
->IncrementDepth();
451 if (descendantASR
->mParent
== parentASR
) {
452 descendantASR
->mParent
= asr
;
460 nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
461 nsDisplayListBuilder
* aBuilder
)
462 : mBuilder(aBuilder
), mSavedContainerASR(aBuilder
->mCurrentContainerASR
) {
463 mBuilder
->mCurrentContainerASR
= mBuilder
->mCurrentActiveScrolledRoot
;
466 nsPresContext
* nsDisplayListBuilder::CurrentPresContext() {
467 return CurrentPresShellState()->mPresShell
->GetPresContext();
471 nsRect
nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
472 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
473 const nsRect
& aVisibleRect
, const nsRect
& aDirtyRect
,
474 nsRect
* aOutDirtyRect
) {
475 nsRect visible
= aVisibleRect
;
476 nsRect dirtyRectRelativeToDirtyFrame
= aDirtyRect
;
478 bool inPartialUpdate
=
479 aBuilder
->IsRetainingDisplayList() && aBuilder
->IsPartialUpdate();
480 if (StaticPrefs::apz_allow_zooming() &&
481 DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame
) &&
482 aBuilder
->IsPaintingToWindow() && !inPartialUpdate
) {
483 dirtyRectRelativeToDirtyFrame
=
484 nsRect(nsPoint(0, 0), aFrame
->GetParent()->GetSize());
486 // If there's a visual viewport size set, restrict the amount of the
487 // fixed-position element we paint to the visual viewport. (In general
488 // the fixed-position element can be as large as the layout viewport,
489 // which at a high zoom level can cause us to paint too large of an
491 PresShell
* presShell
= aFrame
->PresShell();
492 if (presShell
->IsVisualViewportSizeSet()) {
493 dirtyRectRelativeToDirtyFrame
=
494 nsRect(presShell
->GetVisualViewportOffsetRelativeToLayoutViewport(),
495 presShell
->GetVisualViewportSize());
496 // But if we have a displayport, expand it to the displayport, so
497 // that async-scrolling the visual viewport within the layout viewport
498 // will not checkerboard.
499 if (nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame()) {
501 // Note that the displayport here is already in the right coordinate
502 // space: it's relative to the scroll port (= layout viewport), but
503 // covers the visual viewport with some margins around it, which is
504 // exactly what we want.
505 if (DisplayPortUtils::GetDisplayPort(
506 rootScrollFrame
->GetContent(), &displayport
,
507 DisplayPortOptions().With(ContentGeometryType::Fixed
))) {
508 dirtyRectRelativeToDirtyFrame
= displayport
;
512 visible
= dirtyRectRelativeToDirtyFrame
;
513 if (StaticPrefs::apz_test_logging_enabled() &&
514 presShell
->GetDocument()->IsContentDocument()) {
515 nsLayoutUtils::LogAdditionalTestData(
516 aBuilder
, "fixedPosDisplayport",
517 ToString(CSSSize::FromAppUnits(visible
)));
521 *aOutDirtyRect
= dirtyRectRelativeToDirtyFrame
- aFrame
->GetPosition();
522 visible
-= aFrame
->GetPosition();
524 nsRect overflowRect
= aFrame
->InkOverflowRect();
526 if (aFrame
->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
527 aFrame
, DisplayItemType::TYPE_TRANSFORM
)) {
529 * Add a fuzz factor to the overflow rectangle so that elements only
530 * just out of view are pulled into the display list, so they can be
531 * prerendered if necessary.
533 overflowRect
.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
536 visible
.IntersectRect(visible
, overflowRect
);
537 aOutDirtyRect
->IntersectRect(*aOutDirtyRect
, overflowRect
);
542 nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder
* aBuilder
,
544 nsDisplayList
* aList
)
546 // Find the element that we need to check for link-ness, bailing out if
547 // we can't find one.
548 Element
* elem
= Element::FromNodeOrNull(aFrame
->GetContent());
553 // If the element has an id and/or name attribute, generate a destination
554 // for possible internal linking.
555 auto maybeGenerateDest
= [&](const nsAtom
* aAttr
) {
556 nsAutoString attrValue
;
557 elem
->GetAttr(aAttr
, attrValue
);
558 if (!attrValue
.IsEmpty()) {
559 NS_ConvertUTF16toUTF8
dest(attrValue
);
560 // Ensure that we only emit a given destination once, although there may
561 // be multiple frames associated with a given element; we'll simply use
562 // the first of them as the target of any links to it.
563 // XXX(jfkthame) This prevents emitting duplicate destinations *on the
564 // same page*, but does not prevent duplicates on subsequent pages, as
565 // each new page is handled by a new temporary DisplayListBuilder. This
566 // seems to be harmless in practice, though a bit wasteful of space. To
567 // fix, we need to maintain the set of already-seen destinations globally
568 // for the print job, rather than attached to the (per-page) builder.
569 if (aBuilder
->mDestinations
.EnsureInserted(dest
)) {
570 auto* destination
= MakeDisplayItem
<nsDisplayDestination
>(
571 aBuilder
, aFrame
, dest
.get(), aFrame
->GetRect().TopLeft());
572 mList
->AppendToTop(destination
);
577 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
579 maybeGenerateDest(nsGkAtoms::id
);
581 if (elem
->HasName()) {
582 maybeGenerateDest(nsGkAtoms::name
);
586 // Links don't nest, so if the builder already has a destination, no need to
587 // check for a link element here.
588 if (!aBuilder
->mLinkSpec
.IsEmpty()) {
592 // Check if we have actually found a link.
593 if (!elem
->IsLink()) {
597 nsCOMPtr
<nsIURI
> uri
= elem
->GetHrefURI();
602 // Is it a local (in-page) destination?
603 bool hasRef
, eqExRef
;
605 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
606 NS_SUCCEEDED(uri
->GetHasRef(&hasRef
)) && hasRef
&&
607 (docURI
= aFrame
->PresContext()->Document()->GetDocumentURI()) &&
608 NS_SUCCEEDED(uri
->EqualsExceptRef(docURI
, &eqExRef
)) && eqExRef
) {
609 if (NS_FAILED(uri
->GetRef(aBuilder
->mLinkSpec
)) ||
610 aBuilder
->mLinkSpec
.IsEmpty()) {
613 // The destination name is simply a string; we don't want URL-escaping
615 NS_UnescapeURL(aBuilder
->mLinkSpec
);
616 // Mark the link spec as being an internal destination
617 aBuilder
->mLinkSpec
.Insert('#', 0);
619 if (NS_FAILED(uri
->GetSpec(aBuilder
->mLinkSpec
)) ||
620 aBuilder
->mLinkSpec
.IsEmpty()) {
625 // Record that we need to reset the builder's state on destruction.
626 mBuilderToReset
= aBuilder
;
629 void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
630 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
) {
631 // Note that we may generate a link here even if the constructor bailed out
632 // without updating aBuilder->LinkSpec(), because it may have been set by
633 // an ancestor that was associated with a link element.
634 if (!aBuilder
->mLinkSpec
.IsEmpty()) {
635 auto* link
= MakeDisplayItem
<nsDisplayLink
>(
636 aBuilder
, aFrame
, aBuilder
->mLinkSpec
.get(), aFrame
->GetRect());
637 mList
->AppendToTop(link
);
641 uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1);
643 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame
* aReferenceFrame
,
644 nsDisplayListBuilderMode aMode
,
646 bool aRetainingDisplayList
)
647 : mReferenceFrame(aReferenceFrame
),
648 mIgnoreScrollFrame(nullptr),
649 mCurrentActiveScrolledRoot(nullptr),
650 mCurrentContainerASR(nullptr),
651 mCurrentFrame(aReferenceFrame
),
652 mCurrentReferenceFrame(aReferenceFrame
),
653 mScrollInfoItemsForHoisting(nullptr),
654 mFirstClipChainToDestroy(nullptr),
655 mTableBackgroundSet(nullptr),
656 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID
),
657 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID
),
659 mDirtyRect(-1, -1, -1, -1),
660 mBuildingExtraPagesForPageNum(0),
662 mContainsBlendMode(false),
663 mIsBuildingScrollbar(false),
664 mCurrentScrollbarWillHaveLayer(false),
665 mBuildCaret(aBuildCaret
),
666 mRetainingDisplayList(aRetainingDisplayList
),
667 mPartialUpdate(false),
668 mIgnoreSuppression(false),
669 mIncludeAllOutOfFlows(false),
670 mDescendIntoSubdocuments(true),
671 mSelectedFramesOnly(false),
672 mAllowMergingAndFlattening(true),
674 mInEventsOnly(false),
676 mInPageSequence(false),
677 mIsInChromePresContext(false),
678 mSyncDecodeImages(false),
679 mIsPaintingToWindow(false),
680 mUseHighQualityScaling(false),
681 mIsPaintingForWebRender(false),
682 mIsCompositingCheap(false),
683 mAncestorHasApzAwareEventHandler(false),
684 mHaveScrollableDisplayPort(false),
685 mWindowDraggingAllowed(false),
686 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame
)),
687 mForceLayerForScrollParent(false),
688 mContainsNonMinimalDisplayPort(false),
689 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame
)),
690 mBuildingInvisibleItems(false),
692 mInInvalidSubtree(false),
693 mDisablePartialUpdates(false),
694 mPartialBuildFailed(false),
695 mIsInActiveDocShell(false),
696 mBuildAsyncZoomContainer(false),
697 mIsRelativeToLayoutViewport(false),
698 mUseOverlayScrollbars(false),
699 mAlwaysLayerizeScrollbars(false) {
700 MOZ_COUNT_CTOR(nsDisplayListBuilder
);
702 mBuildCompositorHitTestInfo
= mAsyncPanZoomEnabled
&& IsForPainting();
704 ShouldRebuildDisplayListDueToPrefChange();
706 mUseOverlayScrollbars
=
707 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars
);
709 mAlwaysLayerizeScrollbars
=
710 StaticPrefs::layout_scrollbars_always_layerize_track();
713 static_cast<uint32_t>(DisplayItemType::TYPE_MAX
) < (1 << TYPE_BITS
),
714 "Check TYPE_MAX should not overflow");
716 mIsReusingStackingContextItems
=
717 mRetainingDisplayList
&& StaticPrefs::layout_display_list_retain_sc();
720 void nsDisplayListBuilder::BeginFrame() {
721 nsCSSRendering::BeginFrameTreesLocked();
723 mIsPaintingToWindow
= false;
724 mUseHighQualityScaling
= false;
725 mIgnoreSuppression
= false;
726 mInTransform
= false;
728 mSyncDecodeImages
= false;
731 void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser
* aBrowser
,
732 const dom::EffectsInfo
& aUpdate
) {
733 dom::EffectsInfo update
= aUpdate
;
734 // For printing we create one display item for each page that an iframe
735 // appears on, the proper visible rect is the union of all the visible rects
736 // we get from each display item.
738 mReferenceFrame
? mReferenceFrame
->PresContext() : nullptr;
739 if (pc
&& pc
->Type() != nsPresContext::eContext_Galley
) {
740 Maybe
<dom::EffectsInfo
> existing
= mEffectsUpdates
.MaybeGet(aBrowser
);
742 // Only the visible rect should differ, the scales should match.
743 MOZ_ASSERT(existing
->mRasterScale
== aUpdate
.mRasterScale
&&
744 existing
->mTransformToAncestorScale
==
745 aUpdate
.mTransformToAncestorScale
);
746 if (existing
->mVisibleRect
) {
747 if (update
.mVisibleRect
) {
748 update
.mVisibleRect
=
749 Some(update
.mVisibleRect
->Union(*existing
->mVisibleRect
));
751 update
.mVisibleRect
= existing
->mVisibleRect
;
756 mEffectsUpdates
.InsertOrUpdate(aBrowser
, update
);
759 void nsDisplayListBuilder::EndFrame() {
760 NS_ASSERTION(!mInInvalidSubtree
,
761 "Someone forgot to cleanup mInInvalidSubtree!");
762 mCurrentContainerASR
= nullptr;
763 mActiveScrolledRoots
.Clear();
764 mEffectsUpdates
.Clear();
766 FreeTemporaryItems();
767 nsCSSRendering::EndFrameTreesLocked();
770 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame
* aFrame
,
771 const nsIFrame
* aStopAtFrame
) {
772 mFramesMarkedForDisplay
.AppendElement(aFrame
);
773 for (nsIFrame
* f
= aFrame
; f
;
774 f
= nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f
)) {
775 if (f
->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
)) {
778 f
->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
);
779 if (f
== aStopAtFrame
) {
780 // we've reached a frame that we know will be painted, so we can stop.
786 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame
* aFrame
) {
787 mFramesMarkedForDisplayIfVisible
.AppendElement(aFrame
);
790 static void MarkFrameForDisplayIfVisibleInternal(nsIFrame
* aFrame
,
791 const nsIFrame
* aStopAtFrame
) {
792 for (nsIFrame
* f
= aFrame
; f
; f
= nsLayoutUtils::GetDisplayListParent(f
)) {
793 if (f
->ForceDescendIntoIfVisible()) {
796 f
->SetForceDescendIntoIfVisible(true);
798 // This condition must match the condition in
799 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
800 // nsLayoutUtils::GetDisplayListParent
801 if (f
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
) && !f
->GetPrevInFlow()) {
802 nsIFrame
* parent
= f
->GetParent();
803 if (parent
&& !parent
->ForceDescendIntoIfVisible()) {
804 // If the GetDisplayListParent call is going to walk to a placeholder,
805 // in rare cases the placeholder might be contained in a different
806 // continuation from the oof. So we have to make sure to mark the oofs
807 // parent. In the common case this doesn't make us do any extra work,
808 // just changes the order in which we visit the frames since walking
809 // through placeholders will walk through the parent, and we stop when
810 // we find a ForceDescendIntoIfVisible bit set.
811 MarkFrameForDisplayIfVisibleInternal(parent
, aStopAtFrame
);
815 if (f
== aStopAtFrame
) {
816 // we've reached a frame that we know will be painted, so we can stop.
822 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
823 nsIFrame
* aFrame
, const nsIFrame
* aStopAtFrame
) {
824 AddFrameMarkedForDisplayIfVisible(aFrame
);
826 MarkFrameForDisplayIfVisibleInternal(aFrame
, aStopAtFrame
);
829 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
830 mIsRelativeToLayoutViewport
= true;
831 UpdateShouldBuildAsyncZoomContainer();
834 void nsDisplayListBuilder::ForceLayerForScrollParent() {
835 mForceLayerForScrollParent
= true;
836 mNumActiveScrollframesEncountered
++;
839 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
840 const Document
* document
= mReferenceFrame
->PresContext()->Document();
841 mBuildAsyncZoomContainer
= !mIsRelativeToLayoutViewport
&&
842 !document
->Fullscreen() &&
843 nsLayoutUtils::AllowZoomingForDocument(document
);
846 // Certain prefs may cause display list items to be added or removed when they
847 // are toggled. In those cases, we need to fully rebuild the display list.
848 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
849 // If we transition between wrapping the RCD-RSF contents into an async
850 // zoom container vs. not, we need to rebuild the display list. This only
851 // happens when the zooming or container scrolling prefs are toggled
852 // (manually by the user, or during test setup).
853 bool didBuildAsyncZoomContainer
= mBuildAsyncZoomContainer
;
854 UpdateShouldBuildAsyncZoomContainer();
856 bool hadOverlayScrollbarsLastTime
= mUseOverlayScrollbars
;
857 mUseOverlayScrollbars
=
858 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars
);
860 bool alwaysLayerizedScrollbarsLastTime
= mAlwaysLayerizeScrollbars
;
861 mAlwaysLayerizeScrollbars
=
862 StaticPrefs::layout_scrollbars_always_layerize_track();
864 if (didBuildAsyncZoomContainer
!= mBuildAsyncZoomContainer
) {
868 if (hadOverlayScrollbarsLastTime
!= mUseOverlayScrollbars
) {
872 if (alwaysLayerizedScrollbarsLastTime
!= mAlwaysLayerizeScrollbars
) {
879 void nsDisplayListBuilder::AddScrollFrameToNotify(
880 nsIScrollableFrame
* aScrollFrame
) {
881 mScrollFramesToNotify
.insert(aScrollFrame
);
884 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
885 for (const auto& it
: mScrollFramesToNotify
) {
886 it
->NotifyApzTransaction();
888 mScrollFramesToNotify
.clear();
891 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
892 nsIFrame
* aDirtyFrame
, nsIFrame
* aFrame
, const nsRect
& aVisibleRect
,
893 const nsRect
& aDirtyRect
) {
894 MOZ_ASSERT(aFrame
->GetParent() == aDirtyFrame
);
896 nsRect visible
= OutOfFlowDisplayData::ComputeVisibleRectForFrame(
897 this, aFrame
, aVisibleRect
, aDirtyRect
, &dirty
);
898 if (!aFrame
->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) &&
903 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
904 // frame, then it will also mark any outer frames to ensure that building
905 // reaches the dirty feame.
906 if (!dirty
.IsEmpty() || aFrame
->ForceDescendIntoIfVisible()) {
907 MarkFrameForDisplay(aFrame
, aDirtyFrame
);
913 static void UnmarkFrameForDisplay(nsIFrame
* aFrame
,
914 const nsIFrame
* aStopAtFrame
) {
915 for (nsIFrame
* f
= aFrame
; f
;
916 f
= nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f
)) {
917 if (!f
->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
)) {
920 f
->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
);
921 if (f
== aStopAtFrame
) {
922 // we've reached a frame that we know will be painted, so we can stop.
928 static void UnmarkFrameForDisplayIfVisible(nsIFrame
* aFrame
) {
929 for (nsIFrame
* f
= aFrame
; f
; f
= nsLayoutUtils::GetDisplayListParent(f
)) {
930 if (!f
->ForceDescendIntoIfVisible()) {
933 f
->SetForceDescendIntoIfVisible(false);
935 // This condition must match the condition in
936 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
937 // nsLayoutUtils::GetDisplayListParent
938 if (f
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
) && !f
->GetPrevInFlow()) {
939 nsIFrame
* parent
= f
->GetParent();
940 if (parent
&& parent
->ForceDescendIntoIfVisible()) {
941 // If the GetDisplayListParent call is going to walk to a placeholder,
942 // in rare cases the placeholder might be contained in a different
943 // continuation from the oof. So we have to make sure to mark the oofs
944 // parent. In the common case this doesn't make us do any extra work,
945 // just changes the order in which we visit the frames since walking
946 // through placeholders will walk through the parent, and we stop when
947 // we find a ForceDescendIntoIfVisible bit set.
948 UnmarkFrameForDisplayIfVisible(f
);
954 nsDisplayListBuilder::~nsDisplayListBuilder() {
955 NS_ASSERTION(mFramesMarkedForDisplay
.Length() == 0,
956 "All frames should have been unmarked");
957 NS_ASSERTION(mFramesWithOOFData
.Length() == 0,
958 "All OOF data should have been removed");
959 NS_ASSERTION(mPresShellStates
.Length() == 0,
960 "All presshells should have been exited");
962 DisplayItemClipChain
* c
= mFirstClipChainToDestroy
;
964 DisplayItemClipChain
* next
= c
->mNextClipChainToDestroy
;
965 c
->DisplayItemClipChain::~DisplayItemClipChain();
969 MOZ_COUNT_DTOR(nsDisplayListBuilder
);
972 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
974 if (mSyncDecodeImages
) {
975 flags
|= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES
;
977 if (mIsPaintingToWindow
) {
978 flags
|= nsCSSRendering::PAINTBG_TO_WINDOW
;
980 if (mUseHighQualityScaling
) {
981 flags
|= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING
;
986 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
987 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
989 if (mSyncDecodeImages
) {
990 flags
|= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES
;
992 if (mIsPaintingToWindow
) {
993 flags
|= nsImageRenderer::FLAG_PAINTING_TO_WINDOW
;
995 if (mUseHighQualityScaling
) {
996 flags
|= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING
;
1001 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1002 uint32_t flags
= imgIContainer::FLAG_ASYNC_NOTIFY
;
1003 if (mSyncDecodeImages
) {
1004 flags
|= imgIContainer::FLAG_SYNC_DECODE
;
1006 flags
|= imgIContainer::FLAG_SYNC_DECODE_IF_FAST
;
1008 if (mIsPaintingToWindow
|| mUseHighQualityScaling
) {
1009 flags
|= imgIContainer::FLAG_HIGH_QUALITY_SCALING
;
1014 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion
* aVisibleRegion
,
1015 const nsRegion
& aRegion
) {
1016 if (aRegion
.IsEmpty()) {
1021 tmp
.Sub(*aVisibleRegion
, aRegion
);
1022 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1023 // to its bounds either, which can be very bad (see bug 516740).
1024 // Do let aVisibleRegion get more complex if by doing so we reduce its
1025 // area by at least half.
1026 if (tmp
.GetNumRects() <= 15 || tmp
.Area() <= aVisibleRegion
->Area() / 2) {
1027 *aVisibleRegion
= tmp
;
1031 nsCaret
* nsDisplayListBuilder::GetCaret() {
1032 RefPtr
<nsCaret
> caret
= CurrentPresShellState()->mPresShell
->GetCaret();
1036 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell
* aPresShell
) {
1037 if (mIsPaintingToWindow
) {
1038 aPresShell
->IncrementPaintCount();
1042 void nsDisplayListBuilder::EnterPresShell(const nsIFrame
* aReferenceFrame
,
1043 bool aPointerEventsNoneDoc
) {
1044 PresShellState
* state
= mPresShellStates
.AppendElement();
1045 state
->mPresShell
= aReferenceFrame
->PresShell();
1046 state
->mFirstFrameMarkedForDisplay
= mFramesMarkedForDisplay
.Length();
1047 state
->mFirstFrameWithOOFData
= mFramesWithOOFData
.Length();
1049 nsIScrollableFrame
* sf
= state
->mPresShell
->GetRootScrollFrameAsScrollable();
1050 if (sf
&& IsInSubdocument()) {
1051 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1052 // that the canvas background color will be set correctly, and that only one
1053 // unscrollable item will be created.
1054 // This is done to avoid, for example, a case where only scrollbar frames
1055 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1056 // and possibly end up with an extra nsDisplaySolidColor item.
1057 // We skip this for the root document, since we don't want to use
1058 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1059 // do it manually there.
1060 nsCanvasFrame
* canvasFrame
= do_QueryFrame(sf
->GetScrolledFrame());
1062 MarkFrameForDisplayIfVisible(canvasFrame
, aReferenceFrame
);
1067 state
->mAutoLayoutPhase
.emplace(aReferenceFrame
->PresContext(),
1068 nsLayoutPhase::DisplayListBuilding
);
1071 state
->mPresShell
->UpdateCanvasBackground();
1073 bool buildCaret
= mBuildCaret
;
1074 if (mIgnoreSuppression
|| !state
->mPresShell
->IsPaintingSuppressed()) {
1075 state
->mIsBackgroundOnly
= false;
1077 state
->mIsBackgroundOnly
= true;
1081 bool pointerEventsNone
= aPointerEventsNoneDoc
;
1082 if (IsInSubdocument()) {
1083 pointerEventsNone
|= mPresShellStates
[mPresShellStates
.Length() - 2]
1084 .mInsidePointerEventsNoneDoc
;
1086 state
->mInsidePointerEventsNoneDoc
= pointerEventsNone
;
1088 state
->mPresShellIgnoreScrollFrame
=
1089 state
->mPresShell
->IgnoringViewportScrolling()
1090 ? state
->mPresShell
->GetRootScrollFrame()
1093 nsPresContext
* pc
= aReferenceFrame
->PresContext();
1094 mIsInChromePresContext
= pc
->IsChrome();
1095 nsIDocShell
* docShell
= pc
->GetDocShell();
1098 docShell
->GetWindowDraggingAllowed(&mWindowDraggingAllowed
);
1101 state
->mTouchEventPrefEnabledDoc
= dom::TouchEvent::PrefEnabled(docShell
);
1107 RefPtr
<nsCaret
> caret
= state
->mPresShell
->GetCaret();
1108 // This code run for each pres shell and caret->GetPaintGeometry
1109 // will return nullptr for invisible caret. So only one caret
1110 // can be painted at a time.
1111 state
->mCaretFrame
= caret
->GetPaintGeometry(&mCaretRect
);
1113 // Check if the display root for the caret matches the display
1114 // root that we're painting, and only use it if it matches. Likely
1115 // we only need this for popup.
1116 if (state
->mCaretFrame
&&
1117 nsLayoutUtils::GetDisplayRootFrame(state
->mCaretFrame
) !=
1118 nsLayoutUtils::GetDisplayRootFrame(aReferenceFrame
)) {
1119 state
->mCaretFrame
= nullptr;
1122 // Caret frames add visual area to their frame, but we don't update the
1123 // overflow area. Use flags to make sure we build display items for that frame
1125 if (state
->mCaretFrame
) {
1126 MOZ_ASSERT(state
->mCaretFrame
->PresShell() == state
->mPresShell
);
1127 MarkFrameForDisplay(state
->mCaretFrame
, aReferenceFrame
);
1131 // A non-blank paint is a paint that does not just contain the canvas
1133 static bool DisplayListIsNonBlank(nsDisplayList
* aList
) {
1134 for (nsDisplayItem
* i
: *aList
) {
1135 switch (i
->GetType()) {
1136 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
:
1137 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR
:
1138 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE
:
1140 case DisplayItemType::TYPE_SOLID_COLOR
:
1141 case DisplayItemType::TYPE_BACKGROUND
:
1142 case DisplayItemType::TYPE_BACKGROUND_COLOR
:
1143 if (i
->Frame()->IsCanvasFrame()) {
1154 // A contentful paint is a paint that does contains DOM content (text,
1155 // images, non-blank canvases, SVG): "First Contentful Paint entry
1156 // contains a DOMHighResTimeStamp reporting the time when the browser
1157 // first rendered any text, image (including background images),
1158 // non-white canvas or SVG. This excludes any content of iframes, but
1159 // includes text with pending webfonts. This is the first time users
1160 // could start consuming page content."
1161 static bool DisplayListIsContentful(nsDisplayListBuilder
* aBuilder
,
1162 nsDisplayList
* aList
) {
1163 for (nsDisplayItem
* i
: *aList
) {
1164 DisplayItemType type
= i
->GetType();
1165 nsDisplayList
* children
= i
->GetChildren();
1168 case DisplayItemType::TYPE_SUBDOCUMENT
: // iframes are ignored
1170 // CANVASes check if they may have been modified (as a stand-in
1171 // actually tracking all modifications)
1173 if (i
->IsContentful()) {
1175 nsRect bound
= i
->GetBounds(aBuilder
, &dummy
);
1176 if (!bound
.IsEmpty()) {
1181 if (DisplayListIsContentful(aBuilder
, children
)) {
1191 void nsDisplayListBuilder::LeavePresShell(const nsIFrame
* aReferenceFrame
,
1192 nsDisplayList
* aPaintedContents
) {
1194 CurrentPresShellState()->mPresShell
== aReferenceFrame
->PresShell(),
1195 "Presshell mismatch");
1197 if (mIsPaintingToWindow
&& aPaintedContents
) {
1198 nsPresContext
* pc
= aReferenceFrame
->PresContext();
1199 if (!pc
->HadNonBlankPaint()) {
1200 if (!CurrentPresShellState()->mIsBackgroundOnly
&&
1201 DisplayListIsNonBlank(aPaintedContents
)) {
1202 pc
->NotifyNonBlankPaint();
1205 nsRootPresContext
* rootPresContext
= pc
->GetRootPresContext();
1206 if (!pc
->HasStoppedGeneratingLCP() && rootPresContext
) {
1207 if (!CurrentPresShellState()->mIsBackgroundOnly
) {
1208 if (pc
->HasEverBuiltInvisibleText() ||
1209 DisplayListIsContentful(this, aPaintedContents
)) {
1210 pc
->NotifyContentfulPaint();
1216 ResetMarkedFramesForDisplayList(aReferenceFrame
);
1217 mPresShellStates
.RemoveLastElement();
1219 if (!mPresShellStates
.IsEmpty()) {
1220 nsPresContext
* pc
= CurrentPresContext();
1221 nsIDocShell
* docShell
= pc
->GetDocShell();
1223 docShell
->GetWindowDraggingAllowed(&mWindowDraggingAllowed
);
1225 mIsInChromePresContext
= pc
->IsChrome();
1227 for (uint32_t i
= 0; i
< mFramesMarkedForDisplayIfVisible
.Length(); ++i
) {
1228 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible
[i
]);
1230 mFramesMarkedForDisplayIfVisible
.SetLength(0);
1234 void nsDisplayListBuilder::FreeClipChains() {
1235 // Iterate the clip chains from newest to oldest (forward
1236 // iteration), so that we destroy descendants first which
1237 // will drop the ref count on their ancestors.
1238 DisplayItemClipChain
** indirect
= &mFirstClipChainToDestroy
;
1241 if (!(*indirect
)->mRefCount
) {
1242 DisplayItemClipChain
* next
= (*indirect
)->mNextClipChainToDestroy
;
1244 mClipDeduplicator
.erase(*indirect
);
1245 (*indirect
)->DisplayItemClipChain::~DisplayItemClipChain();
1246 Destroy(DisplayListArenaObjectId::CLIPCHAIN
, *indirect
);
1250 indirect
= &(*indirect
)->mNextClipChainToDestroy
;
1255 void nsDisplayListBuilder::FreeTemporaryItems() {
1256 for (nsDisplayItem
* i
: mTemporaryItems
) {
1257 // Temporary display items are not added to the frames.
1258 MOZ_ASSERT(i
->Frame());
1259 i
->RemoveFrame(i
->Frame());
1263 mTemporaryItems
.Clear();
1266 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1267 const nsIFrame
* aReferenceFrame
) {
1268 // Unmark and pop off the frames marked for display in this pres shell.
1269 uint32_t firstFrameForShell
=
1270 CurrentPresShellState()->mFirstFrameMarkedForDisplay
;
1271 for (uint32_t i
= firstFrameForShell
; i
< mFramesMarkedForDisplay
.Length();
1273 UnmarkFrameForDisplay(mFramesMarkedForDisplay
[i
], aReferenceFrame
);
1275 mFramesMarkedForDisplay
.SetLength(firstFrameForShell
);
1277 firstFrameForShell
= CurrentPresShellState()->mFirstFrameWithOOFData
;
1278 for (uint32_t i
= firstFrameForShell
; i
< mFramesWithOOFData
.Length(); ++i
) {
1279 mFramesWithOOFData
[i
]->RemoveProperty(OutOfFlowDisplayDataProperty());
1281 mFramesWithOOFData
.SetLength(firstFrameForShell
);
1284 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1285 CurrentPresShellState()->mFixedBackgroundDisplayData
= Nothing();
1288 void nsDisplayListBuilder::MarkFramesForDisplayList(
1289 nsIFrame
* aDirtyFrame
, const nsFrameList
& aFrames
) {
1290 nsRect visibleRect
= GetVisibleRect();
1291 nsRect dirtyRect
= GetDirtyRect();
1293 // If we are entering content that is fixed to the RCD-RSF, we are
1294 // crossing the async zoom container boundary, and need to convert from
1295 // visual to layout coordinates.
1296 if (ViewportFrame
* viewportFrame
= do_QueryFrame(aDirtyFrame
)) {
1297 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1298 viewportFrame
->PresContext()->IsRootContentDocumentCrossProcess()) {
1299 if (viewportFrame
->PresShell()->GetRootScrollFrame()) {
1301 for (nsIFrame
* f
: aFrames
) {
1302 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f
));
1305 visibleRect
= ViewportUtils::VisualToLayout(visibleRect
,
1306 viewportFrame
->PresShell());
1307 dirtyRect
= ViewportUtils::VisualToLayout(dirtyRect
,
1308 viewportFrame
->PresShell());
1312 // This is an edge case that should only happen if we are in a
1313 // document with a XUL root element so that it does not have a root
1314 // scroll frame but it has fixed pos content and all of the frames in
1315 // aFrames are that fixed pos content.
1316 for (nsIFrame
* f
: aFrames
) {
1317 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f
) &&
1318 f
->GetParent() == aDirtyFrame
&&
1319 f
->StyleDisplay()->mPosition
==
1320 StylePositionProperty::Fixed
);
1322 // There's no root scroll frame so there can't be any zooming or async
1323 // panning so we don't need to adjust the visible and dirty rects.
1329 bool markedFrames
= false;
1330 for (nsIFrame
* e
: aFrames
) {
1331 // Skip the AccessibleCaret frame when building no caret.
1332 if (!IsBuildingCaret()) {
1333 nsIContent
* content
= e
->GetContent();
1334 if (content
&& content
->IsInNativeAnonymousSubtree() &&
1335 content
->IsElement()) {
1336 const nsAttrValue
* classes
= content
->AsElement()->GetClasses();
1338 classes
->Contains(nsGkAtoms::mozAccessiblecaret
, eCaseMatters
)) {
1343 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame
, e
, visibleRect
, dirtyRect
)) {
1344 markedFrames
= true;
1349 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1350 // to objects on the stack, so we need to clone the chain.
1351 const DisplayItemClipChain
* clipChain
=
1352 CopyWholeChain(mClipState
.GetClipChainForContainingBlockDescendants());
1353 const DisplayItemClipChain
* combinedClipChain
=
1354 mClipState
.GetCurrentCombinedClipChain(this);
1355 const ActiveScrolledRoot
* asr
= mCurrentActiveScrolledRoot
;
1357 OutOfFlowDisplayData
* data
= new OutOfFlowDisplayData(
1358 clipChain
, combinedClipChain
, asr
, this->mCurrentScrollParentId
,
1359 visibleRect
, dirtyRect
);
1360 aDirtyFrame
->SetProperty(
1361 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data
);
1362 mFramesWithOOFData
.AppendElement(aDirtyFrame
);
1365 if (!aDirtyFrame
->GetParent()) {
1366 // This is the viewport frame of aDirtyFrame's presshell.
1367 // Store the current display data so that it can be used for fixed
1368 // background images.
1370 CurrentPresShellState()->mPresShell
== aDirtyFrame
->PresShell(),
1371 "Presshell mismatch");
1372 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData
,
1373 "already traversed this presshell's root frame?");
1375 const DisplayItemClipChain
* clipChain
=
1376 CopyWholeChain(mClipState
.GetClipChainForContainingBlockDescendants());
1377 const DisplayItemClipChain
* combinedClipChain
=
1378 mClipState
.GetCurrentCombinedClipChain(this);
1379 const ActiveScrolledRoot
* asr
= mCurrentActiveScrolledRoot
;
1380 CurrentPresShellState()->mFixedBackgroundDisplayData
.emplace(
1381 clipChain
, combinedClipChain
, asr
, this->mCurrentScrollParentId
,
1382 GetVisibleRect(), GetDirtyRect());
1387 * Mark all preserve-3d children with
1388 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1389 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1390 * dirty rect for preserve-3d children.
1392 * @param aDirtyFrame is the frame to mark children extending context.
1394 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1395 nsIFrame
* aDirtyFrame
) {
1396 for (const auto& childList
: aDirtyFrame
->ChildLists()) {
1397 for (nsIFrame
* child
: childList
.mList
) {
1398 if (child
->Combines3DTransformWithAncestors()) {
1399 MarkFrameForDisplay(child
, aDirtyFrame
);
1402 if (child
->IsBlockWrapper()) {
1403 // Mark preserve-3d frames inside the block wrapper.
1404 MarkPreserve3DFramesForDisplayList(child
);
1410 ActiveScrolledRoot
* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1411 const ActiveScrolledRoot
* aParent
, nsIScrollableFrame
* aScrollableFrame
) {
1412 RefPtr
<ActiveScrolledRoot
> asr
= ActiveScrolledRoot::CreateASRForFrame(
1413 aParent
, aScrollableFrame
, IsRetainingDisplayList());
1414 mActiveScrolledRoots
.AppendElement(asr
);
1418 const DisplayItemClipChain
* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1419 const DisplayItemClip
& aClip
, const ActiveScrolledRoot
* aASR
,
1420 const DisplayItemClipChain
* aParent
) {
1421 MOZ_DIAGNOSTIC_ASSERT(!(aParent
&& aParent
->mOnStack
));
1422 void* p
= Allocate(sizeof(DisplayItemClipChain
),
1423 DisplayListArenaObjectId::CLIPCHAIN
);
1424 DisplayItemClipChain
* c
= new (KnownNotNull
, p
)
1425 DisplayItemClipChain(aClip
, aASR
, aParent
, mFirstClipChainToDestroy
);
1426 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
1427 c
->mOnStack
= false;
1429 auto result
= mClipDeduplicator
.insert(c
);
1430 if (!result
.second
) {
1431 // An equivalent clip chain item was already created, so let's return that
1432 // instead. Destroy the one we just created.
1433 // Note that this can cause clip chains from different coordinate systems to
1434 // collapse into the same clip chain object, because clip chains do not keep
1435 // track of the reference frame that they were created in.
1436 c
->DisplayItemClipChain::~DisplayItemClipChain();
1437 Destroy(DisplayListArenaObjectId::CLIPCHAIN
, c
);
1438 return *(result
.first
);
1440 mFirstClipChainToDestroy
= c
;
1444 struct ClipChainItem
{
1445 DisplayItemClip clip
;
1446 const ActiveScrolledRoot
* asr
;
1449 static const DisplayItemClipChain
* FindCommonAncestorClipForIntersection(
1450 const DisplayItemClipChain
* aOne
, const DisplayItemClipChain
* aTwo
) {
1451 for (const ActiveScrolledRoot
* asr
=
1452 ActiveScrolledRoot::PickDescendant(aOne
->mASR
, aTwo
->mASR
);
1453 asr
; asr
= asr
->mParent
) {
1457 if (aOne
->mASR
== asr
) {
1458 aOne
= aOne
->mParent
;
1460 if (aTwo
->mASR
== asr
) {
1461 aTwo
= aTwo
->mParent
;
1473 const DisplayItemClipChain
* nsDisplayListBuilder::CreateClipChainIntersection(
1474 const DisplayItemClipChain
* aAncestor
,
1475 const DisplayItemClipChain
* aLeafClip1
,
1476 const DisplayItemClipChain
* aLeafClip2
) {
1477 AutoTArray
<ClipChainItem
, 8> intersectedClips
;
1479 const DisplayItemClipChain
* clip1
= aLeafClip1
;
1480 const DisplayItemClipChain
* clip2
= aLeafClip2
;
1482 const ActiveScrolledRoot
* asr
= ActiveScrolledRoot::PickDescendant(
1483 clip1
? clip1
->mASR
: nullptr, clip2
? clip2
->mASR
: nullptr);
1485 // Build up the intersection from the leaf to the root and put it into
1486 // intersectedClips. The loop below will convert intersectedClips into an
1487 // actual DisplayItemClipChain.
1488 // (We need to do this in two passes because we need the parent clip in order
1489 // to create the DisplayItemClipChain object, but the parent clip has not
1490 // been created at that point.)
1491 while (!aAncestor
|| asr
!= aAncestor
->mASR
) {
1492 if (clip1
&& clip1
->mASR
== asr
) {
1493 if (clip2
&& clip2
->mASR
== asr
) {
1494 DisplayItemClip intersection
= clip1
->mClip
;
1495 intersection
.IntersectWith(clip2
->mClip
);
1496 intersectedClips
.AppendElement(ClipChainItem
{intersection
, asr
});
1497 clip2
= clip2
->mParent
;
1499 intersectedClips
.AppendElement(ClipChainItem
{clip1
->mClip
, asr
});
1501 clip1
= clip1
->mParent
;
1502 } else if (clip2
&& clip2
->mASR
== asr
) {
1503 intersectedClips
.AppendElement(ClipChainItem
{clip2
->mClip
, asr
});
1504 clip2
= clip2
->mParent
;
1507 MOZ_ASSERT(!aAncestor
, "We should have exited this loop earlier");
1513 // Convert intersectedClips into a DisplayItemClipChain.
1514 const DisplayItemClipChain
* parentSC
= aAncestor
;
1515 for (auto& sc
: Reversed(intersectedClips
)) {
1516 parentSC
= AllocateDisplayItemClipChain(sc
.clip
, sc
.asr
, parentSC
);
1521 const DisplayItemClipChain
* nsDisplayListBuilder::CreateClipChainIntersection(
1522 const DisplayItemClipChain
* aLeafClip1
,
1523 const DisplayItemClipChain
* aLeafClip2
) {
1524 // aLeafClip2 might be a reference to a clip on the stack. We need to make
1525 // sure that CreateClipChainIntersection will allocate the actual intersected
1526 // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1527 // we supply nullptr as the common ancestor so that
1528 // CreateClipChainIntersection clones the whole chain.
1529 const DisplayItemClipChain
* ancestorClip
=
1530 aLeafClip1
? FindCommonAncestorClipForIntersection(aLeafClip1
, aLeafClip2
)
1533 return CreateClipChainIntersection(ancestorClip
, aLeafClip1
, aLeafClip2
);
1536 const DisplayItemClipChain
* nsDisplayListBuilder::CopyWholeChain(
1537 const DisplayItemClipChain
* aClipChain
) {
1538 return CreateClipChainIntersection(nullptr, aClipChain
, nullptr);
1541 const nsIFrame
* nsDisplayListBuilder::FindReferenceFrameFor(
1542 const nsIFrame
* aFrame
, nsPoint
* aOffset
) const {
1543 auto MaybeApplyAdditionalOffset
= [&]() {
1544 if (auto offset
= AdditionalOffset()) {
1545 *aOffset
+= *offset
;
1549 if (aFrame
== mCurrentFrame
) {
1551 *aOffset
= mCurrentOffsetToReferenceFrame
;
1553 return mCurrentReferenceFrame
;
1556 for (const nsIFrame
* f
= aFrame
; f
;
1557 f
= nsLayoutUtils::GetCrossDocParentFrameInProcess(f
)) {
1558 if (f
== mReferenceFrame
|| f
->IsTransformed()) {
1560 *aOffset
= aFrame
->GetOffsetToCrossDoc(f
);
1561 MaybeApplyAdditionalOffset();
1568 *aOffset
= aFrame
->GetOffsetToCrossDoc(mReferenceFrame
);
1569 MaybeApplyAdditionalOffset();
1572 return mReferenceFrame
;
1575 // Sticky frames are active if their nearest scrollable frame is also active.
1576 static bool IsStickyFrameActive(nsDisplayListBuilder
* aBuilder
,
1578 MOZ_ASSERT(aFrame
->StyleDisplay()->mPosition
==
1579 StylePositionProperty::Sticky
);
1581 StickyScrollContainer
* stickyScrollContainer
=
1582 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame
);
1583 return stickyScrollContainer
&&
1584 stickyScrollContainer
->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1587 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame
* aFrame
,
1588 nsIFrame
** aParent
) {
1589 if (aFrame
== mReferenceFrame
) {
1593 if (!IsPaintingToWindow()) {
1595 *aParent
= nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame
);
1600 nsIFrame
* parent
= nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame
);
1606 if (aFrame
->StyleDisplay()->mPosition
== StylePositionProperty::Sticky
&&
1607 IsStickyFrameActive(this, aFrame
)) {
1611 if (aFrame
->IsTransformed()) {
1612 if (EffectCompositor::HasAnimationsForCompositor(
1613 aFrame
, DisplayItemType::TYPE_TRANSFORM
)) {
1618 LayoutFrameType parentType
= parent
->Type();
1619 if (parentType
== LayoutFrameType::Scroll
||
1620 parentType
== LayoutFrameType::ListControl
) {
1621 nsIScrollableFrame
* sf
= do_QueryFrame(parent
);
1622 if (sf
->GetScrolledFrame() == aFrame
) {
1623 MOZ_ASSERT(!aFrame
->IsTransformed());
1624 return sf
->IsMaybeAsynchronouslyScrolled();
1631 nsIFrame
* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1633 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1634 RootReferenceFrame(), aFrame
));
1635 nsIFrame
* cursor
= aFrame
;
1636 while (cursor
!= RootReferenceFrame()) {
1638 if (IsAnimatedGeometryRoot(cursor
, &next
)) {
1646 static nsRect
ApplyAllClipNonRoundedIntersection(
1647 const DisplayItemClipChain
* aClipChain
, const nsRect
& aRect
) {
1648 nsRect result
= aRect
;
1649 while (aClipChain
) {
1650 result
= aClipChain
->mClip
.ApplyNonRoundedIntersection(result
);
1651 aClipChain
= aClipChain
->mParent
;
1656 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame
* aFrame
) {
1657 if (!mWindowDraggingAllowed
|| !IsForPainting()) {
1661 const nsStyleUIReset
* styleUI
= aFrame
->StyleUIReset();
1662 if (styleUI
->mWindowDragging
== StyleWindowDragging::Default
) {
1663 // This frame has the default value and doesn't influence the window
1668 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame
;
1670 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1671 nsIFrame
* referenceFrame
=
1672 const_cast<nsIFrame
*>(FindReferenceFrameFor(aFrame
));
1674 if (IsInTransform()) {
1675 // Only support 2d rectilinear transforms. Transform support is needed for
1676 // the horizontal flip transform that's applied to the urlbar textbox in
1677 // RTL mode - it should be able to exclude itself from the draggable region.
1678 referenceFrameToRootReferenceFrame
=
1679 ViewAs
<LayoutDeviceToLayoutDeviceMatrix4x4
>(
1680 nsLayoutUtils::GetTransformToAncestor(RelativeTo
{referenceFrame
},
1681 RelativeTo
{mReferenceFrame
})
1683 Matrix referenceFrameToRootReferenceFrame2d
;
1684 if (!referenceFrameToRootReferenceFrame
.Is2D(
1685 &referenceFrameToRootReferenceFrame2d
) ||
1686 !referenceFrameToRootReferenceFrame2d
.IsRectilinear()) {
1690 MOZ_ASSERT(referenceFrame
== mReferenceFrame
,
1691 "referenceFrameToRootReferenceFrame needs to be adjusted");
1694 // We do some basic visibility checking on the frame's border box here.
1695 // We intersect it both with the current dirty rect and with the current
1696 // clip. Either one is just a conservative approximation on its own, but
1697 // their intersection luckily works well enough for our purposes, so that
1698 // we don't have to do full-blown visibility computations.
1699 // The most important case we need to handle is the scrolled-off tab:
1700 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1701 // should not be allowed to interfere with the window dragging region. Using
1702 // just the current DisplayItemClip is not enough to cover this case
1703 // completely because clips are reset while building stacking context
1704 // contents, so for example we'd fail to clip frames that have a clip path
1705 // applied to them. But the current dirty rect doesn't get reset in that
1706 // case, so we use it to make this case work.
1707 nsRect borderBox
= aFrame
->GetRectRelativeToSelf().Intersect(mVisibleRect
);
1708 borderBox
+= ToReferenceFrame(aFrame
);
1709 const DisplayItemClipChain
* clip
=
1710 ClipState().GetCurrentCombinedClipChain(this);
1711 borderBox
= ApplyAllClipNonRoundedIntersection(clip
, borderBox
);
1712 if (borderBox
.IsEmpty()) {
1716 LayoutDeviceRect devPixelBorderBox
= LayoutDevicePixel::FromAppUnits(
1717 borderBox
, aFrame
->PresContext()->AppUnitsPerDevPixel());
1719 LayoutDeviceRect transformedDevPixelBorderBox
=
1720 TransformBy(referenceFrameToRootReferenceFrame
, devPixelBorderBox
);
1721 transformedDevPixelBorderBox
.Round();
1722 LayoutDeviceIntRect transformedDevPixelBorderBoxInt
;
1724 if (!transformedDevPixelBorderBox
.ToIntRect(
1725 &transformedDevPixelBorderBoxInt
)) {
1729 LayoutDeviceIntRegion
& region
=
1730 styleUI
->mWindowDragging
== StyleWindowDragging::Drag
1731 ? mWindowDraggingRegion
1732 : mWindowNoDraggingRegion
;
1734 if (!IsRetainingDisplayList()) {
1735 region
.OrWith(transformedDevPixelBorderBoxInt
);
1739 gfx::IntRect
rect(transformedDevPixelBorderBoxInt
.ToUnknownRect());
1740 if (styleUI
->mWindowDragging
== StyleWindowDragging::Drag
) {
1741 mRetainedWindowDraggingRegion
.Add(aFrame
, rect
);
1743 mRetainedWindowNoDraggingRegion
.Add(aFrame
, rect
);
1747 LayoutDeviceIntRegion
nsDisplayListBuilder::GetWindowDraggingRegion() const {
1748 LayoutDeviceIntRegion result
;
1749 if (!IsRetainingDisplayList()) {
1750 result
.Sub(mWindowDraggingRegion
, mWindowNoDraggingRegion
);
1754 LayoutDeviceIntRegion dragRegion
=
1755 mRetainedWindowDraggingRegion
.ToLayoutDeviceIntRegion();
1757 LayoutDeviceIntRegion noDragRegion
=
1758 mRetainedWindowNoDraggingRegion
.ToLayoutDeviceIntRegion();
1760 result
.Sub(dragRegion
, noDragRegion
);
1764 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
) const {
1765 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes
);
1766 aSizes
.mLayoutRetainedDisplayListSize
+=
1767 aSizes
.mState
.mMallocSizeOf(mTransformPreserves3D
.get());
1770 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
) const {
1771 mPool
.AddSizeOfExcludingThis(aSizes
, Arena::ArenaKind::DisplayList
);
1774 MallocSizeOf mallocSizeOf
= aSizes
.mState
.mMallocSizeOf
;
1775 n
+= mDocumentWillChangeBudgets
.ShallowSizeOfExcludingThis(mallocSizeOf
);
1776 n
+= mFrameWillChangeBudgets
.ShallowSizeOfExcludingThis(mallocSizeOf
);
1777 n
+= mEffectsUpdates
.ShallowSizeOfExcludingThis(mallocSizeOf
);
1778 n
+= mRetainedWindowDraggingRegion
.SizeOfExcludingThis(mallocSizeOf
);
1779 n
+= mRetainedWindowNoDraggingRegion
.SizeOfExcludingThis(mallocSizeOf
);
1780 n
+= mRetainedWindowOpaqueRegion
.SizeOfExcludingThis(mallocSizeOf
);
1781 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1783 aSizes
.mLayoutRetainedDisplayListSize
+= n
;
1786 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
) const {
1787 for (nsDisplayItem
* item
: *this) {
1788 item
->AddSizeOfExcludingThis(aSizes
);
1789 if (RetainedDisplayList
* children
= item
->GetChildren()) {
1790 children
->AddSizeOfExcludingThis(aSizes
);
1796 n
+= mDAG
.mDirectPredecessorList
.ShallowSizeOfExcludingThis(
1797 aSizes
.mState
.mMallocSizeOf
);
1798 n
+= mDAG
.mNodesInfo
.ShallowSizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
1799 n
+= mOldItems
.ShallowSizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
1801 aSizes
.mLayoutRetainedDisplayListSize
+= n
;
1804 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1805 MallocSizeOf aMallocSizeOf
) const {
1807 n
+= mFrames
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
1808 for (const auto& frame
: mFrames
) {
1809 const UniquePtr
<WeakFrame
>& weakFrame
= frame
.mWeakFrame
;
1810 n
+= aMallocSizeOf(weakFrame
.get());
1812 n
+= mRects
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
1817 * Removes modified frames and rects from this WeakFrameRegion.
1819 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1820 MOZ_ASSERT(mFrames
.Length() == mRects
.Length());
1823 uint32_t length
= mFrames
.Length();
1825 while (i
< length
) {
1826 auto& wrapper
= mFrames
[i
];
1828 if (!wrapper
.mWeakFrame
->IsAlive() ||
1829 AnyContentAncestorModified(wrapper
.mWeakFrame
->GetFrame())) {
1830 // To avoid multiple O(n) shifts in the array, move the last element of
1831 // the array to the current position and decrease the array length.
1832 mFrameSet
.Remove(wrapper
.mFrame
);
1833 mFrames
[i
] = std::move(mFrames
[length
- 1]);
1834 mRects
[i
] = std::move(mRects
[length
- 1]);
1841 mFrames
.TruncateLength(length
);
1842 mRects
.TruncateLength(length
);
1845 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1846 mRetainedWindowDraggingRegion
.RemoveModifiedFramesAndRects();
1847 mRetainedWindowNoDraggingRegion
.RemoveModifiedFramesAndRects();
1848 mRetainedWindowOpaqueRegion
.RemoveModifiedFramesAndRects();
1851 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1852 mRetainedWindowDraggingRegion
.Clear();
1853 mRetainedWindowNoDraggingRegion
.Clear();
1854 mRetainedWindowOpaqueRegion
.Clear();
1857 const uint32_t gWillChangeAreaMultiplier
= 3;
1858 static uint32_t GetLayerizationCost(const nsSize
& aSize
) {
1859 // There's significant overhead for each layer created from Gecko
1860 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1861 // Therefore we set a minimum cost threshold of a 64x64 area.
1862 const int minBudgetCost
= 64 * 64;
1864 const uint32_t budgetCost
= std::max(
1865 minBudgetCost
, nsPresContext::AppUnitsToIntCSSPixels(aSize
.width
) *
1866 nsPresContext::AppUnitsToIntCSSPixels(aSize
.height
));
1871 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame
* aFrame
,
1872 const nsSize
& aSize
) {
1873 MOZ_ASSERT(IsForPainting());
1875 if (aFrame
->MayHaveWillChangeBudget()) {
1876 // The frame is already in the will-change budget.
1880 const nsPresContext
* presContext
= aFrame
->PresContext();
1881 const nsRect area
= presContext
->GetVisibleArea();
1882 const uint32_t budgetLimit
=
1883 nsPresContext::AppUnitsToIntCSSPixels(area
.width
) *
1884 nsPresContext::AppUnitsToIntCSSPixels(area
.height
);
1885 const uint32_t cost
= GetLayerizationCost(aSize
);
1887 DocumentWillChangeBudget
& documentBudget
=
1888 mDocumentWillChangeBudgets
.LookupOrInsert(presContext
);
1890 const bool onBudget
=
1891 (documentBudget
+ cost
) / gWillChangeAreaMultiplier
< budgetLimit
;
1894 documentBudget
+= cost
;
1895 mFrameWillChangeBudgets
.InsertOrUpdate(
1896 aFrame
, FrameWillChangeBudget(presContext
, cost
));
1897 aFrame
->SetMayHaveWillChangeBudget(true);
1903 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame
* aFrame
,
1904 const nsSize
& aSize
) {
1905 if (!IsForPainting()) {
1906 // If this nsDisplayListBuilder is not for painting, the layerization should
1907 // not matter. Do the simple thing and return false.
1911 const bool onBudget
= AddToWillChangeBudget(aFrame
, aSize
);
1916 auto* pc
= aFrame
->PresContext();
1917 auto* doc
= pc
->Document();
1918 if (!doc
->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget
)) {
1919 AutoTArray
<nsString
, 2> params
;
1920 params
.AppendElement()->AppendInt(gWillChangeAreaMultiplier
);
1922 nsRect area
= pc
->GetVisibleArea();
1923 uint32_t budgetLimit
= nsPresContext::AppUnitsToIntCSSPixels(area
.width
) *
1924 nsPresContext::AppUnitsToIntCSSPixels(area
.height
);
1925 params
.AppendElement()->AppendInt(budgetLimit
);
1927 doc
->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget
, false, params
);
1933 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame
* aFrame
) {
1934 MOZ_ASSERT(IsForPainting());
1936 if (!aFrame
->MayHaveWillChangeBudget()) {
1940 aFrame
->SetMayHaveWillChangeBudget(false);
1941 RemoveFromWillChangeBudgets(aFrame
);
1944 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame
* aFrame
) {
1945 if (auto entry
= mFrameWillChangeBudgets
.Lookup(aFrame
)) {
1946 const FrameWillChangeBudget
& frameBudget
= entry
.Data();
1948 auto documentBudget
=
1949 mDocumentWillChangeBudgets
.Lookup(frameBudget
.mPresContext
);
1951 if (documentBudget
) {
1952 *documentBudget
-= frameBudget
.mUsage
;
1959 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1960 mFrameWillChangeBudgets
.Clear();
1961 mDocumentWillChangeBudgets
.Clear();
1964 void nsDisplayListBuilder::EnterSVGEffectsContents(
1965 nsIFrame
* aEffectsFrame
, nsDisplayList
* aHoistedItemsStorage
) {
1966 MOZ_ASSERT(aHoistedItemsStorage
);
1967 if (mSVGEffectsFrames
.IsEmpty()) {
1968 MOZ_ASSERT(!mScrollInfoItemsForHoisting
);
1969 mScrollInfoItemsForHoisting
= aHoistedItemsStorage
;
1971 mSVGEffectsFrames
.AppendElement(aEffectsFrame
);
1974 void nsDisplayListBuilder::ExitSVGEffectsContents() {
1975 MOZ_ASSERT(!mSVGEffectsFrames
.IsEmpty());
1976 mSVGEffectsFrames
.RemoveLastElement();
1977 MOZ_ASSERT(mScrollInfoItemsForHoisting
);
1978 if (mSVGEffectsFrames
.IsEmpty()) {
1979 mScrollInfoItemsForHoisting
= nullptr;
1983 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
1985 * Note: if changing the conditions under which scroll info layers
1986 * are created, make a corresponding change to
1987 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
1989 for (nsIFrame
* frame
: mSVGEffectsFrames
) {
1990 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame
)) {
1997 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
1998 nsDisplayScrollInfoLayer
* aScrollInfoItem
) {
1999 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2000 MOZ_ASSERT(mScrollInfoItemsForHoisting
);
2001 mScrollInfoItemsForHoisting
->AppendToTop(aScrollInfoItem
);
2004 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2005 nsIFrame
* aFrame
, nsDisplayList
* aList
) {
2009 if (!BuildCompositorHitTestInfo()) {
2013 const CompositorHitTestInfo info
= aFrame
->GetCompositorHitTestInfo(this);
2014 if (info
!= CompositorHitTestInvisibleToHit
) {
2015 aList
->AppendNewToTop
<nsDisplayCompositorHitTestInfo
>(this, aFrame
);
2019 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem
* aItem
) {
2020 mReuseableItems
.Insert(aItem
);
2023 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem
* aItem
) {
2024 MOZ_ASSERT(aItem
->IsReusedItem());
2025 mReuseableItems
.Remove(aItem
);
2028 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2029 const size_t total
= mReuseableItems
.Count();
2032 for (auto* item
: mReuseableItems
) {
2033 if (item
->IsReusedItem()) {
2035 item
->SetReusable();
2037 item
->Destroy(this);
2041 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused
, total
);
2042 mReuseableItems
.Clear();
2045 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem
* aItem
) {
2046 const auto* previous
= mCurrentContainerASR
;
2047 const auto* asr
= aItem
->GetActiveScrolledRoot();
2048 mCurrentContainerASR
=
2049 ActiveScrolledRoot::PickAncestor(asr
, mCurrentContainerASR
);
2051 if (previous
!= mCurrentContainerASR
) {
2052 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous
,
2053 mCurrentContainerASR
);
2056 aItem
->SetReusedItem();
2059 void nsDisplayListSet::CopyTo(const nsDisplayListSet
& aDestination
) const {
2060 for (size_t i
= 0; i
< mLists
.size(); ++i
) {
2061 auto* from
= mLists
[i
];
2062 auto* to
= aDestination
.mLists
[i
];
2068 void nsDisplayListSet::MoveTo(const nsDisplayListSet
& aDestination
) const {
2069 aDestination
.BorderBackground()->AppendToTop(BorderBackground());
2070 aDestination
.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2071 aDestination
.Floats()->AppendToTop(Floats());
2072 aDestination
.Content()->AppendToTop(Content());
2073 aDestination
.PositionedDescendants()->AppendToTop(PositionedDescendants());
2074 aDestination
.Outlines()->AppendToTop(Outlines());
2077 nsRect
nsDisplayList::GetClippedBounds(nsDisplayListBuilder
* aBuilder
) const {
2079 for (nsDisplayItem
* i
: *this) {
2080 bounds
.UnionRect(bounds
, i
->GetClippedBounds(aBuilder
));
2085 nsRect
nsDisplayList::GetClippedBoundsWithRespectToASR(
2086 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
,
2087 nsRect
* aBuildingRect
) const {
2089 for (nsDisplayItem
* i
: *this) {
2090 nsRect r
= i
->GetClippedBounds(aBuilder
);
2091 if (aASR
!= i
->GetActiveScrolledRoot() && !r
.IsEmpty()) {
2092 if (Maybe
<nsRect
> clip
= i
->GetClipWithRespectToASR(aBuilder
, aASR
)) {
2096 if (aBuildingRect
) {
2097 aBuildingRect
->UnionRect(*aBuildingRect
, i
->GetBuildingRect());
2099 bounds
.UnionRect(bounds
, r
);
2104 nsRect
nsDisplayList::GetBuildingRect() const {
2106 for (nsDisplayItem
* i
: *this) {
2107 result
.UnionRect(result
, i
->GetBuildingRect());
2112 WindowRenderer
* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView
** aView
) {
2114 *aView
= RootReferenceFrame()->GetView();
2116 if (RootReferenceFrame() !=
2117 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2120 nsIWidget
* window
= RootReferenceFrame()->GetNearestWidget();
2122 return window
->GetWindowRenderer();
2127 WebRenderLayerManager
* nsDisplayListBuilder::GetWidgetLayerManager(
2129 WindowRenderer
* renderer
= GetWidgetWindowRenderer();
2131 return renderer
->AsWebRender();
2136 void nsDisplayList::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
2137 int32_t aAppUnitsPerDevPixel
) {
2138 FlattenedDisplayListIterator
iter(aBuilder
, this);
2139 while (iter
.HasNext()) {
2140 nsPaintedDisplayItem
* item
= iter
.GetNextItem()->AsPaintedDisplayItem();
2145 nsRect visible
= item
->GetClippedBounds(aBuilder
);
2146 visible
= visible
.Intersect(item
->GetPaintRect(aBuilder
, aCtx
));
2147 if (visible
.IsEmpty()) {
2151 DisplayItemClip currentClip
= item
->GetClip();
2152 if (currentClip
.HasClip()) {
2154 if (currentClip
.IsRectClippedByRoundedCorner(visible
)) {
2155 currentClip
.ApplyTo(aCtx
, aAppUnitsPerDevPixel
);
2157 currentClip
.ApplyRectTo(aCtx
, aAppUnitsPerDevPixel
);
2162 item
->Paint(aBuilder
, aCtx
);
2164 if (currentClip
.HasClip()) {
2171 * We paint by executing a layer manager transaction, constructing a
2172 * single layer representing the display list, and then making it the
2173 * root of the layer manager, drawing into the PaintedLayers.
2175 void nsDisplayList::PaintRoot(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
2177 Maybe
<double> aDisplayListBuildTime
) {
2178 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS
);
2180 RefPtr
<WebRenderLayerManager
> layerManager
;
2181 WindowRenderer
* renderer
= nullptr;
2182 bool widgetTransaction
= false;
2183 bool doBeginTransaction
= true;
2184 nsView
* view
= nullptr;
2185 if (aFlags
& PAINT_USE_WIDGET_LAYERS
) {
2186 renderer
= aBuilder
->GetWidgetWindowRenderer(&view
);
2188 // The fallback renderer doesn't retain any content, so it's
2189 // not meaningful to use it when drawing to an external context.
2190 if (aCtx
&& renderer
->AsFallback()) {
2191 MOZ_ASSERT(!(aFlags
& PAINT_EXISTING_TRANSACTION
));
2194 layerManager
= renderer
->AsWebRender();
2195 doBeginTransaction
= !(aFlags
& PAINT_EXISTING_TRANSACTION
);
2196 widgetTransaction
= true;
2201 nsIFrame
* frame
= aBuilder
->RootReferenceFrame();
2202 nsPresContext
* presContext
= frame
->PresContext();
2203 PresShell
* presShell
= presContext
->PresShell();
2204 Document
* document
= presShell
->GetDocument();
2208 MOZ_ASSERT(!layerManager
|| !layerManager
->GetTarget());
2211 // For layers-free mode, we check the invalidation state bits in the
2212 // EndTransaction. So we clear the invalidation state bits after
2214 if (widgetTransaction
||
2215 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2216 // but they still need the invalidation state bits cleared in order for
2217 // invalidation for CSS/SMIL animation to work properly.
2218 (document
&& document
->IsBeingUsedAsImage())) {
2219 DL_LOGD("Clearing invalidation state bits");
2220 frame
->ClearInvalidationStateBits();
2226 NS_WARNING("Nowhere to paint into");
2229 bool prevIsCompositingCheap
= aBuilder
->SetIsCompositingCheap(false);
2230 Paint(aBuilder
, aCtx
, presContext
->AppUnitsPerDevPixel());
2232 aBuilder
->SetIsCompositingCheap(prevIsCompositingCheap
);
2236 if (renderer
->GetBackendType() == LayersBackend::LAYERS_WR
) {
2237 MOZ_ASSERT(layerManager
);
2238 if (doBeginTransaction
) {
2240 if (!layerManager
->BeginTransactionWithTarget(aCtx
, nsCString())) {
2244 if (!layerManager
->BeginTransaction(nsCString())) {
2250 bool prevIsCompositingCheap
= aBuilder
->SetIsCompositingCheap(true);
2251 layerManager
->SetTransactionIdAllocator(presContext
->RefreshDriver());
2254 if (aFlags
& PAINT_IDENTICAL_DISPLAY_LIST
) {
2256 sent
= layerManager
->EndEmptyTransaction();
2260 auto* wrManager
= static_cast<WebRenderLayerManager
*>(layerManager
.get());
2262 nsIDocShell
* docShell
= presContext
->GetDocShell();
2263 WrFiltersHolder wrFilters
;
2264 gfx::Matrix5x4
* colorMatrix
=
2265 nsDocShell::Cast(docShell
)->GetColorMatrix();
2267 wrFilters
.filters
.AppendElement(
2268 wr::FilterOp::ColorMatrix(colorMatrix
->components
));
2271 wrManager
->EndTransactionWithoutLayer(this, aBuilder
,
2272 std::move(wrFilters
), nullptr,
2273 aDisplayListBuildTime
.valueOr(0.0));
2276 aBuilder
->SetIsCompositingCheap(prevIsCompositingCheap
);
2277 if (presContext
->RefreshDriver()->HasScheduleFlush()) {
2278 presContext
->NotifyInvalidation(layerManager
->GetLastTransactionId(),
2285 FallbackRenderer
* fallback
= renderer
->AsFallback();
2286 MOZ_ASSERT(fallback
);
2288 if (doBeginTransaction
) {
2290 if (!fallback
->BeginTransaction()) {
2295 bool temp
= aBuilder
->SetIsCompositingCheap(renderer
->IsCompositingCheap());
2296 fallback
->EndTransactionWithList(aBuilder
, this,
2297 presContext
->AppUnitsPerDevPixel(),
2298 WindowRenderer::END_DEFAULT
);
2300 aBuilder
->SetIsCompositingCheap(temp
);
2303 void nsDisplayList::DeleteAll(nsDisplayListBuilder
* aBuilder
) {
2304 for (auto* item
: TakeItems()) {
2305 item
->Destroy(aBuilder
);
2309 static bool IsFrameReceivingPointerEvents(nsIFrame
* aFrame
) {
2310 return aFrame
->Style()->PointerEvents() != StylePointerEvents::None
;
2313 // A list of frames, and their z depth. Used for sorting
2314 // the results of hit testing.
2315 struct FramesWithDepth
{
2316 explicit FramesWithDepth(float aDepth
) : mDepth(aDepth
) {}
2318 bool operator<(const FramesWithDepth
& aOther
) const {
2319 if (!FuzzyEqual(mDepth
, aOther
.mDepth
, 0.1f
)) {
2320 // We want to sort so that the shallowest item (highest depth value) is
2322 return mDepth
> aOther
.mDepth
;
2326 bool operator==(const FramesWithDepth
& aOther
) const {
2327 return this == &aOther
;
2331 nsTArray
<nsIFrame
*> mFrames
;
2334 // Sort the frames by depth and then moves all the contained frames to the
2336 static void FlushFramesArray(nsTArray
<FramesWithDepth
>& aSource
,
2337 nsTArray
<nsIFrame
*>* aDest
) {
2338 if (aSource
.IsEmpty()) {
2341 aSource
.StableSort();
2342 uint32_t length
= aSource
.Length();
2343 for (uint32_t i
= 0; i
< length
; i
++) {
2344 aDest
->AppendElements(std::move(aSource
[i
].mFrames
));
2349 void nsDisplayList::HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
2350 nsDisplayItem::HitTestState
* aState
,
2351 nsTArray
<nsIFrame
*>* aOutFrames
) const {
2352 nsDisplayItem
* item
;
2354 if (aState
->mInPreserves3D
) {
2355 // Collect leaves of the current 3D rendering context.
2356 for (nsDisplayItem
* item
: *this) {
2357 auto itemType
= item
->GetType();
2358 if (itemType
!= DisplayItemType::TYPE_TRANSFORM
||
2359 !static_cast<nsDisplayTransform
*>(item
)->IsLeafOf3DContext()) {
2360 item
->HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
2362 // One of leaves in the current 3D rendering context.
2363 aState
->mItemBuffer
.AppendElement(item
);
2369 int32_t itemBufferStart
= aState
->mItemBuffer
.Length();
2370 for (nsDisplayItem
* item
: *this) {
2371 aState
->mItemBuffer
.AppendElement(item
);
2374 AutoTArray
<FramesWithDepth
, 16> temp
;
2375 for (int32_t i
= aState
->mItemBuffer
.Length() - 1; i
>= itemBufferStart
;
2377 // Pop element off the end of the buffer. We want to shorten the buffer
2378 // so that recursive calls to HitTest have more buffer space.
2379 item
= aState
->mItemBuffer
[i
];
2380 aState
->mItemBuffer
.SetLength(i
);
2383 nsRect r
= item
->GetBounds(aBuilder
, &snap
).Intersect(aRect
);
2384 auto itemType
= item
->GetType();
2385 bool same3DContext
=
2386 (itemType
== DisplayItemType::TYPE_TRANSFORM
&&
2387 static_cast<nsDisplayTransform
*>(item
)->IsParticipating3DContext()) ||
2388 (itemType
== DisplayItemType::TYPE_PERSPECTIVE
&&
2389 item
->Frame()->Extend3DContext());
2390 if (same3DContext
&&
2391 (itemType
!= DisplayItemType::TYPE_TRANSFORM
||
2392 !static_cast<nsDisplayTransform
*>(item
)->IsLeafOf3DContext())) {
2393 if (!item
->GetClip().MayIntersect(aRect
)) {
2396 AutoTArray
<nsIFrame
*, 1> neverUsed
;
2397 // Start gathering leaves of the 3D rendering context, and
2398 // append leaves at the end of mItemBuffer. Leaves are
2399 // processed at following iterations.
2400 aState
->mInPreserves3D
= true;
2401 item
->HitTest(aBuilder
, aRect
, aState
, &neverUsed
);
2402 aState
->mInPreserves3D
= false;
2403 i
= aState
->mItemBuffer
.Length();
2406 if (same3DContext
|| item
->GetClip().MayIntersect(r
)) {
2407 AutoTArray
<nsIFrame
*, 16> outFrames
;
2408 item
->HitTest(aBuilder
, aRect
, aState
, &outFrames
);
2410 // For 3d transforms with preserve-3d we add hit frames into the temp list
2411 // so we can sort them later, otherwise we add them directly to the output
2413 nsTArray
<nsIFrame
*>* writeFrames
= aOutFrames
;
2414 if (item
->GetType() == DisplayItemType::TYPE_TRANSFORM
&&
2415 static_cast<nsDisplayTransform
*>(item
)->IsLeafOf3DContext()) {
2416 if (outFrames
.Length()) {
2417 nsDisplayTransform
* transform
=
2418 static_cast<nsDisplayTransform
*>(item
);
2419 nsPoint point
= aRect
.TopLeft();
2420 // A 1x1 rect means a point, otherwise use the center of the rect
2421 if (aRect
.width
!= 1 || aRect
.height
!= 1) {
2422 point
= aRect
.Center();
2425 FramesWithDepth(transform
->GetHitDepthAtPoint(aBuilder
, point
)));
2426 writeFrames
= &temp
[temp
.Length() - 1].mFrames
;
2429 // We may have just finished a run of consecutive preserve-3d
2430 // transforms, so flush these into the destination array before
2431 // processing our frame list.
2432 FlushFramesArray(temp
, aOutFrames
);
2435 for (uint32_t j
= 0; j
< outFrames
.Length(); j
++) {
2436 nsIFrame
* f
= outFrames
.ElementAt(j
);
2437 // Filter out some frames depending on the type of hittest
2438 // we are doing. For visibility tests, pass through all frames.
2439 // For pointer tests, only pass through frames that are styled
2440 // to receive pointer events.
2441 if (aBuilder
->HitTestIsForVisibility() ||
2442 IsFrameReceivingPointerEvents(f
)) {
2443 writeFrames
->AppendElement(f
);
2447 if (aBuilder
->HitTestIsForVisibility()) {
2448 aState
->mHitOccludingItem
= [&] {
2449 if (aState
->mHitOccludingItem
) {
2450 // We already hit something before.
2453 if (aState
->mCurrentOpacity
== 1.0f
&&
2454 item
->GetOpaqueRegion(aBuilder
, &snap
).Contains(aRect
)) {
2455 // An opaque item always occludes everything. Note that we need to
2456 // check wrapping opacity and such as well.
2459 float threshold
= aBuilder
->VisibilityThreshold();
2460 if (threshold
== 1.0f
) {
2463 float itemOpacity
= [&] {
2464 switch (item
->GetType()) {
2465 case DisplayItemType::TYPE_OPACITY
:
2466 return static_cast<nsDisplayOpacity
*>(item
)->GetOpacity();
2467 case DisplayItemType::TYPE_BACKGROUND_COLOR
:
2468 return static_cast<nsDisplayBackgroundColor
*>(item
)
2471 // Be conservative and assume it won't occlude other items.
2475 return itemOpacity
* aState
->mCurrentOpacity
>= threshold
;
2478 if (aState
->mHitOccludingItem
) {
2479 // We're exiting early, so pop the remaining items off the buffer.
2480 aState
->mItemBuffer
.TruncateLength(itemBufferStart
);
2486 // Clear any remaining preserve-3d transforms.
2487 FlushFramesArray(temp
, aOutFrames
);
2488 NS_ASSERTION(aState
->mItemBuffer
.Length() == uint32_t(itemBufferStart
),
2489 "How did we forget to pop some elements?");
2492 static nsIContent
* FindContentInDocument(nsDisplayItem
* aItem
, Document
* aDoc
) {
2493 nsIFrame
* f
= aItem
->Frame();
2495 nsPresContext
* pc
= f
->PresContext();
2496 if (pc
->Document() == aDoc
) {
2497 return f
->GetContent();
2499 f
= nsLayoutUtils::GetCrossDocParentFrameInProcess(
2500 pc
->PresShell()->GetRootFrame());
2506 nsDisplayItem
* item
;
2509 explicit ZSortItem(nsDisplayItem
* aItem
)
2510 : item(aItem
), zIndex(aItem
->ZIndex()) {}
2512 operator nsDisplayItem
*() { return item
; }
2515 struct ZOrderComparator
{
2516 bool operator()(const ZSortItem
& aLeft
, const ZSortItem
& aRight
) const {
2517 // Note that we can't just take the difference of the two
2518 // z-indices here, because that might overflow a 32-bit int.
2519 return aLeft
.zIndex
< aRight
.zIndex
;
2523 void nsDisplayList::SortByZOrder() { Sort
<ZSortItem
>(ZOrderComparator()); }
2525 struct ContentComparator
{
2526 nsIContent
* mCommonAncestor
;
2528 explicit ContentComparator(nsIContent
* aCommonAncestor
)
2529 : mCommonAncestor(aCommonAncestor
) {}
2531 bool operator()(nsDisplayItem
* aLeft
, nsDisplayItem
* aRight
) const {
2532 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2533 // subdocument of commonAncestor, because display items for subdocuments
2534 // have been mixed into the same list. Ensure that we're looking at content
2535 // in commonAncestor's document.
2536 Document
* commonAncestorDoc
= mCommonAncestor
->OwnerDoc();
2537 nsIContent
* content1
= FindContentInDocument(aLeft
, commonAncestorDoc
);
2538 nsIContent
* content2
= FindContentInDocument(aRight
, commonAncestorDoc
);
2539 if (!content1
|| !content2
) {
2540 NS_ERROR("Document trees are mixed up!");
2541 // Something weird going on
2544 return nsContentUtils::CompareTreePosition
<TreeKind::Flat
>(
2545 content1
, content2
, mCommonAncestor
) < 0;
2549 void nsDisplayList::SortByContentOrder(nsIContent
* aCommonAncestor
) {
2550 Sort
<nsDisplayItem
*>(ContentComparator(aCommonAncestor
));
2553 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2554 static_assert(sizeof(nsDisplayItem
) <= 176, "nsDisplayItem has grown");
2557 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
)
2558 : nsDisplayItem(aBuilder
, aFrame
, aBuilder
->CurrentActiveScrolledRoot()) {}
2560 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
2561 const ActiveScrolledRoot
* aActiveScrolledRoot
)
2562 : mFrame(aFrame
), mActiveScrolledRoot(aActiveScrolledRoot
) {
2563 MOZ_COUNT_CTOR(nsDisplayItem
);
2565 if (aBuilder
->IsRetainingDisplayList()) {
2566 mFrame
->AddDisplayItem(this);
2569 aBuilder
->FindReferenceFrameFor(aFrame
, &mToReferenceFrame
);
2571 aBuilder
->GetVisibleRect().width
>= 0 || !aBuilder
->IsForPainting(),
2572 "visible rect not set");
2574 mClipChain
= aBuilder
->ClipState().GetCurrentCombinedClipChain(aBuilder
);
2576 // The visible rect is for mCurrentFrame, so we have to use
2577 // mCurrentOffsetToReferenceFrame
2578 nsRect visible
= aBuilder
->GetVisibleRect() +
2579 aBuilder
->GetCurrentFrameOffsetToReferenceFrame();
2580 SetBuildingRect(visible
);
2582 const nsStyleDisplay
* disp
= mFrame
->StyleDisplay();
2583 if (mFrame
->BackfaceIsHidden(disp
)) {
2584 mItemFlags
+= ItemFlag::BackfaceHidden
;
2586 if (mFrame
->Combines3DTransformWithAncestors()) {
2587 mItemFlags
+= ItemFlag::Combines3DTransformWithAncestors
;
2591 void nsDisplayItem::SetDeletedFrame() { mItemFlags
+= ItemFlag::DeletedFrame
; }
2593 bool nsDisplayItem::HasDeletedFrame() const {
2594 bool retval
= mItemFlags
.contains(ItemFlag::DeletedFrame
) ||
2595 (GetType() == DisplayItemType::TYPE_REMOTE
&&
2596 !static_cast<const nsDisplayRemote
*>(this)->GetFrameLoader());
2597 MOZ_ASSERT(retval
|| mFrame
);
2602 bool nsDisplayItem::ForceActiveLayers() {
2603 return StaticPrefs::layers_force_active();
2606 int32_t nsDisplayItem::ZIndex() const { return mFrame
->ZIndex().valueOr(0); }
2608 void nsDisplayItem::SetClipChain(const DisplayItemClipChain
* aClipChain
,
2610 mClipChain
= aClipChain
;
2613 Maybe
<nsRect
> nsDisplayItem::GetClipWithRespectToASR(
2614 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
) const {
2615 if (const DisplayItemClip
* clip
=
2616 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR
)) {
2617 return Some(clip
->GetClipRect());
2620 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
2625 const DisplayItemClip
& nsDisplayItem::GetClip() const {
2626 const DisplayItemClip
* clip
=
2627 DisplayItemClipChain::ClipForASR(mClipChain
, mActiveScrolledRoot
);
2628 return clip
? *clip
: DisplayItemClip::NoClip();
2631 void nsDisplayItem::IntersectClip(nsDisplayListBuilder
* aBuilder
,
2632 const DisplayItemClipChain
* aOther
,
2634 if (!aOther
|| mClipChain
== aOther
) {
2638 // aOther might be a reference to a clip on the stack. We need to make sure
2639 // that CreateClipChainIntersection will allocate the actual intersected
2640 // clip in the builder's arena, so for the mClipChain == nullptr case,
2641 // we supply nullptr as the common ancestor so that
2642 // CreateClipChainIntersection clones the whole chain.
2643 const DisplayItemClipChain
* ancestorClip
=
2644 mClipChain
? FindCommonAncestorClipForIntersection(mClipChain
, aOther
)
2648 aBuilder
->CreateClipChainIntersection(ancestorClip
, mClipChain
, aOther
),
2652 nsRect
nsDisplayItem::GetClippedBounds(nsDisplayListBuilder
* aBuilder
) const {
2654 nsRect r
= GetBounds(aBuilder
, &snap
);
2655 return GetClip().ApplyNonRoundedIntersection(r
);
2658 nsDisplayContainer::nsDisplayContainer(
2659 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
2660 const ActiveScrolledRoot
* aActiveScrolledRoot
, nsDisplayList
* aList
)
2661 : nsDisplayItem(aBuilder
, aFrame
, aActiveScrolledRoot
),
2662 mChildren(aBuilder
) {
2663 MOZ_COUNT_CTOR(nsDisplayContainer
);
2664 mChildren
.AppendToTop(aList
);
2665 UpdateBounds(aBuilder
);
2667 // Clear and store the clip chain set by nsDisplayItem constructor.
2668 nsDisplayItem::SetClipChain(nullptr, true);
2671 nsRect
nsDisplayItem::GetPaintRect(nsDisplayListBuilder
* aBuilder
,
2674 nsRect result
= GetBounds(aBuilder
, &dummy
);
2676 result
.IntersectRect(result
,
2677 nsLayoutUtils::RoundGfxRectToAppRect(
2678 aCtx
->GetClipExtents(),
2679 mFrame
->PresContext()->AppUnitsPerDevPixel()));
2684 bool nsDisplayContainer::CreateWebRenderCommands(
2685 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
2686 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
2687 nsDisplayListBuilder
* aDisplayListBuilder
) {
2688 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2689 GetChildren(), this, aDisplayListBuilder
, aSc
, aBuilder
, aResources
,
2694 nsRect
nsDisplayContainer::GetBounds(nsDisplayListBuilder
* aBuilder
,
2695 bool* aSnap
) const {
2700 nsRect
nsDisplayContainer::GetComponentAlphaBounds(
2701 nsDisplayListBuilder
* aBuilder
) const {
2702 return mChildren
.GetComponentAlphaBounds(aBuilder
);
2705 static nsRegion
GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
2706 nsDisplayList
* aList
,
2707 const nsRect
& aListBounds
) {
2708 return aList
->GetOpaqueRegion(aBuilder
);
2711 nsRegion
nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
2712 bool* aSnap
) const {
2713 return ::mozilla::GetOpaqueRegion(aBuilder
, GetChildren(),
2714 GetBounds(aBuilder
, aSnap
));
2717 Maybe
<nsRect
> nsDisplayContainer::GetClipWithRespectToASR(
2718 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
) const {
2719 // Our children should have finite bounds with respect to |aASR|.
2720 if (aASR
== mActiveScrolledRoot
) {
2721 return Some(mBounds
);
2724 return Some(mChildren
.GetClippedBoundsWithRespectToASR(aBuilder
, aASR
));
2727 void nsDisplayContainer::HitTest(nsDisplayListBuilder
* aBuilder
,
2728 const nsRect
& aRect
, HitTestState
* aState
,
2729 nsTArray
<nsIFrame
*>* aOutFrames
) {
2730 mChildren
.HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
2733 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder
* aBuilder
) {
2734 // Container item bounds are expected to be clipped.
2736 mChildren
.GetClippedBoundsWithRespectToASR(aBuilder
, mActiveScrolledRoot
);
2739 nsRect
nsDisplaySolidColor::GetBounds(nsDisplayListBuilder
* aBuilder
,
2740 bool* aSnap
) const {
2745 void nsDisplaySolidColor::Paint(nsDisplayListBuilder
* aBuilder
,
2747 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
2748 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
2749 Rect rect
= NSRectToSnappedRect(GetPaintRect(aBuilder
, aCtx
),
2750 appUnitsPerDevPixel
, *drawTarget
);
2751 drawTarget
->FillRect(rect
, ColorPattern(ToDeviceColor(mColor
)));
2754 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream
& aStream
) {
2755 aStream
<< " (rgba " << (int)NS_GET_R(mColor
) << "," << (int)NS_GET_G(mColor
)
2756 << "," << (int)NS_GET_B(mColor
) << "," << (int)NS_GET_A(mColor
)
2760 bool nsDisplaySolidColor::CreateWebRenderCommands(
2761 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
2762 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
2763 nsDisplayListBuilder
* aDisplayListBuilder
) {
2764 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
2765 mBounds
, mFrame
->PresContext()->AppUnitsPerDevPixel());
2766 wr::LayoutRect r
= wr::ToLayoutRect(bounds
);
2767 aBuilder
.PushRect(r
, r
, !BackfaceIsHidden(), false, mIsCheckerboardBackground
,
2768 wr::ToColorF(ToDeviceColor(mColor
)));
2773 nsRect
nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder
* aBuilder
,
2774 bool* aSnap
) const {
2776 return mRegion
.GetBounds();
2779 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder
* aBuilder
,
2781 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
2782 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
2783 ColorPattern
color(ToDeviceColor(mColor
));
2784 for (auto iter
= mRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
2786 NSRectToSnappedRect(iter
.Get(), appUnitsPerDevPixel
, *drawTarget
);
2787 drawTarget
->FillRect(rect
, color
);
2791 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream
& aStream
) {
2792 aStream
<< " (rgba " << int(mColor
.r
* 255) << "," << int(mColor
.g
* 255)
2793 << "," << int(mColor
.b
* 255) << "," << mColor
.a
<< ")";
2796 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2797 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
2798 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
2799 nsDisplayListBuilder
* aDisplayListBuilder
) {
2800 for (auto iter
= mRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
2801 nsRect rect
= iter
.Get();
2802 LayoutDeviceRect layerRects
= LayoutDeviceRect::FromAppUnits(
2803 rect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
2804 wr::LayoutRect r
= wr::ToLayoutRect(layerRects
);
2805 aBuilder
.PushRect(r
, r
, !BackfaceIsHidden(), false, false,
2806 wr::ToColorF(ToDeviceColor(mColor
)));
2812 static void RegisterThemeGeometry(nsDisplayListBuilder
* aBuilder
,
2813 nsDisplayItem
* aItem
, nsIFrame
* aFrame
,
2814 nsITheme::ThemeGeometryType aType
) {
2815 if (aBuilder
->IsInChromeDocumentOrPopup()) {
2816 nsIFrame
* displayRoot
= nsLayoutUtils::GetDisplayRootFrame(aFrame
);
2817 bool preservesAxisAlignedRectangles
= false;
2818 nsRect borderBox
= nsLayoutUtils::TransformFrameRectToAncestor(
2819 aFrame
, aFrame
->GetRectRelativeToSelf(), displayRoot
,
2820 &preservesAxisAlignedRectangles
);
2821 if (preservesAxisAlignedRectangles
) {
2822 aBuilder
->RegisterThemeGeometry(
2824 LayoutDeviceIntRect::FromUnknownRect(borderBox
.ToNearestPixels(
2825 aFrame
->PresContext()->AppUnitsPerDevPixel())));
2830 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2831 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
2832 static Maybe
<nsRect
> GetViewportRectRelativeToReferenceFrame(
2833 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
) {
2834 nsIFrame
* rootFrame
= aFrame
->PresShell()->GetRootFrame();
2835 nsRect rootRect
= rootFrame
->GetRectRelativeToSelf();
2836 if (nsLayoutUtils::TransformRect(rootFrame
, aFrame
, rootRect
) ==
2837 nsLayoutUtils::TRANSFORM_SUCCEEDED
) {
2838 return Some(rootRect
+ aBuilder
->ToReferenceFrame(aFrame
));
2843 /* static */ nsDisplayBackgroundImage::InitData
2844 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder
* aBuilder
,
2845 nsIFrame
* aFrame
, uint16_t aLayer
,
2846 const nsRect
& aBackgroundRect
,
2847 const ComputedStyle
* aBackgroundStyle
) {
2848 nsPresContext
* presContext
= aFrame
->PresContext();
2849 uint32_t flags
= aBuilder
->GetBackgroundPaintFlags();
2850 const nsStyleImageLayers::Layer
& layer
=
2851 aBackgroundStyle
->StyleBackground()->mImage
.mLayers
[aLayer
];
2853 bool isTransformedFixed
;
2854 nsBackgroundLayerState state
= nsCSSRendering::PrepareImageLayer(
2855 presContext
, aFrame
, flags
, aBackgroundRect
, aBackgroundRect
, layer
,
2856 &isTransformedFixed
);
2858 // background-attachment:fixed is treated as background-attachment:scroll
2859 // if it's affected by a transform.
2860 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2861 bool shouldTreatAsFixed
=
2862 layer
.mAttachment
== StyleImageLayerAttachment::Fixed
&&
2863 !isTransformedFixed
;
2865 bool shouldFixToViewport
= shouldTreatAsFixed
&& !layer
.mImage
.IsNone();
2866 bool isRasterImage
= state
.mImageRenderer
.IsRasterImage();
2867 nsCOMPtr
<imgIContainer
> image
;
2868 if (isRasterImage
) {
2869 image
= state
.mImageRenderer
.GetImage();
2871 return InitData
{aBuilder
, aBackgroundStyle
, image
,
2872 aBackgroundRect
, state
.mFillArea
, state
.mDestArea
,
2873 aLayer
, isRasterImage
, shouldFixToViewport
};
2876 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2877 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, const InitData
& aInitData
,
2878 nsIFrame
* aFrameForBounds
)
2879 : nsPaintedDisplayItem(aBuilder
, aFrame
),
2880 mBackgroundStyle(aInitData
.backgroundStyle
),
2881 mImage(aInitData
.image
),
2882 mDependentFrame(nullptr),
2883 mBackgroundRect(aInitData
.backgroundRect
),
2884 mFillRect(aInitData
.fillArea
),
2885 mDestRect(aInitData
.destArea
),
2886 mLayer(aInitData
.layer
),
2887 mIsRasterImage(aInitData
.isRasterImage
),
2888 mShouldFixToViewport(aInitData
.shouldFixToViewport
) {
2889 MOZ_COUNT_CTOR(nsDisplayBackgroundImage
);
2891 if (mBackgroundStyle
&& mBackgroundStyle
!= mFrame
->Style()) {
2892 // If this changes, then you also need to adjust css::ImageLoader to
2893 // invalidate mFrame as needed.
2894 MOZ_ASSERT(mFrame
->IsCanvasFrame() || mFrame
->IsTablePart());
2898 mBounds
= GetBoundsInternal(aInitData
.builder
, aFrameForBounds
);
2899 if (mShouldFixToViewport
) {
2900 // Expand the item's visible rect to cover the entire bounds, limited to the
2901 // viewport rect. This is necessary because the background's clip can move
2903 if (Maybe
<nsRect
> viewportRect
= GetViewportRectRelativeToReferenceFrame(
2904 aInitData
.builder
, mFrame
)) {
2905 SetBuildingRect(mBounds
.Intersect(*viewportRect
));
2910 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2911 MOZ_COUNT_DTOR(nsDisplayBackgroundImage
);
2912 if (mDependentFrame
) {
2913 mDependentFrame
->RemoveDisplayItem(this);
2917 static void SetBackgroundClipRegion(
2918 DisplayListClipState::AutoSaveRestore
& aClipState
, nsIFrame
* aFrame
,
2919 const nsStyleImageLayers::Layer
& aLayer
, const nsRect
& aBackgroundRect
,
2920 bool aWillPaintBorder
) {
2921 nsCSSRendering::ImageLayerClipState clip
;
2922 nsCSSRendering::GetImageLayerClip(
2923 aLayer
, aFrame
, *aFrame
->StyleBorder(), aBackgroundRect
, aBackgroundRect
,
2924 aWillPaintBorder
, aFrame
->PresContext()->AppUnitsPerDevPixel(), &clip
);
2926 if (clip
.mHasAdditionalBGClipArea
) {
2927 aClipState
.ClipContentDescendants(
2928 clip
.mAdditionalBGClipArea
, clip
.mBGClipArea
,
2929 clip
.mHasRoundedCorners
? clip
.mRadii
: nullptr);
2931 aClipState
.ClipContentDescendants(
2932 clip
.mBGClipArea
, clip
.mHasRoundedCorners
? clip
.mRadii
: nullptr);
2937 * This is used for the find bar highlighter overlay. It's only accessible
2938 * through the AnonymousContent API, so it's not exposed to general web pages.
2940 static bool SpecialCutoutRegionCase(nsDisplayListBuilder
* aBuilder
,
2942 const nsRect
& aBackgroundRect
,
2943 nsDisplayList
* aList
, nscolor aColor
) {
2944 nsIContent
* content
= aFrame
->GetContent();
2949 void* cutoutRegion
= content
->GetProperty(nsGkAtoms::cutoutregion
);
2950 if (!cutoutRegion
) {
2954 if (NS_GET_A(aColor
) == 0) {
2959 region
.Sub(aBackgroundRect
, *static_cast<nsRegion
*>(cutoutRegion
));
2960 region
.MoveBy(aBuilder
->ToReferenceFrame(aFrame
));
2961 aList
->AppendNewToTop
<nsDisplaySolidColorRegion
>(aBuilder
, aFrame
, region
,
2967 enum class TableType
: uint8_t {
2978 enum class TableTypeBits
: uint8_t { Count
= 3 };
2980 static_assert(static_cast<uint8_t>(TableType::MAX
) <
2981 (1 << (static_cast<uint8_t>(TableTypeBits::Count
) + 1)),
2982 "TableType cannot fit with TableTypeBits::Count");
2983 TableType
GetTableTypeFromFrame(nsIFrame
* aFrame
);
2985 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex
,
2986 const TableType aType
) {
2987 const uint32_t key
= (aIndex
<< static_cast<uint8_t>(TableTypeBits::Count
)) |
2988 static_cast<uint8_t>(aType
);
2990 return static_cast<uint16_t>(key
);
2993 static nsDisplayBackgroundImage
* CreateBackgroundImage(
2994 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
2995 const nsDisplayBackgroundImage::InitData
& aBgData
) {
2996 const auto index
= aBgData
.layer
;
2998 if (aSecondaryFrame
) {
2999 const auto tableType
= GetTableTypeFromFrame(aFrame
);
3000 const uint16_t tableItemIndex
= CalculateTablePerFrameKey(index
, tableType
);
3002 return MakeDisplayItemWithIndex
<nsDisplayTableBackgroundImage
>(
3003 aBuilder
, aSecondaryFrame
, tableItemIndex
, aBgData
, aFrame
);
3006 return MakeDisplayItemWithIndex
<nsDisplayBackgroundImage
>(aBuilder
, aFrame
,
3010 static nsDisplayThemedBackground
* CreateThemedBackground(
3011 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
3012 const nsRect
& aBgRect
) {
3013 if (aSecondaryFrame
) {
3014 const uint16_t index
= static_cast<uint16_t>(GetTableTypeFromFrame(aFrame
));
3015 return MakeDisplayItemWithIndex
<nsDisplayTableThemedBackground
>(
3016 aBuilder
, aSecondaryFrame
, index
, aBgRect
, aFrame
);
3019 return MakeDisplayItem
<nsDisplayThemedBackground
>(aBuilder
, aFrame
, aBgRect
);
3022 static nsDisplayBackgroundColor
* CreateBackgroundColor(
3023 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
3024 nsRect
& aBgRect
, const ComputedStyle
* aBgSC
, nscolor aColor
) {
3025 if (aSecondaryFrame
) {
3026 const uint16_t index
= static_cast<uint16_t>(GetTableTypeFromFrame(aFrame
));
3027 return MakeDisplayItemWithIndex
<nsDisplayTableBackgroundColor
>(
3028 aBuilder
, aSecondaryFrame
, index
, aBgRect
, aBgSC
, aColor
, aFrame
);
3031 return MakeDisplayItem
<nsDisplayBackgroundColor
>(aBuilder
, aFrame
, aBgRect
,
3035 static void DealWithWindowsAppearanceHacks(nsIFrame
* aFrame
,
3036 nsDisplayListBuilder
* aBuilder
) {
3037 const auto& disp
= *aFrame
->StyleDisplay();
3039 // We use default appearance rather than effective appearance because we want
3040 // to handle when titlebar buttons that have appearance: none.
3041 const auto defaultAppearance
= disp
.mDefaultAppearance
;
3042 if (MOZ_LIKELY(defaultAppearance
== StyleAppearance::None
)) {
3046 if (auto type
= disp
.GetWindowButtonType()) {
3047 if (auto* widget
= aFrame
->GetNearestWidget()) {
3048 auto rect
= LayoutDevicePixel::FromAppUnitsToNearest(
3049 nsRect(aBuilder
->ToReferenceFrame(aFrame
), aFrame
->GetSize()),
3050 aFrame
->PresContext()->AppUnitsPerDevPixel());
3051 widget
->SetWindowButtonRect(*type
, rect
);
3057 AppendedBackgroundType
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3058 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
3059 const nsRect
& aBackgroundRect
, nsDisplayList
* aList
,
3060 bool aAllowWillPaintBorderOptimization
, const nsRect
& aBackgroundOriginRect
,
3061 nsIFrame
* aSecondaryReferenceFrame
,
3062 Maybe
<nsDisplayListBuilder::AutoBuildingDisplayList
>*
3063 aAutoBuildingDisplayList
) {
3064 MOZ_ASSERT(!aFrame
->IsCanvasFrame(),
3065 "We don't expect propagated canvas backgrounds here");
3068 nsIFrame
* bgFrame
= nsCSSRendering::FindBackgroundFrame(aFrame
);
3070 !bgFrame
|| bgFrame
== aFrame
,
3071 "Should only suppress backgrounds, never propagate to another frame");
3075 DealWithWindowsAppearanceHacks(aFrame
, aBuilder
);
3077 const bool isThemed
= aFrame
->IsThemed();
3079 const ComputedStyle
* bgSC
= aFrame
->Style();
3080 const nsStyleBackground
* bg
= bgSC
->StyleBackground();
3081 const bool needsBackgroundColor
=
3082 aBuilder
->IsForEventDelivery() ||
3083 (EffectCompositor::HasAnimationsForCompositor(
3084 aFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
) &&
3086 if (!needsBackgroundColor
&& !isThemed
&& bg
->IsTransparent(bgSC
)) {
3087 return AppendedBackgroundType::None
;
3090 bool drawBackgroundColor
= false;
3091 bool drawBackgroundImage
= false;
3092 nscolor color
= NS_RGBA(0, 0, 0, 0);
3093 // Don't get background color / images if we propagated our background to the
3094 // canvas (that is, if FindBackgroundFrame is null). But don't early return
3095 // yet, since we might still need a background-color item for hit-testing.
3096 if (!isThemed
&& nsCSSRendering::FindBackgroundFrame(aFrame
)) {
3097 color
= nsCSSRendering::DetermineBackgroundColor(
3098 aFrame
->PresContext(), bgSC
, aFrame
, drawBackgroundImage
,
3099 drawBackgroundColor
);
3102 if (SpecialCutoutRegionCase(aBuilder
, aFrame
, aBackgroundRect
, aList
,
3104 return AppendedBackgroundType::None
;
3107 const nsStyleBorder
& border
= *aFrame
->StyleBorder();
3108 const bool willPaintBorder
=
3109 aAllowWillPaintBorderOptimization
&& !isThemed
&&
3110 !aFrame
->StyleEffects()->HasBoxShadowWithInset(true) &&
3113 auto EnsureBuildingDisplayList
= [&] {
3114 if (!aAutoBuildingDisplayList
|| *aAutoBuildingDisplayList
) {
3117 nsPoint offset
= aBuilder
->GetCurrentFrame()->GetOffsetTo(aFrame
);
3118 aAutoBuildingDisplayList
->emplace(aBuilder
, aFrame
,
3119 aBuilder
->GetVisibleRect() + offset
,
3120 aBuilder
->GetDirtyRect() + offset
);
3123 // An auxiliary list is necessary in case we have background blending; if that
3124 // is the case, background items need to be wrapped by a blend container to
3125 // isolate blending to the background
3126 nsDisplayList
bgItemList(aBuilder
);
3127 // Even if we don't actually have a background color to paint, we may still
3128 // need to create an item for hit testing and we still need to create an item
3129 // for background-color animations.
3130 if ((drawBackgroundColor
&& color
!= NS_RGBA(0, 0, 0, 0)) ||
3131 needsBackgroundColor
) {
3132 EnsureBuildingDisplayList();
3133 Maybe
<DisplayListClipState::AutoSaveRestore
> clipState
;
3134 nsRect bgColorRect
= aBackgroundRect
;
3135 if (!isThemed
&& !aBuilder
->IsForEventDelivery()) {
3136 // Disable the will-paint-border optimization for background
3137 // colors with no border-radius. Enabling it for background colors
3138 // doesn't help much (there are no tiling issues) and clipping the
3139 // background breaks detection of the element's border-box being
3140 // opaque. For nonzero border-radius we still need it because we
3141 // want to inset the background if possible to avoid antialiasing
3142 // artifacts along the rounded corners.
3143 const bool useWillPaintBorderOptimization
=
3145 nsLayoutUtils::HasNonZeroCorner(border
.mBorderRadius
);
3147 nsCSSRendering::ImageLayerClipState clip
;
3148 nsCSSRendering::GetImageLayerClip(
3149 bg
->BottomLayer(), aFrame
, border
, aBackgroundRect
, aBackgroundRect
,
3150 useWillPaintBorderOptimization
,
3151 aFrame
->PresContext()->AppUnitsPerDevPixel(), &clip
);
3153 bgColorRect
= bgColorRect
.Intersect(clip
.mBGClipArea
);
3154 if (clip
.mHasAdditionalBGClipArea
) {
3155 bgColorRect
= bgColorRect
.Intersect(clip
.mAdditionalBGClipArea
);
3157 if (clip
.mHasRoundedCorners
) {
3158 clipState
.emplace(aBuilder
);
3159 clipState
->ClipContentDescendants(clip
.mBGClipArea
, clip
.mRadii
);
3163 nsDisplayBackgroundColor
* bgItem
= CreateBackgroundColor(
3164 aBuilder
, aFrame
, aSecondaryReferenceFrame
, bgColorRect
, bgSC
,
3165 drawBackgroundColor
? color
: NS_RGBA(0, 0, 0, 0));
3168 bgItemList
.AppendToTop(bgItem
);
3173 nsDisplayThemedBackground
* bgItem
= CreateThemedBackground(
3174 aBuilder
, aFrame
, aSecondaryReferenceFrame
, aBackgroundRect
);
3177 bgItem
->Init(aBuilder
);
3178 bgItemList
.AppendToTop(bgItem
);
3181 if (!bgItemList
.IsEmpty()) {
3182 aList
->AppendToTop(&bgItemList
);
3183 return AppendedBackgroundType::ThemedBackground
;
3186 return AppendedBackgroundType::None
;
3189 if (!drawBackgroundImage
) {
3190 if (!bgItemList
.IsEmpty()) {
3191 aList
->AppendToTop(&bgItemList
);
3192 return AppendedBackgroundType::Background
;
3195 return AppendedBackgroundType::None
;
3198 const ActiveScrolledRoot
* asr
= aBuilder
->CurrentActiveScrolledRoot();
3200 bool needBlendContainer
= false;
3201 const nsRect
& bgOriginRect
=
3202 aBackgroundOriginRect
.IsEmpty() ? aBackgroundRect
: aBackgroundOriginRect
;
3204 // Passing bg == nullptr in this macro will result in one iteration with
3206 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i
, bg
->mImage
) {
3207 if (bg
->mImage
.mLayers
[i
].mImage
.IsNone()) {
3211 EnsureBuildingDisplayList();
3213 if (bg
->mImage
.mLayers
[i
].mBlendMode
!= StyleBlend::Normal
) {
3214 needBlendContainer
= true;
3217 DisplayListClipState::AutoSaveRestore
clipState(aBuilder
);
3218 if (!aBuilder
->IsForEventDelivery()) {
3219 const nsStyleImageLayers::Layer
& layer
= bg
->mImage
.mLayers
[i
];
3220 SetBackgroundClipRegion(clipState
, aFrame
, layer
, aBackgroundRect
,
3224 nsDisplayList
thisItemList(aBuilder
);
3225 nsDisplayBackgroundImage::InitData bgData
=
3226 nsDisplayBackgroundImage::GetInitData(aBuilder
, aFrame
, i
, bgOriginRect
,
3229 if (bgData
.shouldFixToViewport
) {
3230 auto* displayData
= aBuilder
->GetCurrentFixedBackgroundDisplayData();
3231 nsDisplayListBuilder::AutoBuildingDisplayList
buildingDisplayList(
3232 aBuilder
, aFrame
, aBuilder
->GetVisibleRect(),
3233 aBuilder
->GetDirtyRect());
3235 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter
asrSetter(
3238 asrSetter
.SetCurrentActiveScrolledRoot(
3239 displayData
->mContainingBlockActiveScrolledRoot
);
3240 asrSetter
.SetCurrentScrollParentId(displayData
->mScrollParentId
);
3241 if (nsLayoutUtils::UsesAsyncScrolling(aFrame
)) {
3242 // Override the dirty rect on the builder to be the dirty rect of
3244 // displayData->mDirtyRect is relative to the presshell's viewport
3245 // frame (the root frame), and we need it to be relative to aFrame.
3246 nsIFrame
* rootFrame
=
3247 aBuilder
->CurrentPresShellState()->mPresShell
->GetRootFrame();
3248 // There cannot be any transforms between aFrame and rootFrame
3249 // because then bgData.shouldFixToViewport would have been false.
3250 nsRect visibleRect
=
3251 displayData
->mVisibleRect
+ aFrame
->GetOffsetTo(rootFrame
);
3252 aBuilder
->SetVisibleRect(visibleRect
);
3254 displayData
->mDirtyRect
+ aFrame
->GetOffsetTo(rootFrame
);
3255 aBuilder
->SetDirtyRect(dirtyRect
);
3259 nsDisplayBackgroundImage
* bgItem
= nullptr;
3261 // The clip is captured by the nsDisplayFixedPosition, so clear the
3262 // clip for the nsDisplayBackgroundImage inside.
3263 DisplayListClipState::AutoSaveRestore
bgImageClip(aBuilder
);
3264 bgImageClip
.Clear();
3265 bgItem
= CreateBackgroundImage(aBuilder
, aFrame
,
3266 aSecondaryReferenceFrame
, bgData
);
3269 thisItemList
.AppendToTop(
3270 nsDisplayFixedPosition::CreateForFixedBackground(
3271 aBuilder
, aFrame
, aSecondaryReferenceFrame
, bgItem
, i
, asr
));
3273 } else { // bgData.shouldFixToViewport == false
3274 nsDisplayBackgroundImage
* bgItem
= CreateBackgroundImage(
3275 aBuilder
, aFrame
, aSecondaryReferenceFrame
, bgData
);
3277 thisItemList
.AppendToTop(bgItem
);
3281 if (bg
->mImage
.mLayers
[i
].mBlendMode
!= StyleBlend::Normal
) {
3282 // asr is scrolled. Even if we wrap a fixed background layer, that's
3283 // fine, because the item will have a scrolled clip that limits the
3284 // item with respect to asr.
3285 if (aSecondaryReferenceFrame
) {
3286 const auto tableType
= GetTableTypeFromFrame(aFrame
);
3287 const uint16_t index
= CalculateTablePerFrameKey(i
+ 1, tableType
);
3289 thisItemList
.AppendNewToTopWithIndex
<nsDisplayTableBlendMode
>(
3290 aBuilder
, aSecondaryReferenceFrame
, index
, &thisItemList
,
3291 bg
->mImage
.mLayers
[i
].mBlendMode
, asr
, aFrame
, true);
3293 thisItemList
.AppendNewToTopWithIndex
<nsDisplayBlendMode
>(
3294 aBuilder
, aFrame
, i
+ 1, &thisItemList
,
3295 bg
->mImage
.mLayers
[i
].mBlendMode
, asr
, true);
3298 bgItemList
.AppendToTop(&thisItemList
);
3301 if (needBlendContainer
) {
3302 bgItemList
.AppendToTop(
3303 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3304 aBuilder
, aFrame
, aSecondaryReferenceFrame
, &bgItemList
, asr
));
3307 if (!bgItemList
.IsEmpty()) {
3308 aList
->AppendToTop(&bgItemList
);
3309 return AppendedBackgroundType::Background
;
3312 return AppendedBackgroundType::None
;
3315 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3316 // intersects aRect. Assumes that the unrounded border has already
3317 // been checked for intersection.
3318 static bool RoundedBorderIntersectsRect(nsIFrame
* aFrame
,
3319 const nsPoint
& aFrameToReferenceFrame
,
3320 const nsRect
& aTestRect
) {
3321 if (!nsRect(aFrameToReferenceFrame
, aFrame
->GetSize())
3322 .Intersects(aTestRect
)) {
3327 return !aFrame
->GetBorderRadii(radii
) ||
3328 nsLayoutUtils::RoundedRectIntersectsRect(
3329 nsRect(aFrameToReferenceFrame
, aFrame
->GetSize()), radii
,
3333 // Returns TRUE if aContainedRect is guaranteed to be contained in
3334 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3335 // handled conservatively by returning FALSE in some situations where
3336 // a more thorough analysis could return TRUE.
3338 // See also RoundedRectIntersectsRect.
3339 static bool RoundedRectContainsRect(const nsRect
& aRoundedRect
,
3340 const nscoord aRadii
[8],
3341 const nsRect
& aContainedRect
) {
3342 nsRegion rgn
= nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect
, aRadii
,
3344 return rgn
.Contains(aContainedRect
);
3347 bool nsDisplayBackgroundImage::CanApplyOpacity(
3348 WebRenderLayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
) const {
3349 return CanBuildWebRenderDisplayItems(aManager
, aBuilder
);
3352 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3353 WebRenderLayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
) const {
3354 return mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
].mClip
!=
3355 StyleGeometryBox::Text
&&
3356 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3357 aManager
, *StyleFrame()->PresContext(), StyleFrame(),
3358 mBackgroundStyle
->StyleBackground(), mLayer
,
3359 aBuilder
->GetBackgroundPaintFlags());
3362 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3363 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
3364 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
3365 nsDisplayListBuilder
* aDisplayListBuilder
) {
3366 if (!CanBuildWebRenderDisplayItems(aManager
->LayerManager(),
3367 aDisplayListBuilder
)) {
3371 uint32_t paintFlags
= aDisplayListBuilder
->GetBackgroundPaintFlags();
3373 nsCSSRendering::PaintBGParams params
=
3374 nsCSSRendering::PaintBGParams::ForSingleLayer(
3375 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder
, &dummy
),
3376 mBackgroundRect
, StyleFrame(), paintFlags
, mLayer
,
3377 CompositionOp::OP_OVER
, aBuilder
.GetInheritedOpacity());
3378 params
.bgClipRect
= &mBounds
;
3379 ImgDrawResult result
=
3380 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3381 params
, aBuilder
, aResources
, aSc
, aManager
, this);
3382 if (result
== ImgDrawResult::NOT_SUPPORTED
) {
3386 if (nsIContent
* content
= StyleFrame()->GetContent()) {
3387 if (imgRequestProxy
* requestProxy
= mBackgroundStyle
->StyleBackground()
3388 ->mImage
.mLayers
[mLayer
]
3389 .mImage
.GetImageRequest()) {
3390 // LCP don't consider gradient backgrounds.
3391 LCPHelpers::FinalizeLCPEntryForImage(content
->AsElement(), requestProxy
,
3392 mBounds
- ToReferenceFrame());
3399 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder
* aBuilder
,
3400 const nsRect
& aRect
,
3401 HitTestState
* aState
,
3402 nsTArray
<nsIFrame
*>* aOutFrames
) {
3403 if (RoundedBorderIntersectsRect(mFrame
, ToReferenceFrame(), aRect
)) {
3404 aOutFrames
->AppendElement(mFrame
);
3408 static nsRect
GetInsideClipRect(const nsDisplayItem
* aItem
,
3409 StyleGeometryBox aClip
, const nsRect
& aRect
,
3410 const nsRect
& aBackgroundRect
) {
3411 if (aRect
.IsEmpty()) {
3415 nsIFrame
* frame
= aItem
->Frame();
3417 nsRect clipRect
= aBackgroundRect
;
3418 if (frame
->IsCanvasFrame()) {
3419 nsCanvasFrame
* canvasFrame
= static_cast<nsCanvasFrame
*>(frame
);
3420 clipRect
= canvasFrame
->CanvasArea() + aItem
->ToReferenceFrame();
3421 } else if (aClip
== StyleGeometryBox::PaddingBox
||
3422 aClip
== StyleGeometryBox::ContentBox
) {
3423 nsMargin border
= frame
->GetUsedBorder();
3424 if (aClip
== StyleGeometryBox::ContentBox
) {
3425 border
+= frame
->GetUsedPadding();
3427 border
.ApplySkipSides(frame
->GetSkipSides());
3428 clipRect
.Deflate(border
);
3431 return clipRect
.Intersect(aRect
);
3434 nsRegion
nsDisplayBackgroundImage::GetOpaqueRegion(
3435 nsDisplayListBuilder
* aBuilder
, bool* aSnap
) const {
3439 if (!mBackgroundStyle
) {
3445 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3446 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3447 // which expects frames to be sent to it in content order, not reverse
3448 // content order which we'll produce here.
3449 // Of course, if there's only one frame in the flow, it doesn't matter.
3450 if (mFrame
->StyleBorder()->mBoxDecorationBreak
==
3451 StyleBoxDecorationBreak::Clone
||
3452 (!mFrame
->GetPrevContinuation() && !mFrame
->GetNextContinuation())) {
3453 const nsStyleImageLayers::Layer
& layer
=
3454 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
3455 if (layer
.mImage
.IsOpaque() && layer
.mBlendMode
== StyleBlend::Normal
&&
3456 layer
.mRepeat
.mXRepeat
!= StyleImageLayerRepeat::Space
&&
3457 layer
.mRepeat
.mYRepeat
!= StyleImageLayerRepeat::Space
&&
3458 layer
.mClip
!= StyleGeometryBox::Text
) {
3459 result
= GetInsideClipRect(this, layer
.mClip
, mBounds
, mBackgroundRect
);
3466 Maybe
<nscolor
> nsDisplayBackgroundImage::IsUniform(
3467 nsDisplayListBuilder
* aBuilder
) const {
3468 if (!mBackgroundStyle
) {
3469 return Some(NS_RGBA(0, 0, 0, 0));
3474 nsRect
nsDisplayBackgroundImage::GetPositioningArea() const {
3475 if (!mBackgroundStyle
) {
3478 nsIFrame
* attachedToFrame
;
3479 bool transformedFixed
;
3480 return nsCSSRendering::ComputeImageLayerPositioningArea(
3481 mFrame
->PresContext(), mFrame
, mBackgroundRect
,
3482 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
],
3483 &attachedToFrame
, &transformedFixed
) +
3487 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3489 if (!mBackgroundStyle
) {
3494 if (mFrame
->GetBorderRadii(radii
)) {
3495 // A change in the size of the positioning area might change the position
3496 // of the rounded corners.
3500 const nsStyleImageLayers::Layer
& layer
=
3501 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
3502 return layer
.RenderingMightDependOnPositioningAreaSizeChange();
3505 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder
* aBuilder
,
3507 PaintInternal(aBuilder
, aCtx
, GetPaintRect(aBuilder
, aCtx
), &mBounds
);
3510 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder
* aBuilder
,
3512 const nsRect
& aBounds
,
3513 nsRect
* aClipRect
) {
3514 gfxContext
* ctx
= aCtx
;
3515 StyleGeometryBox clip
=
3516 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
].mClip
;
3518 if (clip
== StyleGeometryBox::Text
) {
3519 if (!GenerateAndPushTextMask(StyleFrame(), aCtx
, mBackgroundRect
,
3525 nsCSSRendering::PaintBGParams params
=
3526 nsCSSRendering::PaintBGParams::ForSingleLayer(
3527 *StyleFrame()->PresContext(), aBounds
, mBackgroundRect
, StyleFrame(),
3528 aBuilder
->GetBackgroundPaintFlags(), mLayer
, CompositionOp::OP_OVER
,
3530 params
.bgClipRect
= aClipRect
;
3531 Unused
<< nsCSSRendering::PaintStyleImageLayer(params
, *aCtx
);
3533 if (clip
== StyleGeometryBox::Text
) {
3534 ctx
->PopGroupAndBlend();
3538 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3539 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
3540 nsRegion
* aInvalidRegion
) const {
3541 if (!mBackgroundStyle
) {
3545 const auto* geometry
=
3546 static_cast<const nsDisplayBackgroundGeometry
*>(aGeometry
);
3549 nsRect bounds
= GetBounds(aBuilder
, &snap
);
3550 nsRect positioningArea
= GetPositioningArea();
3551 if (positioningArea
.TopLeft() != geometry
->mPositioningArea
.TopLeft() ||
3552 (positioningArea
.Size() != geometry
->mPositioningArea
.Size() &&
3553 RenderingMightDependOnPositioningAreaSizeChange())) {
3554 // Positioning area changed in a way that could cause everything to change,
3555 // so invalidate everything (both old and new painting areas).
3556 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
3559 if (!mDestRect
.IsEqualInterior(geometry
->mDestRect
)) {
3560 // Dest area changed in a way that could cause everything to change,
3561 // so invalidate everything (both old and new painting areas).
3562 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
3565 if (!bounds
.IsEqualInterior(geometry
->mBounds
)) {
3566 // Positioning area is unchanged, so invalidate just the change in the
3568 aInvalidRegion
->Xor(bounds
, geometry
->mBounds
);
3572 nsRect
nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder
* aBuilder
,
3573 bool* aSnap
) const {
3578 nsRect
nsDisplayBackgroundImage::GetBoundsInternal(
3579 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrameForBounds
) {
3580 // This allows nsDisplayTableBackgroundImage to change the frame used for
3581 // bounds calculation.
3582 nsIFrame
* frame
= aFrameForBounds
? aFrameForBounds
: mFrame
;
3584 nsPresContext
* presContext
= frame
->PresContext();
3586 if (!mBackgroundStyle
) {
3590 nsRect clipRect
= mBackgroundRect
;
3591 if (frame
->IsCanvasFrame()) {
3592 nsCanvasFrame
* canvasFrame
= static_cast<nsCanvasFrame
*>(frame
);
3593 clipRect
= canvasFrame
->CanvasArea() + ToReferenceFrame();
3595 const nsStyleImageLayers::Layer
& layer
=
3596 mBackgroundStyle
->StyleBackground()->mImage
.mLayers
[mLayer
];
3597 return nsCSSRendering::GetBackgroundLayerRect(
3598 presContext
, frame
, mBackgroundRect
, clipRect
, layer
,
3599 aBuilder
->GetBackgroundPaintFlags());
3602 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3603 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, const InitData
& aData
,
3604 nsIFrame
* aCellFrame
)
3605 : nsDisplayBackgroundImage(aBuilder
, aFrame
, aData
, aCellFrame
),
3606 mStyleFrame(aCellFrame
) {
3607 if (aBuilder
->IsRetainingDisplayList()) {
3608 mStyleFrame
->AddDisplayItem(this);
3612 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3614 mStyleFrame
->RemoveDisplayItem(this);
3618 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect
& aRect
) const {
3619 bool result
= mStyleFrame
? mStyleFrame
->IsInvalid(aRect
) : false;
3620 aRect
+= ToReferenceFrame();
3624 nsDisplayThemedBackground::nsDisplayThemedBackground(
3625 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
3626 const nsRect
& aBackgroundRect
)
3627 : nsPaintedDisplayItem(aBuilder
, aFrame
), mBackgroundRect(aBackgroundRect
) {
3628 MOZ_COUNT_CTOR(nsDisplayThemedBackground
);
3631 void nsDisplayThemedBackground::Init(nsDisplayListBuilder
* aBuilder
) {
3632 const nsStyleDisplay
* disp
= StyleFrame()->StyleDisplay();
3633 mAppearance
= disp
->EffectiveAppearance();
3634 StyleFrame()->IsThemed(disp
, &mThemeTransparency
);
3636 // Perform necessary RegisterThemeGeometry
3637 nsITheme
* theme
= StyleFrame()->PresContext()->Theme();
3638 nsITheme::ThemeGeometryType type
=
3639 theme
->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance
);
3640 if (type
!= nsITheme::eThemeGeometryTypeUnknown
) {
3641 RegisterThemeGeometry(aBuilder
, this, StyleFrame(), type
);
3644 mBounds
= GetBoundsInternal();
3647 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream
& aStream
) {
3648 aStream
<< " (themed, appearance:" << (int)mAppearance
<< ")";
3651 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder
* aBuilder
,
3652 const nsRect
& aRect
,
3653 HitTestState
* aState
,
3654 nsTArray
<nsIFrame
*>* aOutFrames
) {
3655 // Assume that any point in our background rect is a hit.
3656 if (mBackgroundRect
.Intersects(aRect
)) {
3657 aOutFrames
->AppendElement(mFrame
);
3661 nsRegion
nsDisplayThemedBackground::GetOpaqueRegion(
3662 nsDisplayListBuilder
* aBuilder
, bool* aSnap
) const {
3666 if (mThemeTransparency
== nsITheme::eOpaque
) {
3668 result
= mBackgroundRect
;
3673 Maybe
<nscolor
> nsDisplayThemedBackground::IsUniform(
3674 nsDisplayListBuilder
* aBuilder
) const {
3678 nsRect
nsDisplayThemedBackground::GetPositioningArea() const {
3679 return mBackgroundRect
;
3682 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder
* aBuilder
,
3684 PaintInternal(aBuilder
, aCtx
, GetPaintRect(aBuilder
, aCtx
), nullptr);
3687 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder
* aBuilder
,
3689 const nsRect
& aBounds
,
3690 nsRect
* aClipRect
) {
3691 // XXXzw this ignores aClipRect.
3692 nsPresContext
* presContext
= StyleFrame()->PresContext();
3693 nsITheme
* theme
= presContext
->Theme();
3694 nsRect
drawing(mBackgroundRect
);
3695 theme
->GetWidgetOverflow(presContext
->DeviceContext(), StyleFrame(),
3696 mAppearance
, &drawing
);
3697 drawing
.IntersectRect(drawing
, aBounds
);
3698 theme
->DrawWidgetBackground(aCtx
, StyleFrame(), mAppearance
, mBackgroundRect
,
3702 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3703 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
3704 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
3705 nsDisplayListBuilder
* aDisplayListBuilder
) {
3706 nsITheme
* theme
= StyleFrame()->PresContext()->Theme();
3707 return theme
->CreateWebRenderCommandsForWidget(aBuilder
, aResources
, aSc
,
3708 aManager
, StyleFrame(),
3709 mAppearance
, mBackgroundRect
);
3712 bool nsDisplayThemedBackground::IsWindowActive() const {
3713 return !mFrame
->PresContext()->Document()->IsTopLevelWindowInactive();
3716 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3717 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
3718 nsRegion
* aInvalidRegion
) const {
3719 const auto* geometry
=
3720 static_cast<const nsDisplayThemedBackgroundGeometry
*>(aGeometry
);
3723 nsRect bounds
= GetBounds(aBuilder
, &snap
);
3724 nsRect positioningArea
= GetPositioningArea();
3725 if (!positioningArea
.IsEqualInterior(geometry
->mPositioningArea
)) {
3726 // Invalidate everything (both old and new painting areas).
3727 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
3730 if (!bounds
.IsEqualInterior(geometry
->mBounds
)) {
3731 // Positioning area is unchanged, so invalidate just the change in the
3733 aInvalidRegion
->Xor(bounds
, geometry
->mBounds
);
3735 nsITheme
* theme
= StyleFrame()->PresContext()->Theme();
3736 if (theme
->WidgetAppearanceDependsOnWindowFocus(mAppearance
) &&
3737 IsWindowActive() != geometry
->mWindowIsActive
) {
3738 aInvalidRegion
->Or(*aInvalidRegion
, bounds
);
3742 nsRect
nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder
* aBuilder
,
3743 bool* aSnap
) const {
3748 nsRect
nsDisplayThemedBackground::GetBoundsInternal() {
3749 nsPresContext
* presContext
= mFrame
->PresContext();
3751 nsRect r
= mBackgroundRect
- ToReferenceFrame();
3752 presContext
->Theme()->GetWidgetOverflow(
3753 presContext
->DeviceContext(), mFrame
,
3754 mFrame
->StyleDisplay()->EffectiveAppearance(), &r
);
3755 return r
+ ToReferenceFrame();
3758 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
3759 void nsDisplayReflowCount::Paint(nsDisplayListBuilder
* aBuilder
,
3761 mFrame
->PresShell()->PaintCount(mFrameName
, aCtx
, mFrame
->PresContext(),
3762 mFrame
, ToReferenceFrame(), mColor
);
3766 bool nsDisplayBackgroundColor::CanApplyOpacity(
3767 WebRenderLayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
) const {
3768 // Don't apply opacity if the background color is animated since the color is
3769 // going to be changed on the compositor.
3770 return !EffectCompositor::HasAnimationsForCompositor(
3771 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
);
3774 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3775 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
3776 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
3777 nsDisplayListBuilder
* aDisplayListBuilder
) {
3778 gfx::sRGBColor color
= mColor
;
3779 color
.a
*= aBuilder
.GetInheritedOpacity();
3781 if (color
== sRGBColor() &&
3782 !EffectCompositor::HasAnimationsForCompositor(
3783 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
)) {
3787 if (HasBackgroundClipText()) {
3791 uint64_t animationsId
= 0;
3792 // We don't support background-color animations on table elements yet.
3793 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR
) {
3795 AddAnimationsForWebRender(this, aManager
, aDisplayListBuilder
);
3798 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
3799 mBackgroundRect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
3800 wr::LayoutRect r
= wr::ToLayoutRect(bounds
);
3803 wr::WrAnimationProperty prop
{
3804 wr::WrAnimationType::BackgroundColor
,
3807 aBuilder
.PushRectWithAnimation(r
, r
, !BackfaceIsHidden(),
3808 wr::ToColorF(ToDeviceColor(color
)), &prop
);
3810 aBuilder
.StartGroup(this);
3811 aBuilder
.PushRect(r
, r
, !BackfaceIsHidden(), false, false,
3812 wr::ToColorF(ToDeviceColor(color
)));
3813 aBuilder
.FinishGroup();
3819 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder
* aBuilder
,
3821 const DisplayItemClip
& aClip
) {
3822 MOZ_ASSERT(!HasBackgroundClipText());
3824 if (mColor
== sRGBColor()) {
3828 nsRect fillRect
= mBackgroundRect
;
3829 if (aClip
.HasClip()) {
3830 fillRect
.IntersectRect(fillRect
, aClip
.GetClipRect());
3833 DrawTarget
* dt
= aCtx
->GetDrawTarget();
3834 int32_t A2D
= mFrame
->PresContext()->AppUnitsPerDevPixel();
3835 Rect bounds
= ToRect(nsLayoutUtils::RectToGfxRect(fillRect
, A2D
));
3836 MaybeSnapToDevicePixels(bounds
, *dt
);
3837 ColorPattern
fill(ToDeviceColor(mColor
));
3839 if (aClip
.GetRoundedRectCount()) {
3840 MOZ_ASSERT(aClip
.GetRoundedRectCount() == 1);
3842 AutoTArray
<DisplayItemClip::RoundedRect
, 1> roundedRect
;
3843 aClip
.AppendRoundedRects(&roundedRect
);
3845 bool pushedClip
= false;
3846 if (!fillRect
.Contains(roundedRect
[0].mRect
)) {
3847 dt
->PushClipRect(bounds
);
3851 RectCornerRadii pixelRadii
;
3852 nsCSSRendering::ComputePixelRadii(roundedRect
[0].mRadii
, A2D
, &pixelRadii
);
3853 dt
->FillRoundedRect(
3854 RoundedRect(NSRectToSnappedRect(roundedRect
[0].mRect
, A2D
, *dt
),
3861 dt
->FillRect(bounds
, fill
);
3865 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder
* aBuilder
,
3867 if (mColor
== sRGBColor()) {
3872 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3873 // results in a precision induced rounding issue that makes the rect one
3874 // pixel shorter in rare cases. Disabled in favor of the old code for now.
3875 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3876 // reproduce the bug.
3879 // This new path does not include support for background-clip:text; need to
3880 // be fixed if/when we switch to this new code path.
3882 DrawTarget
& aDrawTarget
= *aCtx
->GetDrawTarget();
3884 Rect rect
= NSRectToSnappedRect(mBackgroundRect
,
3885 mFrame
->PresContext()->AppUnitsPerDevPixel(),
3887 ColorPattern
color(ToDeviceColor(mColor
));
3888 aDrawTarget
.FillRect(rect
, color
);
3890 gfxContext
* ctx
= aCtx
;
3891 gfxRect bounds
= nsLayoutUtils::RectToGfxRect(
3892 mBackgroundRect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
3894 if (HasBackgroundClipText()) {
3895 if (!GenerateAndPushTextMask(mFrame
, aCtx
, mBackgroundRect
, aBuilder
)) {
3899 ctx
->SetColor(mColor
);
3901 ctx
->SnappedRectangle(bounds
);
3903 ctx
->PopGroupAndBlend();
3907 ctx
->SetColor(mColor
);
3909 ctx
->SnappedRectangle(bounds
);
3914 nsRegion
nsDisplayBackgroundColor::GetOpaqueRegion(
3915 nsDisplayListBuilder
* aBuilder
, bool* aSnap
) const {
3918 if (mColor
.a
!= 1 ||
3919 // Even if the current alpha channel is 1, we treat this item as if it's
3920 // non-opaque if there is a background-color animation since the animation
3921 // might change the alpha channel.
3922 EffectCompositor::HasAnimationsForCompositor(
3923 mFrame
, DisplayItemType::TYPE_BACKGROUND_COLOR
)) {
3927 if (!mHasStyle
|| HasBackgroundClipText()) {
3932 return GetInsideClipRect(this, mBottomLayerClip
, mBackgroundRect
,
3936 Maybe
<nscolor
> nsDisplayBackgroundColor::IsUniform(
3937 nsDisplayListBuilder
* aBuilder
) const {
3938 return Some(mColor
.ToABGR());
3941 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder
* aBuilder
,
3942 const nsRect
& aRect
,
3943 HitTestState
* aState
,
3944 nsTArray
<nsIFrame
*>* aOutFrames
) {
3945 if (!RoundedBorderIntersectsRect(mFrame
, ToReferenceFrame(), aRect
)) {
3946 // aRect doesn't intersect our border-radius curve.
3950 aOutFrames
->AppendElement(mFrame
);
3953 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream
& aStream
) {
3954 aStream
<< " (rgba " << mColor
.r
<< "," << mColor
.g
<< "," << mColor
.b
<< ","
3956 aStream
<< " backgroundRect" << mBackgroundRect
;
3959 nsRect
nsDisplayOutline::GetBounds(nsDisplayListBuilder
* aBuilder
,
3960 bool* aSnap
) const {
3962 return mFrame
->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
3965 nsRect
nsDisplayOutline::GetInnerRect() const {
3966 if (nsRect
* savedOutlineInnerRect
=
3967 mFrame
->GetProperty(nsIFrame::OutlineInnerRectProperty())) {
3968 return *savedOutlineInnerRect
;
3970 return mFrame
->GetRectRelativeToSelf();
3973 void nsDisplayOutline::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
3974 // TODO join outlines together
3975 MOZ_ASSERT(mFrame
->StyleOutline()->ShouldPaintOutline(),
3976 "Should have not created a nsDisplayOutline!");
3978 nsRect rect
= GetInnerRect() + ToReferenceFrame();
3979 nsPresContext
* pc
= mFrame
->PresContext();
3980 if (IsThemedOutline()) {
3981 rect
.Inflate(mFrame
->StyleOutline()->EffectiveOffsetFor(rect
));
3982 pc
->Theme()->DrawWidgetBackground(aCtx
, mFrame
,
3983 StyleAppearance::FocusOutline
, rect
,
3984 GetPaintRect(aBuilder
, aCtx
));
3988 nsCSSRendering::PaintNonThemedOutline(
3989 pc
, *aCtx
, mFrame
, GetPaintRect(aBuilder
, aCtx
), rect
, mFrame
->Style());
3992 bool nsDisplayOutline::IsThemedOutline() const {
3994 nsPresContext
* pc
= mFrame
->PresContext();
3996 pc
->Theme()->ThemeSupportsWidget(pc
, mFrame
,
3997 StyleAppearance::FocusOutline
),
3998 "All of our supported platforms have support for themed focus-outlines");
4000 return mFrame
->StyleOutline()->mOutlineStyle
.IsAuto();
4003 bool nsDisplayOutline::CreateWebRenderCommands(
4004 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4005 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4006 nsDisplayListBuilder
* aDisplayListBuilder
) {
4007 nsPresContext
* pc
= mFrame
->PresContext();
4008 nsRect rect
= GetInnerRect() + ToReferenceFrame();
4009 if (IsThemedOutline()) {
4010 rect
.Inflate(mFrame
->StyleOutline()->EffectiveOffsetFor(rect
));
4011 return pc
->Theme()->CreateWebRenderCommandsForWidget(
4012 aBuilder
, aResources
, aSc
, aManager
, mFrame
,
4013 StyleAppearance::FocusOutline
, rect
);
4017 Maybe
<nsCSSBorderRenderer
> borderRenderer
=
4018 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4019 pc
, /* aDrawTarget = */ nullptr, mFrame
,
4020 GetBounds(aDisplayListBuilder
, &dummy
), rect
, mFrame
->Style());
4022 if (!borderRenderer
) {
4023 // No border renderer means "there is no outline".
4024 // Paint nothing and return success.
4028 borderRenderer
->CreateWebRenderCommands(this, aBuilder
, aResources
, aSc
);
4032 bool nsDisplayOutline::HasRadius() const {
4033 const auto& radius
= mFrame
->StyleBorder()->mBorderRadius
;
4034 return !nsLayoutUtils::HasNonZeroCorner(radius
);
4037 bool nsDisplayOutline::IsInvisibleInRect(const nsRect
& aRect
) const {
4038 const nsStyleOutline
* outline
= mFrame
->StyleOutline();
4039 nsRect
borderBox(ToReferenceFrame(), mFrame
->GetSize());
4040 if (borderBox
.Contains(aRect
) && !HasRadius() &&
4041 outline
->mOutlineOffset
.ToCSSPixels() >= 0.0f
) {
4042 // aRect is entirely inside the border-rect, and the outline isn't rendered
4043 // inside the border-rect, so the outline is not visible.
4049 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder
* aBuilder
,
4050 const nsRect
& aRect
, HitTestState
* aState
,
4051 nsTArray
<nsIFrame
*>* aOutFrames
) {
4052 if (!RoundedBorderIntersectsRect(mFrame
, ToReferenceFrame(), aRect
)) {
4053 // aRect doesn't intersect our border-radius curve.
4057 aOutFrames
->AppendElement(mFrame
);
4060 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4061 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4062 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4063 nsDisplayListBuilder
* aDisplayListBuilder
) {
4067 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4068 return mOverrideZIndex
? *mOverrideZIndex
: nsDisplayItem::ZIndex();
4071 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex
) {
4072 mOverrideZIndex
= Some(aZIndex
);
4075 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder
* aBuilder
,
4076 nsIFrame
* aCaretFrame
)
4077 : nsPaintedDisplayItem(aBuilder
, aCaretFrame
),
4078 mCaret(aBuilder
->GetCaret()),
4079 mBounds(aBuilder
->GetCaretRect() + ToReferenceFrame()) {
4080 MOZ_COUNT_CTOR(nsDisplayCaret
);
4081 // The presence of a caret doesn't change the overflow rect
4082 // of the owning frame, so the normal building rect might not
4083 // include the caret at all. We use MarkFrameForDisplay to ensure
4084 // we build this item, and here we override the building rect
4085 // to cover the pixels we're going to draw.
4086 SetBuildingRect(mBounds
);
4089 #ifdef NS_BUILD_REFCNT_LOGGING
4090 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret
); }
4093 nsRect
nsDisplayCaret::GetBounds(nsDisplayListBuilder
* aBuilder
,
4094 bool* aSnap
) const {
4096 // The caret returns a rect in the coordinates of mFrame.
4100 void nsDisplayCaret::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
4101 // Note: Because we exist, we know that the caret is visible, so we don't
4102 // need to check for the caret's visibility.
4103 mCaret
->PaintCaret(*aCtx
->GetDrawTarget(), mFrame
, ToReferenceFrame());
4106 bool nsDisplayCaret::CreateWebRenderCommands(
4107 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4108 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4109 nsDisplayListBuilder
* aDisplayListBuilder
) {
4110 using namespace layers
;
4115 mCaret
->GetPaintGeometry(&caretRect
, &hookRect
, &caretColor
);
4116 if (NS_WARN_IF(!frame
) || NS_WARN_IF(frame
!= mFrame
)) {
4120 int32_t appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
4121 gfx::DeviceColor color
= ToDeviceColor(caretColor
);
4122 LayoutDeviceRect devCaretRect
= LayoutDeviceRect::FromAppUnits(
4123 caretRect
+ ToReferenceFrame(), appUnitsPerDevPixel
);
4124 LayoutDeviceRect devHookRect
= LayoutDeviceRect::FromAppUnits(
4125 hookRect
+ ToReferenceFrame(), appUnitsPerDevPixel
);
4127 wr::LayoutRect caret
= wr::ToLayoutRect(devCaretRect
);
4128 wr::LayoutRect hook
= wr::ToLayoutRect(devHookRect
);
4130 // Note, WR will pixel snap anything that is layout aligned.
4131 aBuilder
.PushRect(caret
, caret
, !BackfaceIsHidden(), false, false,
4132 wr::ToColorF(color
));
4134 if (!devHookRect
.IsEmpty()) {
4135 aBuilder
.PushRect(hook
, hook
, !BackfaceIsHidden(), false, false,
4136 wr::ToColorF(color
));
4141 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder
* aBuilder
,
4143 : nsPaintedDisplayItem(aBuilder
, aFrame
) {
4144 MOZ_COUNT_CTOR(nsDisplayBorder
);
4146 mBounds
= CalculateBounds
<nsRect
>(*mFrame
->StyleBorder());
4149 bool nsDisplayBorder::IsInvisibleInRect(const nsRect
& aRect
) const {
4150 nsRect paddingRect
= GetPaddingRect();
4151 const nsStyleBorder
* styleBorder
;
4152 if (paddingRect
.Contains(aRect
) &&
4153 !(styleBorder
= mFrame
->StyleBorder())->IsBorderImageSizeAvailable() &&
4154 !nsLayoutUtils::HasNonZeroCorner(styleBorder
->mBorderRadius
)) {
4155 // aRect is entirely inside the content rect, and no part
4156 // of the border is rendered inside the content rect, so we are not
4158 // Skip this if there's a border-image (which draws a background
4159 // too) or if there is a border-radius (which makes the border draw
4167 nsDisplayItemGeometry
* nsDisplayBorder::AllocateGeometry(
4168 nsDisplayListBuilder
* aBuilder
) {
4169 return new nsDisplayBorderGeometry(this, aBuilder
);
4172 void nsDisplayBorder::ComputeInvalidationRegion(
4173 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
4174 nsRegion
* aInvalidRegion
) const {
4175 const auto* geometry
= static_cast<const nsDisplayBorderGeometry
*>(aGeometry
);
4178 if (!geometry
->mBounds
.IsEqualInterior(GetBounds(aBuilder
, &snap
))) {
4179 // We can probably get away with only invalidating the difference
4180 // between the border and padding rects, but the XUL ui at least
4181 // is apparently painting a background with this?
4182 aInvalidRegion
->Or(GetBounds(aBuilder
, &snap
), geometry
->mBounds
);
4186 bool nsDisplayBorder::CreateWebRenderCommands(
4187 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4188 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4189 nsDisplayListBuilder
* aDisplayListBuilder
) {
4190 nsRect rect
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
4192 ImgDrawResult drawResult
= nsCSSRendering::CreateWebRenderCommandsForBorder(
4193 this, mFrame
, rect
, aBuilder
, aResources
, aSc
, aManager
,
4194 aDisplayListBuilder
);
4196 if (drawResult
== ImgDrawResult::NOT_SUPPORTED
) {
4202 void nsDisplayBorder::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
4203 nsPoint offset
= ToReferenceFrame();
4205 PaintBorderFlags flags
= aBuilder
->ShouldSyncDecodeImages()
4206 ? PaintBorderFlags::SyncDecodeImages
4207 : PaintBorderFlags();
4209 Unused
<< nsCSSRendering::PaintBorder(
4210 mFrame
->PresContext(), *aCtx
, mFrame
, GetPaintRect(aBuilder
, aCtx
),
4211 nsRect(offset
, mFrame
->GetSize()), mFrame
->Style(), flags
,
4212 mFrame
->GetSkipSides());
4215 nsRect
nsDisplayBorder::GetBounds(nsDisplayListBuilder
* aBuilder
,
4216 bool* aSnap
) const {
4221 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder
* aBuilder
,
4223 nsPoint offset
= ToReferenceFrame();
4224 nsRect borderRect
= mFrame
->VisualBorderRectRelativeToSelf() + offset
;
4225 nsPresContext
* presContext
= mFrame
->PresContext();
4227 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS
);
4229 nsCSSRendering::PaintBoxShadowOuter(presContext
, *aCtx
, mFrame
, borderRect
,
4230 GetPaintRect(aBuilder
, aCtx
), 1.0f
);
4233 nsRect
nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder
* aBuilder
,
4234 bool* aSnap
) const {
4239 nsRect
nsDisplayBoxShadowOuter::GetBoundsInternal() {
4240 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame
, mFrame
->GetSize()) +
4244 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect
& aRect
) const {
4245 nsPoint origin
= ToReferenceFrame();
4246 nsRect
frameRect(origin
, mFrame
->GetSize());
4247 if (!frameRect
.Contains(aRect
)) {
4251 // the visible region is entirely inside the border-rect, and box shadows
4252 // never render within the border-rect (unless there's a border radius).
4253 nscoord twipsRadii
[8];
4254 bool hasBorderRadii
= mFrame
->GetBorderRadii(twipsRadii
);
4255 if (!hasBorderRadii
) {
4259 return RoundedRectContainsRect(frameRect
, twipsRadii
, aRect
);
4262 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4263 auto shadows
= mFrame
->StyleEffects()->mBoxShadow
.AsSpan();
4264 if (shadows
.IsEmpty()) {
4268 bool hasBorderRadius
;
4269 // We don't support native themed things yet like box shadows around
4272 // TODO(emilio): The non-native theme could provide the right rect+radius
4273 // instead relatively painlessly, if we find this causes performance issues or
4275 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame
, hasBorderRadius
);
4278 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4279 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4280 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4281 nsDisplayListBuilder
* aDisplayListBuilder
) {
4282 if (!CanBuildWebRenderDisplayItems()) {
4286 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
4287 nsPoint offset
= ToReferenceFrame();
4288 nsRect borderRect
= mFrame
->VisualBorderRectRelativeToSelf() + offset
;
4290 nsRect bounds
= GetBounds(aDisplayListBuilder
, &snap
);
4292 bool hasBorderRadius
;
4294 nsCSSRendering::HasBoxShadowNativeTheme(mFrame
, hasBorderRadius
);
4296 // Don't need the full size of the shadow rect like we do in
4297 // nsCSSRendering since WR takes care of calculations for blur
4298 // and spread radius.
4300 nsCSSRendering::GetShadowRect(borderRect
, nativeTheme
, mFrame
);
4302 RectCornerRadii borderRadii
;
4303 if (hasBorderRadius
) {
4304 hasBorderRadius
= nsCSSRendering::GetBorderRadii(frameRect
, borderRect
,
4305 mFrame
, borderRadii
);
4308 // Everything here is in app units, change to device units.
4309 LayoutDeviceRect clipRect
=
4310 LayoutDeviceRect::FromAppUnits(bounds
, appUnitsPerDevPixel
);
4311 auto shadows
= mFrame
->StyleEffects()->mBoxShadow
.AsSpan();
4312 MOZ_ASSERT(!shadows
.IsEmpty());
4314 for (const auto& shadow
: Reversed(shadows
)) {
4320 float(shadow
.base
.blur
.ToAppUnits()) / float(appUnitsPerDevPixel
);
4321 gfx::sRGBColor shadowColor
= nsCSSRendering::GetShadowColor(
4322 shadow
.base
, mFrame
, aBuilder
.GetInheritedOpacity());
4324 // We don't move the shadow rect here since WR does it for us
4325 // Now translate everything to device pixels.
4326 const nsRect
& shadowRect
= frameRect
;
4327 LayoutDevicePoint shadowOffset
= LayoutDevicePoint::FromAppUnits(
4328 nsPoint(shadow
.base
.horizontal
.ToAppUnits(),
4329 shadow
.base
.vertical
.ToAppUnits()),
4330 appUnitsPerDevPixel
);
4332 LayoutDeviceRect deviceBox
=
4333 LayoutDeviceRect::FromAppUnits(shadowRect
, appUnitsPerDevPixel
);
4334 wr::LayoutRect deviceBoxRect
= wr::ToLayoutRect(deviceBox
);
4335 wr::LayoutRect deviceClipRect
= wr::ToLayoutRect(clipRect
);
4337 LayoutDeviceSize zeroSize
;
4338 wr::BorderRadius borderRadius
=
4339 wr::ToBorderRadius(zeroSize
, zeroSize
, zeroSize
, zeroSize
);
4340 if (hasBorderRadius
) {
4341 borderRadius
= wr::ToBorderRadius(
4342 LayoutDeviceSize::FromUnknownSize(borderRadii
.TopLeft()),
4343 LayoutDeviceSize::FromUnknownSize(borderRadii
.TopRight()),
4344 LayoutDeviceSize::FromUnknownSize(borderRadii
.BottomLeft()),
4345 LayoutDeviceSize::FromUnknownSize(borderRadii
.BottomRight()));
4348 float spreadRadius
=
4349 float(shadow
.spread
.ToAppUnits()) / float(appUnitsPerDevPixel
);
4351 aBuilder
.PushBoxShadow(deviceBoxRect
, deviceClipRect
, !BackfaceIsHidden(),
4352 deviceBoxRect
, wr::ToLayoutVector2D(shadowOffset
),
4353 wr::ToColorF(ToDeviceColor(shadowColor
)), blurRadius
,
4354 spreadRadius
, borderRadius
,
4355 wr::BoxShadowClipMode::Outset
);
4361 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4362 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
4363 nsRegion
* aInvalidRegion
) const {
4364 const auto* geometry
=
4365 static_cast<const nsDisplayItemGenericGeometry
*>(aGeometry
);
4367 if (!geometry
->mBounds
.IsEqualInterior(GetBounds(aBuilder
, &snap
)) ||
4368 !geometry
->mBorderRect
.IsEqualInterior(GetBorderRect())) {
4369 nsRegion oldShadow
, newShadow
;
4370 nscoord dontCare
[8];
4371 bool hasBorderRadius
= mFrame
->GetBorderRadii(dontCare
);
4372 if (hasBorderRadius
) {
4373 // If we have rounded corners then we need to invalidate the frame area
4374 // too since we paint into it.
4375 oldShadow
= geometry
->mBounds
;
4376 newShadow
= GetBounds(aBuilder
, &snap
);
4378 oldShadow
.Sub(geometry
->mBounds
, geometry
->mBorderRect
);
4379 newShadow
.Sub(GetBounds(aBuilder
, &snap
), GetBorderRect());
4381 aInvalidRegion
->Or(oldShadow
, newShadow
);
4385 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder
* aBuilder
,
4387 nsPoint offset
= ToReferenceFrame();
4388 nsRect borderRect
= nsRect(offset
, mFrame
->GetSize());
4389 nsPresContext
* presContext
= mFrame
->PresContext();
4391 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS
);
4393 nsCSSRendering::PaintBoxShadowInner(presContext
, *aCtx
, mFrame
, borderRect
);
4396 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4397 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
4398 const nsPoint
& aReferenceOffset
) {
4399 auto shadows
= aFrame
->StyleEffects()->mBoxShadow
.AsSpan();
4400 if (shadows
.IsEmpty()) {
4401 // Means we don't have to paint anything
4405 bool hasBorderRadius
;
4407 nsCSSRendering::HasBoxShadowNativeTheme(aFrame
, hasBorderRadius
);
4409 // We don't support native themed things yet like box shadows around
4411 return !nativeTheme
;
4415 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4416 wr::DisplayListBuilder
& aBuilder
, const StackingContextHelper
& aSc
,
4417 nsRect
& aVisibleRect
, nsIFrame
* aFrame
, const nsRect
& aBorderRect
) {
4418 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame
)) {
4422 int32_t appUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
4424 auto shadows
= aFrame
->StyleEffects()->mBoxShadow
.AsSpan();
4426 LayoutDeviceRect clipRect
=
4427 LayoutDeviceRect::FromAppUnits(aVisibleRect
, appUnitsPerDevPixel
);
4429 for (const auto& shadow
: Reversed(shadows
)) {
4430 if (!shadow
.inset
) {
4435 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame
, aBorderRect
);
4436 RectCornerRadii innerRadii
;
4437 nsCSSRendering::GetShadowInnerRadii(aFrame
, aBorderRect
, innerRadii
);
4439 // Now translate everything to device pixels.
4440 LayoutDeviceRect deviceBoxRect
=
4441 LayoutDeviceRect::FromAppUnits(shadowRect
, appUnitsPerDevPixel
);
4442 wr::LayoutRect deviceClipRect
= wr::ToLayoutRect(clipRect
);
4443 sRGBColor shadowColor
=
4444 nsCSSRendering::GetShadowColor(shadow
.base
, aFrame
, 1.0);
4446 LayoutDevicePoint shadowOffset
= LayoutDevicePoint::FromAppUnits(
4447 nsPoint(shadow
.base
.horizontal
.ToAppUnits(),
4448 shadow
.base
.vertical
.ToAppUnits()),
4449 appUnitsPerDevPixel
);
4452 float(shadow
.base
.blur
.ToAppUnits()) / float(appUnitsPerDevPixel
);
4454 wr::BorderRadius borderRadius
= wr::ToBorderRadius(
4455 LayoutDeviceSize::FromUnknownSize(innerRadii
.TopLeft()),
4456 LayoutDeviceSize::FromUnknownSize(innerRadii
.TopRight()),
4457 LayoutDeviceSize::FromUnknownSize(innerRadii
.BottomLeft()),
4458 LayoutDeviceSize::FromUnknownSize(innerRadii
.BottomRight()));
4459 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4460 float spreadRadius
=
4461 float(shadow
.spread
.ToAppUnits()) / float(appUnitsPerDevPixel
);
4463 aBuilder
.PushBoxShadow(
4464 wr::ToLayoutRect(deviceBoxRect
), deviceClipRect
,
4465 !aFrame
->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect
),
4466 wr::ToLayoutVector2D(shadowOffset
),
4467 wr::ToColorF(ToDeviceColor(shadowColor
)), blurRadius
, spreadRadius
,
4468 borderRadius
, wr::BoxShadowClipMode::Inset
);
4472 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4473 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4474 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4475 nsDisplayListBuilder
* aDisplayListBuilder
) {
4476 if (!CanCreateWebRenderCommands(aDisplayListBuilder
, mFrame
,
4477 ToReferenceFrame())) {
4482 nsRect visible
= GetBounds(aDisplayListBuilder
, &snap
);
4483 nsPoint offset
= ToReferenceFrame();
4484 nsRect borderRect
= nsRect(offset
, mFrame
->GetSize());
4485 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4486 aBuilder
, aSc
, visible
, mFrame
, borderRect
);
4491 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder
* aBuilder
,
4492 nsIFrame
* aFrame
, nsDisplayList
* aList
)
4493 : nsDisplayWrapList(aBuilder
, aFrame
, aList
,
4494 aBuilder
->CurrentActiveScrolledRoot(), false) {}
4496 nsDisplayWrapList::nsDisplayWrapList(
4497 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
4498 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aClearClipChain
)
4499 : nsPaintedDisplayItem(aBuilder
, aFrame
, aActiveScrolledRoot
),
4501 mFrameActiveScrolledRoot(aBuilder
->CurrentActiveScrolledRoot()),
4503 mHasZIndexOverride(false),
4504 mClearingClipChain(aClearClipChain
) {
4505 MOZ_COUNT_CTOR(nsDisplayWrapList
);
4507 mBaseBuildingRect
= GetBuildingRect();
4510 mListPtr
->AppendToTop(aList
);
4511 mOriginalClipChain
= mClipChain
;
4512 nsDisplayWrapList::UpdateBounds(aBuilder
);
4515 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder
* aBuilder
,
4516 nsIFrame
* aFrame
, nsDisplayItem
* aItem
)
4517 : nsPaintedDisplayItem(aBuilder
, aFrame
,
4518 aBuilder
->CurrentActiveScrolledRoot()),
4521 mHasZIndexOverride(false) {
4522 MOZ_COUNT_CTOR(nsDisplayWrapList
);
4524 mBaseBuildingRect
= GetBuildingRect();
4527 mListPtr
->AppendToTop(aItem
);
4528 mOriginalClipChain
= mClipChain
;
4529 nsDisplayWrapList::UpdateBounds(aBuilder
);
4531 if (!aFrame
|| !aFrame
->IsTransformed()) {
4535 // See the previous nsDisplayWrapList constructor
4536 if (aItem
->Frame() == aFrame
) {
4537 mToReferenceFrame
= aItem
->ToReferenceFrame();
4540 nsRect visible
= aBuilder
->GetVisibleRect() +
4541 aBuilder
->GetCurrentFrameOffsetToReferenceFrame();
4543 SetBuildingRect(visible
);
4546 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList
); }
4548 void nsDisplayWrapList::HitTest(nsDisplayListBuilder
* aBuilder
,
4549 const nsRect
& aRect
, HitTestState
* aState
,
4550 nsTArray
<nsIFrame
*>* aOutFrames
) {
4551 mListPtr
->HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
4554 nsRect
nsDisplayWrapList::GetBounds(nsDisplayListBuilder
* aBuilder
,
4555 bool* aSnap
) const {
4560 nsRegion
nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
4561 bool* aSnap
) const {
4564 return ::mozilla::GetOpaqueRegion(aBuilder
, GetChildren(),
4565 GetBounds(aBuilder
, &snap
));
4568 Maybe
<nscolor
> nsDisplayWrapList::IsUniform(
4569 nsDisplayListBuilder
* aBuilder
) const {
4570 // We could try to do something but let's conservatively just return Nothing.
4574 void nsDisplayWrapper::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
4575 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4578 nsRect
nsDisplayWrapList::GetComponentAlphaBounds(
4579 nsDisplayListBuilder
* aBuilder
) const {
4580 return mListPtr
->GetComponentAlphaBounds(aBuilder
);
4583 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4584 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4585 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4586 nsDisplayListBuilder
* aDisplayListBuilder
, bool aNewClipList
) {
4587 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4588 GetChildren(), this, aDisplayListBuilder
, aSc
, aBuilder
, aResources
,
4593 static nsresult
WrapDisplayList(nsDisplayListBuilder
* aBuilder
,
4594 nsIFrame
* aFrame
, nsDisplayList
* aList
,
4595 nsDisplayItemWrapper
* aWrapper
) {
4596 if (!aList
->GetTop()) {
4599 nsDisplayItem
* item
= aWrapper
->WrapList(aBuilder
, aFrame
, aList
);
4601 return NS_ERROR_OUT_OF_MEMORY
;
4603 // aList was emptied
4604 aList
->AppendToTop(item
);
4608 static nsresult
WrapEachDisplayItem(nsDisplayListBuilder
* aBuilder
,
4609 nsDisplayList
* aList
,
4610 nsDisplayItemWrapper
* aWrapper
) {
4611 for (nsDisplayItem
* item
: aList
->TakeItems()) {
4612 item
= aWrapper
->WrapItem(aBuilder
, item
);
4614 return NS_ERROR_OUT_OF_MEMORY
;
4616 aList
->AppendToTop(item
);
4618 // aList was emptied
4622 nsresult
nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder
* aBuilder
,
4624 const nsDisplayListSet
& aIn
,
4625 const nsDisplayListSet
& aOut
) {
4626 nsresult rv
= WrapListsInPlace(aBuilder
, aFrame
, aIn
);
4627 NS_ENSURE_SUCCESS(rv
, rv
);
4629 if (&aOut
== &aIn
) {
4632 aOut
.BorderBackground()->AppendToTop(aIn
.BorderBackground());
4633 aOut
.BlockBorderBackgrounds()->AppendToTop(aIn
.BlockBorderBackgrounds());
4634 aOut
.Floats()->AppendToTop(aIn
.Floats());
4635 aOut
.Content()->AppendToTop(aIn
.Content());
4636 aOut
.PositionedDescendants()->AppendToTop(aIn
.PositionedDescendants());
4637 aOut
.Outlines()->AppendToTop(aIn
.Outlines());
4641 nsresult
nsDisplayItemWrapper::WrapListsInPlace(
4642 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
4643 const nsDisplayListSet
& aLists
) {
4645 if (WrapBorderBackground()) {
4646 // Our border-backgrounds are in-flow
4647 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.BorderBackground(), this);
4648 NS_ENSURE_SUCCESS(rv
, rv
);
4650 // Our block border-backgrounds are in-flow
4651 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.BlockBorderBackgrounds(), this);
4652 NS_ENSURE_SUCCESS(rv
, rv
);
4653 // The floats are not in flow
4654 rv
= WrapEachDisplayItem(aBuilder
, aLists
.Floats(), this);
4655 NS_ENSURE_SUCCESS(rv
, rv
);
4656 // Our child content is in flow
4657 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.Content(), this);
4658 NS_ENSURE_SUCCESS(rv
, rv
);
4659 // The positioned descendants may not be in-flow
4660 rv
= WrapEachDisplayItem(aBuilder
, aLists
.PositionedDescendants(), this);
4661 NS_ENSURE_SUCCESS(rv
, rv
);
4662 // The outlines may not be in-flow
4663 return WrapEachDisplayItem(aBuilder
, aLists
.Outlines(), this);
4666 nsDisplayOpacity::nsDisplayOpacity(
4667 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
4668 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aForEventsOnly
,
4669 bool aNeedsActiveLayer
, bool aWrapsBackdropFilter
)
4670 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
4671 mOpacity(aFrame
->StyleEffects()->mOpacity
),
4672 mForEventsOnly(aForEventsOnly
),
4673 mNeedsActiveLayer(aNeedsActiveLayer
),
4674 mChildOpacityState(ChildOpacityState::Unknown
),
4675 mWrapsBackdropFilter(aWrapsBackdropFilter
) {
4676 MOZ_COUNT_CTOR(nsDisplayOpacity
);
4679 void nsDisplayOpacity::HitTest(nsDisplayListBuilder
* aBuilder
,
4680 const nsRect
& aRect
,
4681 nsDisplayItem::HitTestState
* aState
,
4682 nsTArray
<nsIFrame
*>* aOutFrames
) {
4683 AutoRestore
<float> opacity(aState
->mCurrentOpacity
);
4684 aState
->mCurrentOpacity
*= mOpacity
;
4686 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4687 // only consider fully opaque items? Or make this configurable somehow?
4688 if (aBuilder
->HitTestIsForVisibility() && mOpacity
== 0.0f
) {
4691 nsDisplayWrapList::HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
4694 nsRegion
nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
4695 bool* aSnap
) const {
4697 // The only time where mOpacity == 1.0 should be when we have will-change.
4698 // We could report this as opaque then but when the will-change value starts
4699 // animating the element would become non opaque and could cause repaints.
4703 void nsDisplayOpacity::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
4704 if (GetOpacity() == 0.0f
) {
4708 if (GetOpacity() == 1.0f
) {
4709 GetChildren()->Paint(aBuilder
, aCtx
,
4710 mFrame
->PresContext()->AppUnitsPerDevPixel());
4714 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4716 aCtx
->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4717 GetChildren()->Paint(aBuilder
, aCtx
,
4718 mFrame
->PresContext()->AppUnitsPerDevPixel());
4719 aCtx
->GetDrawTarget()->PopLayer();
4723 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder
* aBuilder
,
4725 return EffectCompositor::HasAnimationsForCompositor(
4726 aFrame
, DisplayItemType::TYPE_OPACITY
) ||
4727 (ActiveLayerTracker::IsStyleAnimated(
4728 aBuilder
, aFrame
, nsCSSPropertyIDSet::OpacityProperties()));
4731 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager
* aManager
,
4732 nsDisplayListBuilder
* aBuilder
) const {
4733 return !EffectCompositor::HasAnimationsForCompositor(
4734 mFrame
, DisplayItemType::TYPE_OPACITY
);
4737 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4738 // children that don't overlap and can all apply the opacity to themselves.
4739 static const size_t kOpacityMaxChildCount
= 3;
4741 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4742 // are likely have more child items than |kOpacityMaxChildCount|.
4743 static const size_t kOpacityMaxListSize
= kOpacityMaxChildCount
* 2;
4746 * Recursively iterates through |aList| and collects at most
4747 * |kOpacityMaxChildCount| display item pointers to items that return true for
4748 * CanApplyOpacity(). The item pointers are added to |aArray|.
4750 * LayerEventRegions and WrapList items are ignored.
4752 * We need to do this recursively, because the child display items might contain
4753 * nested nsDisplayWrapLists.
4755 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4756 * item that returns false for CanApplyOpacity() is encountered.
4757 * Otherwise returns true.
4759 static bool CollectItemsWithOpacity(WebRenderLayerManager
* aManager
,
4760 nsDisplayListBuilder
* aBuilder
,
4761 nsDisplayList
* aList
,
4762 nsTArray
<nsPaintedDisplayItem
*>& aArray
) {
4763 if (aList
->Length() > kOpacityMaxListSize
) {
4764 // Exit early, since |aList| will likely contain more than
4765 // |kOpacityMaxChildCount| items.
4769 for (nsDisplayItem
* i
: *aList
) {
4770 const DisplayItemType type
= i
->GetType();
4772 if (type
== DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
) {
4776 // Descend only into wraplists.
4777 if (type
== DisplayItemType::TYPE_WRAP_LIST
||
4778 type
== DisplayItemType::TYPE_CONTAINER
) {
4779 // The current display item has children, process them first.
4780 if (!CollectItemsWithOpacity(aManager
, aBuilder
, i
->GetChildren(),
4788 if (aArray
.Length() == kOpacityMaxChildCount
) {
4792 auto* item
= i
->AsPaintedDisplayItem();
4793 if (!item
|| !item
->CanApplyOpacity(aManager
, aBuilder
)) {
4797 aArray
.AppendElement(item
);
4803 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager
* aManager
,
4804 nsDisplayListBuilder
* aBuilder
) {
4805 if (mChildOpacityState
== ChildOpacityState::Deferred
) {
4809 // Iterate through the child display list and copy at most
4810 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4811 AutoTArray
<nsPaintedDisplayItem
*, kOpacityMaxChildCount
> items
;
4812 if (!CollectItemsWithOpacity(aManager
, aBuilder
, &mList
, items
)) {
4813 mChildOpacityState
= ChildOpacityState::Deferred
;
4818 nsPaintedDisplayItem
* item
{};
4820 } children
[kOpacityMaxChildCount
];
4823 size_t childCount
= 0;
4824 for (nsPaintedDisplayItem
* item
: items
) {
4825 children
[childCount
].item
= item
;
4826 children
[childCount
].bounds
= item
->GetBounds(aBuilder
, &snap
);
4830 for (size_t i
= 0; i
< childCount
; i
++) {
4831 for (size_t j
= i
+ 1; j
< childCount
; j
++) {
4832 if (children
[i
].bounds
.Intersects(children
[j
].bounds
)) {
4833 mChildOpacityState
= ChildOpacityState::Deferred
;
4839 mChildOpacityState
= ChildOpacityState::Applied
;
4844 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4845 * that has the same frame as the opacity item, and that supports painting with
4846 * opacity. In this case the opacity item can be optimized away.
4848 bool nsDisplayOpacity::ApplyToMask() {
4849 if (mList
.Length() != 1) {
4853 nsDisplayItem
* item
= mList
.GetBottom();
4854 if (item
->Frame() != mFrame
) {
4855 // The effect item needs to have the same frame as the opacity item.
4859 const DisplayItemType type
= item
->GetType();
4860 if (type
== DisplayItemType::TYPE_MASK
) {
4867 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4868 WebRenderLayerManager
* aManager
, nsDisplayListBuilder
* aBuilder
,
4869 float aInheritedOpacity
) {
4870 if (mFrame
->GetPrevContinuation() || mFrame
->GetNextContinuation() ||
4871 mFrame
->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT
)) {
4872 // If we've been split, then we might need to merge, so
4873 // don't flatten us away.
4877 if (mNeedsActiveLayer
|| mOpacity
== 0.0) {
4878 // If our opacity is zero then we'll discard all descendant display items
4879 // except for layer event regions, so there's no point in doing this
4880 // optimization (and if we do do it, then invalidations of those descendants
4881 // might trigger repainting).
4885 if (mList
.IsEmpty()) {
4889 // We can only flatten opacity items into a mask if we haven't
4890 // already flattened an earlier ancestor, since the SVG code pulls the opacity
4891 // from style directly, and won't know about the outer opacity value.
4892 if (aInheritedOpacity
== 1.0f
&& ApplyToMask()) {
4893 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame
));
4894 mChildOpacityState
= ChildOpacityState::Applied
;
4898 // Return true if we successfully applied opacity to child items.
4899 return CanApplyToChildren(aManager
, aBuilder
);
4902 void nsDisplayOpacity::ComputeInvalidationRegion(
4903 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
4904 nsRegion
* aInvalidRegion
) const {
4905 const auto* geometry
=
4906 static_cast<const nsDisplayOpacityGeometry
*>(aGeometry
);
4909 if (mOpacity
!= geometry
->mOpacity
) {
4910 aInvalidRegion
->Or(GetBounds(aBuilder
, &snap
), geometry
->mBounds
);
4914 void nsDisplayOpacity::WriteDebugInfo(std::stringstream
& aStream
) {
4915 aStream
<< " (opacity " << mOpacity
<< ", mChildOpacityState: ";
4916 switch (mChildOpacityState
) {
4917 case ChildOpacityState::Unknown
:
4918 aStream
<< "Unknown";
4920 case ChildOpacityState::Applied
:
4921 aStream
<< "Applied";
4923 case ChildOpacityState::Deferred
:
4924 aStream
<< "Deferred";
4933 bool nsDisplayOpacity::CreateWebRenderCommands(
4934 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4935 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4936 nsDisplayListBuilder
* aDisplayListBuilder
) {
4937 MOZ_ASSERT(mChildOpacityState
!= ChildOpacityState::Applied
);
4938 float oldOpacity
= aBuilder
.GetInheritedOpacity();
4939 const DisplayItemClipChain
* oldClipChain
= aBuilder
.GetInheritedClipChain();
4940 aBuilder
.SetInheritedOpacity(1.0f
);
4941 aBuilder
.SetInheritedClipChain(nullptr);
4942 float opacity
= mOpacity
* oldOpacity
;
4943 float* opacityForSC
= &opacity
;
4945 uint64_t animationsId
=
4946 AddAnimationsForWebRender(this, aManager
, aDisplayListBuilder
);
4947 wr::WrAnimationProperty prop
{
4948 wr::WrAnimationType::Opacity
,
4952 wr::StackingContextParams params
;
4953 params
.animation
= animationsId
? &prop
: nullptr;
4954 params
.opacity
= opacityForSC
;
4956 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
4957 if (mWrapsBackdropFilter
) {
4958 params
.flags
|= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER
;
4960 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
4963 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4964 &mList
, this, aDisplayListBuilder
, sc
, aBuilder
, aResources
);
4965 aBuilder
.SetInheritedOpacity(oldOpacity
);
4966 aBuilder
.SetInheritedClipChain(oldClipChain
);
4970 nsDisplayBlendMode::nsDisplayBlendMode(
4971 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
4972 StyleBlend aBlendMode
, const ActiveScrolledRoot
* aActiveScrolledRoot
,
4973 const bool aIsForBackground
)
4974 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
4975 mBlendMode(aBlendMode
),
4976 mIsForBackground(aIsForBackground
) {
4977 MOZ_COUNT_CTOR(nsDisplayBlendMode
);
4980 nsRegion
nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
4981 bool* aSnap
) const {
4983 // We are never considered opaque
4987 bool nsDisplayBlendMode::CreateWebRenderCommands(
4988 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
4989 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
4990 nsDisplayListBuilder
* aDisplayListBuilder
) {
4991 wr::StackingContextParams params
;
4992 params
.mix_blend_mode
=
4993 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode
));
4995 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
4996 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
4999 return nsDisplayWrapList::CreateWebRenderCommands(
5000 aBuilder
, aResources
, sc
, aManager
, aDisplayListBuilder
);
5003 void nsDisplayBlendMode::Paint(nsDisplayListBuilder
* aBuilder
,
5005 // This should be switched to use PushLayerWithBlend, once it's
5006 // been implemented for all DrawTarget backends.
5007 DrawTarget
* dt
= aCtx
->GetDrawTarget();
5008 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
5009 Rect rect
= NSRectToRect(GetPaintRect(aBuilder
, aCtx
), appUnitsPerDevPixel
);
5012 // Create a temporary DrawTarget that is clipped to the area that
5013 // we're going to draw to. This will include the same transform as
5014 // is currently on |dt|.
5015 RefPtr
<DrawTarget
> temp
=
5016 dt
->CreateClippedDrawTarget(rect
, SurfaceFormat::B8G8R8A8
);
5021 gfxContext
ctx(temp
, /* aPreserveTransform */ true);
5023 GetChildren()->Paint(aBuilder
, &ctx
,
5024 mFrame
->PresContext()->AppUnitsPerDevPixel());
5026 // Draw the temporary DT to the real destination, applying the blend mode, but
5029 RefPtr
<SourceSurface
> surface
= temp
->Snapshot();
5030 gfxContextMatrixAutoSaveRestore
saveMatrix(aCtx
);
5031 dt
->SetTransform(Matrix());
5033 surface
, Rect(surface
->GetRect()), Rect(surface
->GetRect()),
5034 DrawSurfaceOptions(),
5035 DrawOptions(1.0f
, nsCSSRendering::GetGFXBlendMode(mBlendMode
)));
5038 gfx::CompositionOp
nsDisplayBlendMode::BlendMode() {
5039 return nsCSSRendering::GetGFXBlendMode(mBlendMode
);
5042 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem
* aItem
) const {
5043 // Items for the same content element should be merged into a single
5044 // compositing group.
5045 if (!HasDifferentFrame(aItem
) || !HasSameTypeAndClip(aItem
) ||
5046 !HasSameContent(aItem
)) {
5050 const auto* item
= static_cast<const nsDisplayBlendMode
*>(aItem
);
5051 if (mIsForBackground
|| item
->mIsForBackground
) {
5052 // Don't merge background-blend-mode items
5060 nsDisplayBlendContainer
* nsDisplayBlendContainer::CreateForMixBlendMode(
5061 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5062 const ActiveScrolledRoot
* aActiveScrolledRoot
) {
5063 return MakeDisplayItem
<nsDisplayBlendContainer
>(aBuilder
, aFrame
, aList
,
5064 aActiveScrolledRoot
, false);
5068 nsDisplayBlendContainer
* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5069 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
5070 nsDisplayList
* aList
, const ActiveScrolledRoot
* aActiveScrolledRoot
) {
5071 if (aSecondaryFrame
) {
5072 auto type
= GetTableTypeFromFrame(aFrame
);
5073 auto index
= static_cast<uint16_t>(type
);
5075 return MakeDisplayItemWithIndex
<nsDisplayTableBlendContainer
>(
5076 aBuilder
, aSecondaryFrame
, index
, aList
, aActiveScrolledRoot
, true,
5080 return MakeDisplayItemWithIndex
<nsDisplayBlendContainer
>(
5081 aBuilder
, aFrame
, 1, aList
, aActiveScrolledRoot
, true);
5084 nsDisplayBlendContainer::nsDisplayBlendContainer(
5085 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5086 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aIsForBackground
)
5087 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
5088 mIsForBackground(aIsForBackground
) {
5089 MOZ_COUNT_CTOR(nsDisplayBlendContainer
);
5092 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder
* aBuilder
,
5094 aCtx
->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5095 GetChildren()->Paint(aBuilder
, aCtx
,
5096 mFrame
->PresContext()->AppUnitsPerDevPixel());
5097 aCtx
->GetDrawTarget()->PopLayer();
5100 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5101 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5102 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5103 nsDisplayListBuilder
* aDisplayListBuilder
) {
5104 wr::StackingContextParams params
;
5105 params
.flags
|= wr::StackingContextFlags::IS_BLEND_CONTAINER
;
5107 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
5108 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
5111 return nsDisplayWrapList::CreateWebRenderCommands(
5112 aBuilder
, aResources
, sc
, aManager
, aDisplayListBuilder
);
5115 nsDisplayOwnLayer::nsDisplayOwnLayer(
5116 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5117 const ActiveScrolledRoot
* aActiveScrolledRoot
,
5118 nsDisplayOwnLayerFlags aFlags
, const ScrollbarData
& aScrollbarData
,
5119 bool aForceActive
, bool aClearClipChain
)
5120 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
,
5123 mScrollbarData(aScrollbarData
),
5124 mForceActive(aForceActive
),
5126 MOZ_COUNT_CTOR(nsDisplayOwnLayer
);
5129 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5130 return mScrollbarData
.mScrollbarLayerType
== ScrollbarLayerType::Thumb
;
5133 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5134 return mScrollbarData
.mScrollbarLayerType
== ScrollbarLayerType::Container
;
5137 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5138 return IsScrollbarContainer() && IsScrollbarLayerForRoot();
5141 bool nsDisplayOwnLayer::IsScrollbarLayerForRoot() const {
5142 return mFrame
->PresContext()->IsRootContentDocumentCrossProcess() &&
5143 mScrollbarData
.mTargetViewId
==
5144 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame
->PresContext());
5147 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5148 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM
;
5151 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5152 return GetType() == DisplayItemType::TYPE_FIXED_POSITION
||
5153 GetType() == DisplayItemType::TYPE_TABLE_FIXED_POSITION
;
5156 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5157 return GetType() == DisplayItemType::TYPE_STICKY_POSITION
;
5160 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5161 if (!mFrame
->PresContext()->IsRootContentDocumentCrossProcess()) {
5164 return mFrame
->PresContext()->HasDynamicToolbar() ||
5165 // For tests on Android, this pref is set to simulate the dynamic
5167 StaticPrefs::apz_fixed_margin_override_enabled();
5170 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5171 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5172 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5173 nsDisplayListBuilder
* aDisplayListBuilder
) {
5174 Maybe
<wr::WrAnimationProperty
> prop
;
5175 bool needsProp
= aManager
->LayerManager()->AsyncPanZoomEnabled() &&
5176 (IsScrollThumbLayer() || IsZoomingLayer() ||
5177 ShouldGetFixedOrStickyAnimationId() ||
5178 (IsRootScrollbarContainer() && HasDynamicToolbar()));
5181 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5182 // to create and set an animation id. That way APZ can adjust the position/
5183 // zoom of this content asynchronously as needed.
5184 RefPtr
<WebRenderAPZAnimationData
> animationData
=
5185 aManager
->CommandBuilder()
5186 .CreateOrRecycleWebRenderUserData
<WebRenderAPZAnimationData
>(this);
5187 mWrAnimationId
= animationData
->GetAnimationId();
5190 prop
->id
= mWrAnimationId
;
5191 prop
->key
= wr::SpatialKey(uint64_t(mFrame
), GetPerFrameKey(),
5192 wr::SpatialKeyKind::APZ
);
5193 prop
->effect_type
= wr::WrAnimationType::Transform
;
5196 wr::StackingContextParams params
;
5197 params
.animation
= prop
.ptrOr(nullptr);
5199 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
5200 if (IsScrollbarContainer() && IsRootScrollbarContainer()) {
5201 params
.prim_flags
|= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER
;
5203 if (IsZoomingLayer() ||
5204 (ShouldGetFixedOrStickyAnimationId() ||
5205 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5206 params
.is_2d_scale_translation
= true;
5207 params
.should_snap
= true;
5210 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
5213 nsDisplayWrapList::CreateWebRenderCommands(aBuilder
, aResources
, sc
, aManager
,
5214 aDisplayListBuilder
);
5218 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData
* aData
,
5219 WebRenderLayerScrollData
* aLayerData
) {
5220 bool isRelevantToApz
=
5221 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5222 ShouldGetFixedOrStickyAnimationId());
5224 if (!isRelevantToApz
) {
5232 if (IsZoomingLayer()) {
5233 aLayerData
->SetZoomAnimationId(mWrAnimationId
);
5237 if (IsFixedPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5238 aLayerData
->SetFixedPositionAnimationId(mWrAnimationId
);
5242 if (IsStickyPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5243 aLayerData
->SetStickyPositionAnimationId(mWrAnimationId
);
5247 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5249 aLayerData
->SetScrollbarData(mScrollbarData
);
5251 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5252 aLayerData
->SetScrollbarAnimationId(mWrAnimationId
);
5256 if (IsScrollThumbLayer()) {
5257 aLayerData
->SetScrollbarAnimationId(mWrAnimationId
);
5258 LayoutDeviceRect bounds
= LayoutDeviceIntRect::FromAppUnits(
5259 mBounds
, mFrame
->PresContext()->AppUnitsPerDevPixel());
5260 // Subframe scrollbars are subject to the pinch-zoom scale,
5261 // but root scrollbars are not because they are outside of the
5262 // region that is zoomed.
5263 const float resolution
=
5264 IsScrollbarLayerForRoot()
5266 : mFrame
->PresShell()->GetCumulativeResolution();
5267 LayerIntRect layerBounds
=
5268 RoundedOut(bounds
* LayoutDeviceToLayerScale(resolution
));
5269 aLayerData
->SetVisibleRect(layerBounds
);
5274 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream
& aStream
) {
5275 aStream
<< nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64
")",
5276 (int)mFlags
, mScrollbarData
.mTargetViewId
)
5280 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder
* aBuilder
,
5282 nsSubDocumentFrame
* aSubDocFrame
,
5283 nsDisplayList
* aList
,
5284 nsDisplayOwnLayerFlags aFlags
)
5285 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
,
5286 aBuilder
->CurrentActiveScrolledRoot(), aFlags
),
5287 mScrollParentId(aBuilder
->GetCurrentScrollParentId()),
5288 mShouldFlatten(false),
5289 mSubDocFrame(aSubDocFrame
) {
5290 MOZ_COUNT_CTOR(nsDisplaySubDocument
);
5292 if (mSubDocFrame
&& mSubDocFrame
!= mFrame
) {
5293 mSubDocFrame
->AddDisplayItem(this);
5297 nsDisplaySubDocument::~nsDisplaySubDocument() {
5298 MOZ_COUNT_DTOR(nsDisplaySubDocument
);
5300 mSubDocFrame
->RemoveDisplayItem(this);
5304 nsIFrame
* nsDisplaySubDocument::FrameForInvalidation() const {
5305 return mSubDocFrame
? mSubDocFrame
: mFrame
;
5308 void nsDisplaySubDocument::RemoveFrame(nsIFrame
* aFrame
) {
5309 if (aFrame
== mSubDocFrame
) {
5310 mSubDocFrame
= nullptr;
5313 nsDisplayOwnLayer::RemoveFrame(aFrame
);
5316 static bool UseDisplayPortForViewport(nsDisplayListBuilder
* aBuilder
,
5318 return aBuilder
->IsPaintingToWindow() &&
5319 DisplayPortUtils::ViewportHasDisplayPort(aFrame
->PresContext());
5322 nsRect
nsDisplaySubDocument::GetBounds(nsDisplayListBuilder
* aBuilder
,
5323 bool* aSnap
) const {
5324 bool usingDisplayPort
= UseDisplayPortForViewport(aBuilder
, mFrame
);
5326 if ((mFlags
& nsDisplayOwnLayerFlags::GenerateScrollableLayer
) &&
5329 return mFrame
->GetRect() + aBuilder
->ToReferenceFrame(mFrame
);
5332 return nsDisplayOwnLayer::GetBounds(aBuilder
, aSnap
);
5335 nsRegion
nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
5336 bool* aSnap
) const {
5337 bool usingDisplayPort
= UseDisplayPortForViewport(aBuilder
, mFrame
);
5339 if ((mFlags
& nsDisplayOwnLayerFlags::GenerateScrollableLayer
) &&
5345 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder
, aSnap
);
5349 nsDisplayFixedPosition
* nsDisplayFixedPosition::CreateForFixedBackground(
5350 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsIFrame
* aSecondaryFrame
,
5351 nsDisplayBackgroundImage
* aImage
, const uint16_t aIndex
,
5352 const ActiveScrolledRoot
* aScrollTargetASR
) {
5353 nsDisplayList
temp(aBuilder
);
5354 temp
.AppendToTop(aImage
);
5356 if (aSecondaryFrame
) {
5357 auto tableType
= GetTableTypeFromFrame(aFrame
);
5358 const uint16_t index
= CalculateTablePerFrameKey(aIndex
+ 1, tableType
);
5359 return MakeDisplayItemWithIndex
<nsDisplayTableFixedPosition
>(
5360 aBuilder
, aSecondaryFrame
, index
, &temp
, aFrame
, aScrollTargetASR
);
5363 return MakeDisplayItemWithIndex
<nsDisplayFixedPosition
>(
5364 aBuilder
, aFrame
, aIndex
+ 1, &temp
, aScrollTargetASR
);
5367 nsDisplayFixedPosition::nsDisplayFixedPosition(
5368 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5369 const ActiveScrolledRoot
* aActiveScrolledRoot
,
5370 const ActiveScrolledRoot
* aScrollTargetASR
)
5371 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
),
5372 mScrollTargetASR(aScrollTargetASR
),
5373 mIsFixedBackground(false) {
5374 MOZ_COUNT_CTOR(nsDisplayFixedPosition
);
5377 nsDisplayFixedPosition::nsDisplayFixedPosition(
5378 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5379 const ActiveScrolledRoot
* aScrollTargetASR
)
5380 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
,
5381 aBuilder
->CurrentActiveScrolledRoot()),
5382 // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5383 mScrollTargetASR(aScrollTargetASR
),
5384 mIsFixedBackground(true) {
5385 MOZ_COUNT_CTOR(nsDisplayFixedPosition
);
5388 ScrollableLayerGuid::ViewID
nsDisplayFixedPosition::GetScrollTargetId() const {
5389 if (mScrollTargetASR
&&
5390 (mIsFixedBackground
|| !nsLayoutUtils::IsReallyFixedPos(mFrame
))) {
5391 return mScrollTargetASR
->GetViewId();
5393 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame
->PresContext());
5396 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5397 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5398 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5399 nsDisplayListBuilder
* aDisplayListBuilder
) {
5400 SideBits sides
= SideBits::eNone
;
5401 if (!mIsFixedBackground
) {
5402 sides
= nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame
);
5405 // We install this RAII scrolltarget tracker so that any
5406 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5407 // share the same ASR as this item) use the correct scroll target. That way
5408 // attempts to scroll on those items will scroll the root scroll frame.
5409 wr::DisplayListBuilder::FixedPosScrollTargetTracker
tracker(
5410 aBuilder
, GetActiveScrolledRoot(), GetScrollTargetId(), sides
);
5411 return nsDisplayOwnLayer::CreateWebRenderCommands(
5412 aBuilder
, aResources
, aSc
, aManager
, aDisplayListBuilder
);
5415 bool nsDisplayFixedPosition::UpdateScrollData(
5416 WebRenderScrollData
* aData
, WebRenderLayerScrollData
* aLayerData
) {
5418 if (!mIsFixedBackground
) {
5419 aLayerData
->SetFixedPositionSides(
5420 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame
));
5422 aLayerData
->SetFixedPositionScrollContainerId(GetScrollTargetId());
5424 nsDisplayOwnLayer::UpdateScrollData(aData
, aLayerData
);
5428 bool nsDisplayFixedPosition::ShouldGetFixedOrStickyAnimationId() {
5429 #if defined(MOZ_WIDGET_ANDROID)
5430 return mFrame
->PresContext()->IsRootContentDocumentCrossProcess() &&
5431 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame
->PresContext()) ==
5432 GetScrollTargetId();
5438 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream
& aStream
) {
5439 aStream
<< nsPrintfCString(
5440 " (containerASR %s) (scrolltarget %" PRIu64
")",
5441 ActiveScrolledRoot::ToString(mScrollTargetASR
).get(),
5442 GetScrollTargetId())
5446 TableType
GetTableTypeFromFrame(nsIFrame
* aFrame
) {
5447 if (aFrame
->IsTableFrame()) {
5448 return TableType::Table
;
5451 if (aFrame
->IsTableColFrame()) {
5452 return TableType::TableCol
;
5455 if (aFrame
->IsTableColGroupFrame()) {
5456 return TableType::TableColGroup
;
5459 if (aFrame
->IsTableRowFrame()) {
5460 return TableType::TableRow
;
5463 if (aFrame
->IsTableRowGroupFrame()) {
5464 return TableType::TableRowGroup
;
5467 if (aFrame
->IsTableCellFrame()) {
5468 return TableType::TableCell
;
5471 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5472 return TableType::Table
;
5475 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5476 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5477 nsIFrame
* aAncestorFrame
, const ActiveScrolledRoot
* aScrollTargetASR
)
5478 : nsDisplayFixedPosition(aBuilder
, aFrame
, aList
, aScrollTargetASR
),
5479 mAncestorFrame(aAncestorFrame
) {
5480 if (aBuilder
->IsRetainingDisplayList()) {
5481 mAncestorFrame
->AddDisplayItem(this);
5485 nsDisplayStickyPosition::nsDisplayStickyPosition(
5486 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5487 const ActiveScrolledRoot
* aActiveScrolledRoot
,
5488 const ActiveScrolledRoot
* aContainerASR
, bool aClippedToDisplayPort
)
5489 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
),
5490 mContainerASR(aContainerASR
),
5491 mClippedToDisplayPort(aClippedToDisplayPort
),
5492 mShouldFlatten(false) {
5493 MOZ_COUNT_CTOR(nsDisplayStickyPosition
);
5496 // Returns the smallest distance from "0" to the range [min, max] where
5497 // min <= max. Despite the name, the return value is actually a 1-D vector,
5498 // and so may be negative if max < 0.
5499 static nscoord
DistanceToRange(nscoord min
, nscoord max
) {
5500 MOZ_ASSERT(min
<= max
);
5507 MOZ_ASSERT(min
<= 0 && max
>= 0);
5511 // Returns the magnitude of the part of the range [min, max] that is greater
5512 // than zero. The return value is always non-negative.
5513 static nscoord
PositivePart(nscoord min
, nscoord max
) {
5514 MOZ_ASSERT(min
<= max
);
5524 // Returns the magnitude of the part of the range [min, max] that is less
5525 // than zero. The return value is always non-negative.
5526 static nscoord
NegativePart(nscoord min
, nscoord max
) {
5527 MOZ_ASSERT(min
<= max
);
5537 StickyScrollContainer
* nsDisplayStickyPosition::GetStickyScrollContainer() {
5538 StickyScrollContainer
* stickyScrollContainer
=
5539 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame
);
5540 if (stickyScrollContainer
) {
5541 // If there's no ASR for the scrollframe that this sticky item is attached
5542 // to, then don't create a WR sticky item for it either. Trying to do so
5543 // will end in sadness because WR will interpret some coordinates as
5544 // relative to the nearest enclosing scrollframe, which will correspond
5545 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5546 // same as the scrollframe this sticky item is actually supposed to be
5547 // attached to, thus the sadness.
5548 // Not sending WR the sticky item is ok, because the enclosing scrollframe
5549 // will never be asynchronously scrolled. Instead we will always position
5550 // the sticky items correctly on the gecko side and WR will never need to
5551 // adjust their position itself.
5553 stickyScrollContainer
->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5554 if (!stickyScrollContainer
->ScrollFrame()
5555 ->IsMaybeAsynchronouslyScrolled()) {
5556 stickyScrollContainer
= nullptr;
5559 return stickyScrollContainer
;
5562 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5563 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5564 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5565 nsDisplayListBuilder
* aDisplayListBuilder
) {
5566 StickyScrollContainer
* stickyScrollContainer
= GetStickyScrollContainer();
5568 Maybe
<wr::SpaceAndClipChainHelper
> saccHelper
;
5570 if (stickyScrollContainer
) {
5571 float auPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
5574 nsRect itemBounds
= GetBounds(aDisplayListBuilder
, &snap
);
5576 Maybe
<float> topMargin
;
5577 Maybe
<float> rightMargin
;
5578 Maybe
<float> bottomMargin
;
5579 Maybe
<float> leftMargin
;
5580 wr::StickyOffsetBounds vBounds
= {0.0, 0.0};
5581 wr::StickyOffsetBounds hBounds
= {0.0, 0.0};
5582 nsPoint appliedOffset
;
5584 nsRectAbsolute outer
;
5585 nsRectAbsolute inner
;
5586 stickyScrollContainer
->GetScrollRanges(mFrame
, &outer
, &inner
);
5588 nsIFrame
* scrollFrame
= do_QueryFrame(stickyScrollContainer
->ScrollFrame());
5590 scrollFrame
->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5592 // Adjust the scrollPort coordinates to be relative to the reference frame,
5593 // so that it is in the same space as everything else.
5595 stickyScrollContainer
->ScrollFrame()->GetScrollPortRect();
5596 scrollPort
+= offset
;
5598 // The following computations make more sense upon understanding the
5599 // semantics of "inner" and "outer", which is explained in the comment on
5600 // SetStickyPositionData in Layers.h.
5602 if (outer
.YMost() != inner
.YMost()) {
5603 // Question: How far will itemBounds.y be from the top of the scrollport
5604 // when we have scrolled from the current scroll position of "0" to
5605 // reach the range [inner.YMost(), outer.YMost()] where the item gets
5607 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5608 // needs to be adjusted by the distance to the range, less any other
5609 // sticky ranges that fall between 0 and the range. If the distance is
5610 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5611 // scrolling upwards (decreasing scroll offset) to reach that range,
5612 // which would increase itemBounds.y and make it farther away from the
5613 // top of the scrollport. So in that case the adjustment is -distance.
5614 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5615 // we would be scrolling downwards, itemBounds.y would decrease, and we
5616 // again need to adjust by -distance. If we are already in the range
5617 // then no adjustment is needed and distance is 0 so again using
5618 // -distance works. If the distance is positive, and the item has both
5619 // top and bottom sticky ranges, then the bottom sticky range may fall
5620 // (entirely[1] or partly[2]) between the current scroll position.
5621 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5622 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5623 // In these cases, the item doesn't actually move for that part of the
5624 // distance, so we need to subtract out that bit, which can be computed
5625 // as the positive portion of the range [outer.Y(), inner.Y()].
5626 nscoord distance
= DistanceToRange(inner
.YMost(), outer
.YMost());
5628 distance
-= PositivePart(outer
.Y(), inner
.Y());
5630 topMargin
= Some(NSAppUnitsToFloatPixels(
5631 itemBounds
.y
- scrollPort
.y
- distance
, auPerDevPixel
));
5632 // Question: What is the maximum positive ("downward") offset that WR
5633 // will have to apply to this item in order to prevent the item from
5635 // Answer: Since the item is "sticky" in the range [inner.YMost(),
5636 // outer.YMost()], the maximum offset will be the size of the range, which
5637 // is outer.YMost() - inner.YMost().
5639 NSAppUnitsToFloatPixels(outer
.YMost() - inner
.YMost(), auPerDevPixel
);
5640 // Question: how much of an offset has layout already applied to the item?
5641 // Answer: if we are
5642 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5643 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5644 // then layout has already applied some offset to the position of the
5645 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5646 // and |outer.YMost() - inner.YMost()| in case (b).
5647 if (inner
.YMost() < 0) {
5648 appliedOffset
.y
= std::min(0, outer
.YMost()) - inner
.YMost();
5649 MOZ_ASSERT(appliedOffset
.y
> 0);
5652 if (outer
.Y() != inner
.Y()) {
5653 // Similar logic as in the previous section, but this time we care about
5654 // the distance from itemBounds.YMost() to scrollPort.YMost().
5655 nscoord distance
= DistanceToRange(outer
.Y(), inner
.Y());
5657 distance
+= NegativePart(inner
.YMost(), outer
.YMost());
5659 bottomMargin
= Some(NSAppUnitsToFloatPixels(
5660 scrollPort
.YMost() - itemBounds
.YMost() + distance
, auPerDevPixel
));
5661 // And here WR will be moving the item upwards rather than downwards so
5662 // again things are inverted from the previous block.
5664 NSAppUnitsToFloatPixels(outer
.Y() - inner
.Y(), auPerDevPixel
);
5665 // We can't have appliedOffset be both positive and negative, and the top
5666 // adjustment takes priority. So here we only update appliedOffset.y if
5667 // it wasn't set by the top-sticky case above.
5668 if (appliedOffset
.y
== 0 && inner
.Y() > 0) {
5669 appliedOffset
.y
= std::max(0, outer
.Y()) - inner
.Y();
5670 MOZ_ASSERT(appliedOffset
.y
< 0);
5673 // Same as above, but for the x-axis
5674 if (outer
.XMost() != inner
.XMost()) {
5675 nscoord distance
= DistanceToRange(inner
.XMost(), outer
.XMost());
5677 distance
-= PositivePart(outer
.X(), inner
.X());
5679 leftMargin
= Some(NSAppUnitsToFloatPixels(
5680 itemBounds
.x
- scrollPort
.x
- distance
, auPerDevPixel
));
5682 NSAppUnitsToFloatPixels(outer
.XMost() - inner
.XMost(), auPerDevPixel
);
5683 if (inner
.XMost() < 0) {
5684 appliedOffset
.x
= std::min(0, outer
.XMost()) - inner
.XMost();
5685 MOZ_ASSERT(appliedOffset
.x
> 0);
5688 if (outer
.X() != inner
.X()) {
5689 nscoord distance
= DistanceToRange(outer
.X(), inner
.X());
5691 distance
+= NegativePart(inner
.XMost(), outer
.XMost());
5693 rightMargin
= Some(NSAppUnitsToFloatPixels(
5694 scrollPort
.XMost() - itemBounds
.XMost() + distance
, auPerDevPixel
));
5696 NSAppUnitsToFloatPixels(outer
.X() - inner
.X(), auPerDevPixel
);
5697 if (appliedOffset
.x
== 0 && inner
.X() > 0) {
5698 appliedOffset
.x
= std::max(0, outer
.X()) - inner
.X();
5699 MOZ_ASSERT(appliedOffset
.x
< 0);
5703 LayoutDeviceRect bounds
=
5704 LayoutDeviceRect::FromAppUnits(itemBounds
, auPerDevPixel
);
5705 wr::LayoutVector2D applied
= {
5706 NSAppUnitsToFloatPixels(appliedOffset
.x
, auPerDevPixel
),
5707 NSAppUnitsToFloatPixels(appliedOffset
.y
, auPerDevPixel
)};
5708 wr::WrSpatialId spatialId
= aBuilder
.DefineStickyFrame(
5709 wr::ToLayoutRect(bounds
), topMargin
.ptrOr(nullptr),
5710 rightMargin
.ptrOr(nullptr), bottomMargin
.ptrOr(nullptr),
5711 leftMargin
.ptrOr(nullptr), vBounds
, hBounds
, applied
,
5712 wr::SpatialKey(uint64_t(mFrame
), GetPerFrameKey(),
5713 wr::SpatialKeyKind::Sticky
));
5715 saccHelper
.emplace(aBuilder
, spatialId
);
5716 aManager
->CommandBuilder().PushOverrideForASR(mContainerASR
, spatialId
);
5720 wr::StackingContextParams params
;
5722 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
5723 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this,
5725 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder
, aResources
, sc
,
5726 aManager
, aDisplayListBuilder
);
5729 if (stickyScrollContainer
) {
5730 aManager
->CommandBuilder().PopOverrideForASR(mContainerASR
);
5736 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5737 StickyScrollContainer
* aStickyScrollContainer
, float aAppUnitsPerDevPixel
,
5738 float aScaleX
, float aScaleY
, LayerRectAbsolute
& aStickyOuter
,
5739 LayerRectAbsolute
& aStickyInner
) {
5740 nsRectAbsolute outer
;
5741 nsRectAbsolute inner
;
5742 aStickyScrollContainer
->GetScrollRanges(mFrame
, &outer
, &inner
);
5743 aStickyOuter
.SetBox(
5744 NSAppUnitsToFloatPixels(outer
.X(), aAppUnitsPerDevPixel
) * aScaleX
,
5745 NSAppUnitsToFloatPixels(outer
.Y(), aAppUnitsPerDevPixel
) * aScaleY
,
5746 NSAppUnitsToFloatPixels(outer
.XMost(), aAppUnitsPerDevPixel
) * aScaleX
,
5747 NSAppUnitsToFloatPixels(outer
.YMost(), aAppUnitsPerDevPixel
) * aScaleY
);
5748 aStickyInner
.SetBox(
5749 NSAppUnitsToFloatPixels(inner
.X(), aAppUnitsPerDevPixel
) * aScaleX
,
5750 NSAppUnitsToFloatPixels(inner
.Y(), aAppUnitsPerDevPixel
) * aScaleY
,
5751 NSAppUnitsToFloatPixels(inner
.XMost(), aAppUnitsPerDevPixel
) * aScaleX
,
5752 NSAppUnitsToFloatPixels(inner
.YMost(), aAppUnitsPerDevPixel
) * aScaleY
);
5755 bool nsDisplayStickyPosition::UpdateScrollData(
5756 WebRenderScrollData
* aData
, WebRenderLayerScrollData
* aLayerData
) {
5757 bool hasDynamicToolbar
= HasDynamicToolbar();
5758 if (aLayerData
&& hasDynamicToolbar
) {
5759 StickyScrollContainer
* stickyScrollContainer
= GetStickyScrollContainer();
5760 if (stickyScrollContainer
) {
5761 float auPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
5762 float cumulativeResolution
=
5763 mFrame
->PresShell()->GetCumulativeResolution();
5764 LayerRectAbsolute stickyOuter
;
5765 LayerRectAbsolute stickyInner
;
5766 CalculateLayerScrollRanges(stickyScrollContainer
, auPerDevPixel
,
5767 cumulativeResolution
, cumulativeResolution
,
5768 stickyOuter
, stickyInner
);
5769 aLayerData
->SetStickyScrollRangeOuter(stickyOuter
);
5770 aLayerData
->SetStickyScrollRangeInner(stickyInner
);
5773 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame
);
5774 aLayerData
->SetFixedPositionSides(sides
);
5776 ScrollableLayerGuid::ViewID scrollId
=
5777 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer
->ScrollFrame()
5778 ->GetScrolledFrame()
5780 aLayerData
->SetStickyPositionScrollContainerId(scrollId
);
5783 // Return true if either there is a dynamic toolbar affecting this sticky
5784 // item or the OwnLayer base implementation returns true for some other
5786 bool ret
= hasDynamicToolbar
;
5787 ret
|= nsDisplayOwnLayer::UpdateScrollData(aData
, aLayerData
);
5791 bool nsDisplayStickyPosition::ShouldGetFixedOrStickyAnimationId() {
5792 #if defined(MOZ_WIDGET_ANDROID)
5793 if (HasDynamicToolbar()) { // also implies being in the cross-process RCD
5794 StickyScrollContainer
* stickyScrollContainer
= GetStickyScrollContainer();
5795 if (stickyScrollContainer
) {
5796 ScrollableLayerGuid::ViewID scrollId
=
5797 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer
->ScrollFrame()
5798 ->GetScrolledFrame()
5800 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame
->PresContext()) ==
5808 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5809 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aScrolledFrame
,
5810 nsIFrame
* aScrollFrame
, const CompositorHitTestInfo
& aHitInfo
,
5811 const nsRect
& aHitArea
)
5812 : nsDisplayWrapList(aBuilder
, aScrollFrame
),
5813 mScrollFrame(aScrollFrame
),
5814 mScrolledFrame(aScrolledFrame
),
5815 mScrollParentId(aBuilder
->GetCurrentScrollParentId()),
5817 mHitArea(aHitArea
) {
5818 #ifdef NS_BUILD_REFCNT_LOGGING
5819 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer
);
5823 UniquePtr
<ScrollMetadata
> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5824 nsDisplayListBuilder
* aBuilder
, WebRenderLayerManager
* aLayerManager
) {
5825 ScrollMetadata metadata
= nsLayoutUtils::ComputeScrollMetadata(
5826 mScrolledFrame
, mScrollFrame
, mScrollFrame
->GetContent(), Frame(),
5827 ToReferenceFrame(), aLayerManager
, mScrollParentId
,
5828 mScrollFrame
->GetSize(), false);
5829 metadata
.GetMetrics().SetIsScrollInfoLayer(true);
5830 nsIScrollableFrame
* scrollableFrame
= mScrollFrame
->GetScrollTargetFrame();
5831 if (scrollableFrame
) {
5832 aBuilder
->AddScrollFrameToNotify(scrollableFrame
);
5835 return UniquePtr
<ScrollMetadata
>(new ScrollMetadata(metadata
));
5838 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5839 WebRenderScrollData
* aData
, WebRenderLayerScrollData
* aLayerData
) {
5841 UniquePtr
<ScrollMetadata
> metadata
=
5842 ComputeScrollMetadata(aData
->GetBuilder(), aData
->GetManager());
5844 MOZ_ASSERT(metadata
);
5845 aLayerData
->AppendScrollMetadata(*aData
, *metadata
);
5850 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5851 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
5852 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
5853 nsDisplayListBuilder
* aDisplayListBuilder
) {
5854 ScrollableLayerGuid::ViewID scrollId
=
5855 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame
->GetContent());
5857 const LayoutDeviceRect devRect
= LayoutDeviceRect::FromAppUnits(
5858 mHitArea
, mScrollFrame
->PresContext()->AppUnitsPerDevPixel());
5860 const wr::LayoutRect rect
= wr::ToLayoutRect(devRect
);
5862 aBuilder
.PushHitTest(rect
, rect
, !BackfaceIsHidden(), scrollId
, mHitInfo
,
5868 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream
& aStream
) {
5869 aStream
<< " (scrollframe " << mScrollFrame
<< " scrolledFrame "
5870 << mScrolledFrame
<< ")";
5873 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
5874 nsSubDocumentFrame
* aSubDocFrame
,
5875 nsDisplayList
* aList
, int32_t aAPD
,
5876 int32_t aParentAPD
, nsDisplayOwnLayerFlags aFlags
)
5877 : nsDisplaySubDocument(aBuilder
, aFrame
, aSubDocFrame
, aList
, aFlags
),
5879 mParentAPD(aParentAPD
) {
5880 MOZ_COUNT_CTOR(nsDisplayZoom
);
5883 nsRect
nsDisplayZoom::GetBounds(nsDisplayListBuilder
* aBuilder
,
5884 bool* aSnap
) const {
5885 nsRect bounds
= nsDisplaySubDocument::GetBounds(aBuilder
, aSnap
);
5887 return bounds
.ScaleToOtherAppUnitsRoundOut(mAPD
, mParentAPD
);
5890 void nsDisplayZoom::HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
5891 HitTestState
* aState
,
5892 nsTArray
<nsIFrame
*>* aOutFrames
) {
5894 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5895 // rect as well instead of possibly rounding the width or height to zero.
5896 if (aRect
.width
== 1 && aRect
.height
== 1) {
5897 rect
.MoveTo(aRect
.TopLeft().ScaleToOtherAppUnits(mParentAPD
, mAPD
));
5898 rect
.width
= rect
.height
= 1;
5900 rect
= aRect
.ScaleToOtherAppUnitsRoundOut(mParentAPD
, mAPD
);
5902 mList
.HitTest(aBuilder
, rect
, aState
, aOutFrames
);
5905 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5906 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
5907 const ActiveScrolledRoot
* aActiveScrolledRoot
, FrameMetrics::ViewID aViewID
)
5908 : nsDisplayOwnLayer(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
),
5910 MOZ_COUNT_CTOR(nsDisplayAsyncZoom
);
5913 #ifdef NS_BUILD_REFCNT_LOGGING
5914 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5915 MOZ_COUNT_DTOR(nsDisplayAsyncZoom
);
5919 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder
* aBuilder
,
5920 const nsRect
& aRect
, HitTestState
* aState
,
5921 nsTArray
<nsIFrame
*>* aOutFrames
) {
5923 nsIScrollableFrame
* scrollFrame
= do_QueryFrame(mFrame
);
5924 MOZ_ASSERT(scrollFrame
&& ViewportUtils::IsZoomedContentRoot(
5925 scrollFrame
->GetScrolledFrame()));
5927 nsRect rect
= ViewportUtils::VisualToLayout(aRect
, mFrame
->PresShell());
5928 mList
.HitTest(aBuilder
, rect
, aState
, aOutFrames
);
5931 bool nsDisplayAsyncZoom::UpdateScrollData(
5932 WebRenderScrollData
* aData
, WebRenderLayerScrollData
* aLayerData
) {
5933 bool ret
= nsDisplayOwnLayer::UpdateScrollData(aData
, aLayerData
);
5936 aLayerData
->SetAsyncZoomContainerId(mViewID
);
5941 ///////////////////////////////////////////////////
5942 // nsDisplayTransform Implementation
5946 static_assert(sizeof(nsDisplayTransform
) <= 512,
5947 "nsDisplayTransform has grown");
5950 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder
* aBuilder
,
5951 nsIFrame
* aFrame
, nsDisplayList
* aList
,
5952 const nsRect
& aChildrenBuildingRect
)
5953 : nsPaintedDisplayItem(aBuilder
, aFrame
),
5954 mChildren(aBuilder
),
5955 mTransform(Some(Matrix4x4())),
5956 mChildrenBuildingRect(aChildrenBuildingRect
),
5957 mPrerenderDecision(PrerenderDecision::No
),
5958 mIsTransformSeparator(true),
5959 mHasTransformGetter(false),
5960 mHasAssociatedPerspective(false),
5961 mContainsASRs(false) {
5962 MOZ_COUNT_CTOR(nsDisplayTransform
);
5963 MOZ_ASSERT(aFrame
, "Must have a frame!");
5964 Init(aBuilder
, aList
);
5967 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder
* aBuilder
,
5968 nsIFrame
* aFrame
, nsDisplayList
* aList
,
5969 const nsRect
& aChildrenBuildingRect
,
5970 PrerenderDecision aPrerenderDecision
)
5971 : nsPaintedDisplayItem(aBuilder
, aFrame
),
5972 mChildren(aBuilder
),
5973 mChildrenBuildingRect(aChildrenBuildingRect
),
5974 mPrerenderDecision(aPrerenderDecision
),
5975 mIsTransformSeparator(false),
5976 mHasTransformGetter(false),
5977 mHasAssociatedPerspective(false),
5978 mContainsASRs(false) {
5979 MOZ_COUNT_CTOR(nsDisplayTransform
);
5980 MOZ_ASSERT(aFrame
, "Must have a frame!");
5981 SetReferenceFrameToAncestor(aBuilder
);
5982 Init(aBuilder
, aList
);
5985 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder
* aBuilder
,
5986 nsIFrame
* aFrame
, nsDisplayList
* aList
,
5987 const nsRect
& aChildrenBuildingRect
,
5988 decltype(WithTransformGetter
))
5989 : nsPaintedDisplayItem(aBuilder
, aFrame
),
5990 mChildren(aBuilder
),
5991 mChildrenBuildingRect(aChildrenBuildingRect
),
5992 mPrerenderDecision(PrerenderDecision::No
),
5993 mIsTransformSeparator(false),
5994 mHasTransformGetter(true),
5995 mHasAssociatedPerspective(false),
5996 mContainsASRs(false) {
5997 MOZ_COUNT_CTOR(nsDisplayTransform
);
5998 MOZ_ASSERT(aFrame
, "Must have a frame!");
5999 MOZ_ASSERT(aFrame
->GetTransformGetter());
6000 Init(aBuilder
, aList
);
6003 void nsDisplayTransform::SetReferenceFrameToAncestor(
6004 nsDisplayListBuilder
* aBuilder
) {
6005 if (mFrame
== aBuilder
->RootReferenceFrame()) {
6008 // We manually recompute mToReferenceFrame without going through the
6009 // builder, since this won't apply the 'additional offset'. Our
6010 // children will already be painting with that applied, and we don't
6011 // want to include it a second time in our transform. We don't recompute
6012 // our visible/building rects, since those should still include the additional
6014 // TODO: Are there are things computed using our ToReferenceFrame that should
6015 // have the additional offset applied? Should we instead just manually remove
6016 // the offset from our transform instead of this more general value?
6017 // Can we instead apply the additional offset to us and not our children, like
6018 // we do for all other offsets (and how reference frames are supposed to
6020 nsIFrame
* outerFrame
= nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame
);
6021 const nsIFrame
* referenceFrame
= aBuilder
->FindReferenceFrameFor(outerFrame
);
6022 mToReferenceFrame
= mFrame
->GetOffsetToCrossDoc(referenceFrame
);
6025 void nsDisplayTransform::Init(nsDisplayListBuilder
* aBuilder
,
6026 nsDisplayList
* aChildren
) {
6027 mChildren
.AppendToTop(aChildren
);
6028 UpdateBounds(aBuilder
);
6031 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) {
6035 /* Returns the delta specified by the transform-origin property.
6036 * This is a positive delta, meaning that it indicates the direction to move
6037 * to get from (0, 0) of the frame to the transform origin. This function is
6038 * called off the main thread.
6041 Point3D
nsDisplayTransform::GetDeltaToTransformOrigin(
6042 const nsIFrame
* aFrame
, TransformReferenceBox
& aRefBox
,
6043 float aAppUnitsPerPixel
) {
6044 MOZ_ASSERT(aFrame
, "Can't get delta for a null frame!");
6045 MOZ_ASSERT(aFrame
->IsTransformed() || aFrame
->BackfaceIsHidden() ||
6046 aFrame
->Combines3DTransformWithAncestors(),
6047 "Shouldn't get a delta for an untransformed frame!");
6049 if (!aFrame
->IsTransformed()) {
6053 /* For both of the coordinates, if the value of transform is a
6054 * percentage, it's relative to the size of the frame. Otherwise, if it's
6055 * a distance, it's already computed for us!
6057 const nsStyleDisplay
* display
= aFrame
->StyleDisplay();
6059 const StyleTransformOrigin
& transformOrigin
= display
->mTransformOrigin
;
6060 CSSPoint origin
= nsStyleTransformMatrix::Convert2DPosition(
6061 transformOrigin
.horizontal
, transformOrigin
.vertical
, aRefBox
);
6064 // 1. SVG frames have a reference box that can be (and typically is) offset
6065 // from the TopLeft() of the frame. We need to account for that here.
6066 // 2. If we are using transform-box:content-box in CSS layout, we have the
6067 // offset from TopLeft() of the frame as well.
6068 origin
.x
+= CSSPixel::FromAppUnits(aRefBox
.X());
6069 origin
.y
+= CSSPixel::FromAppUnits(aRefBox
.Y());
6071 float scale
= AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel
);
6072 float z
= transformOrigin
.depth
._0
;
6073 return Point3D(origin
.x
* scale
, origin
.y
* scale
, z
* scale
);
6077 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame
* aFrame
,
6078 float aAppUnitsPerPixel
,
6079 Matrix4x4
& aOutMatrix
) {
6080 MOZ_ASSERT(aFrame
, "Can't get delta for a null frame!");
6081 MOZ_ASSERT(aFrame
->IsTransformed() || aFrame
->BackfaceIsHidden() ||
6082 aFrame
->Combines3DTransformWithAncestors(),
6083 "Shouldn't get a delta for an untransformed frame!");
6084 MOZ_ASSERT(aOutMatrix
.IsIdentity(), "Must have a blank output matrix");
6086 if (!aFrame
->IsTransformed()) {
6090 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6091 // correctly yet (similar to the aBoundsOverride case for
6092 // GetResultingTransformMatrix)?
6093 nsIFrame
* perspectiveFrame
=
6094 aFrame
->GetClosestFlattenedTreeAncestorPrimaryFrame();
6095 if (!perspectiveFrame
) {
6099 /* Grab the values for perspective and perspective-origin (if present) */
6100 const nsStyleDisplay
* perspectiveDisplay
= perspectiveFrame
->StyleDisplay();
6101 if (perspectiveDisplay
->mChildPerspective
.IsNone()) {
6105 MOZ_ASSERT(perspectiveDisplay
->mChildPerspective
.IsLength());
6107 perspectiveDisplay
->mChildPerspective
.AsLength().ToCSSPixels();
6108 perspective
= std::max(1.0f
, perspective
);
6109 if (perspective
< std::numeric_limits
<Float
>::epsilon()) {
6113 TransformReferenceBox
refBox(perspectiveFrame
);
6115 Point perspectiveOrigin
= nsStyleTransformMatrix::Convert2DPosition(
6116 perspectiveDisplay
->mPerspectiveOrigin
.horizontal
,
6117 perspectiveDisplay
->mPerspectiveOrigin
.vertical
, refBox
,
6120 /* GetOffsetTo computes the offset required to move from 0,0 in
6121 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6122 * this, it's faster to compute this way.
6124 nsPoint frameToPerspectiveOffset
= -aFrame
->GetOffsetTo(perspectiveFrame
);
6125 Point
frameToPerspectiveGfxOffset(
6126 NSAppUnitsToFloatPixels(frameToPerspectiveOffset
.x
, aAppUnitsPerPixel
),
6127 NSAppUnitsToFloatPixels(frameToPerspectiveOffset
.y
, aAppUnitsPerPixel
));
6129 /* Move the perspective origin to be relative to aFrame, instead of relative
6130 * to the containing block which is how it was specified in the style system.
6132 perspectiveOrigin
+= frameToPerspectiveGfxOffset
;
6135 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective
),
6138 aOutMatrix
.ChangeBasis(Point3D(perspectiveOrigin
.x
, perspectiveOrigin
.y
, 0));
6142 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6143 const nsIFrame
* aFrame
, TransformReferenceBox
& aRefBox
,
6144 float aAppUnitsPerPixel
)
6146 mTranslate(aFrame
->StyleDisplay()->mTranslate
),
6147 mRotate(aFrame
->StyleDisplay()->mRotate
),
6148 mScale(aFrame
->StyleDisplay()->mScale
),
6149 mTransform(aFrame
->StyleDisplay()->mTransform
),
6150 mMotion(aFrame
->StyleDisplay()->mOffsetPath
.IsNone()
6152 : MotionPathUtils::ResolveMotionPath(aFrame
, aRefBox
)),
6154 GetDeltaToTransformOrigin(aFrame
, aRefBox
, aAppUnitsPerPixel
)) {}
6156 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6157 * translates from local coordinate space to transform coordinate space, then
6160 Matrix4x4
nsDisplayTransform::GetResultingTransformMatrix(
6161 const FrameTransformProperties
& aProperties
, TransformReferenceBox
& aRefBox
,
6162 float aAppUnitsPerPixel
) {
6163 return GetResultingTransformMatrixInternal(aProperties
, aRefBox
, nsPoint(),
6164 aAppUnitsPerPixel
, 0);
6167 Matrix4x4
nsDisplayTransform::GetResultingTransformMatrix(
6168 const nsIFrame
* aFrame
, const nsPoint
& aOrigin
, float aAppUnitsPerPixel
,
6170 TransformReferenceBox
refBox(aFrame
);
6171 FrameTransformProperties
props(aFrame
, refBox
, aAppUnitsPerPixel
);
6172 return GetResultingTransformMatrixInternal(props
, refBox
, aOrigin
,
6173 aAppUnitsPerPixel
, aFlags
);
6176 Matrix4x4
nsDisplayTransform::GetResultingTransformMatrixInternal(
6177 const FrameTransformProperties
& aProperties
, TransformReferenceBox
& aRefBox
,
6178 const nsPoint
& aOrigin
, float aAppUnitsPerPixel
, uint32_t aFlags
) {
6179 const nsIFrame
* frame
= aProperties
.mFrame
;
6180 NS_ASSERTION(frame
|| !(aFlags
& INCLUDE_PERSPECTIVE
),
6181 "Must have a frame to compute perspective!");
6183 // IncrementScaleRestyleCountIfNeeded in ActiveLayerTracker.cpp is a
6184 // simplified copy of this function.
6186 // Get the underlying transform matrix:
6188 /* Get the matrix, then change its basis to factor in the origin. */
6190 // Call IsSVGTransformed() regardless of the value of
6191 // aProperties.HasTransform(), since we still need any
6192 // potential parentsChildrenOnlyTransform.
6193 Matrix svgTransform
, parentsChildrenOnlyTransform
;
6194 const bool hasSVGTransforms
=
6195 frame
&& frame
->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED
) &&
6196 frame
->IsSVGTransformed(&svgTransform
, &parentsChildrenOnlyTransform
);
6197 bool shouldRound
= nsLayoutUtils::ShouldSnapToGrid(frame
);
6199 /* Transformed frames always have a transform, or are preserving 3d (and might
6200 * still have perspective!) */
6201 if (aProperties
.HasTransform()) {
6202 result
= nsStyleTransformMatrix::ReadTransforms(
6203 aProperties
.mTranslate
, aProperties
.mRotate
, aProperties
.mScale
,
6204 aProperties
.mMotion
.ptrOr(nullptr), aProperties
.mTransform
, aRefBox
,
6206 } else if (hasSVGTransforms
) {
6207 // Correct the translation components for zoom:
6208 float pixelsPerCSSPx
= AppUnitsPerCSSPixel() / aAppUnitsPerPixel
;
6209 svgTransform
._31
*= pixelsPerCSSPx
;
6210 svgTransform
._32
*= pixelsPerCSSPx
;
6211 result
= Matrix4x4::From2D(svgTransform
);
6214 // Apply any translation due to 'transform-origin' and/or 'transform-box':
6215 result
.ChangeBasis(aProperties
.mToTransformOrigin
);
6217 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
6218 // an explanation of what children-only transforms are.
6219 const bool parentHasChildrenOnlyTransform
=
6220 hasSVGTransforms
&& !parentsChildrenOnlyTransform
.IsIdentity();
6222 if (parentHasChildrenOnlyTransform
) {
6223 float pixelsPerCSSPx
= AppUnitsPerCSSPixel() / aAppUnitsPerPixel
;
6224 parentsChildrenOnlyTransform
._31
*= pixelsPerCSSPx
;
6225 parentsChildrenOnlyTransform
._32
*= pixelsPerCSSPx
;
6227 Point3D
frameOffset(
6228 NSAppUnitsToFloatPixels(-frame
->GetPosition().x
, aAppUnitsPerPixel
),
6229 NSAppUnitsToFloatPixels(-frame
->GetPosition().y
, aAppUnitsPerPixel
), 0);
6230 Matrix4x4 parentsChildrenOnlyTransform3D
=
6231 Matrix4x4::From2D(parentsChildrenOnlyTransform
)
6232 .ChangeBasis(frameOffset
);
6234 result
*= parentsChildrenOnlyTransform3D
;
6237 Matrix4x4 perspectiveMatrix
;
6238 bool hasPerspective
= aFlags
& INCLUDE_PERSPECTIVE
;
6239 if (hasPerspective
) {
6240 if (ComputePerspectiveMatrix(frame
, aAppUnitsPerPixel
, perspectiveMatrix
)) {
6241 result
*= perspectiveMatrix
;
6245 if ((aFlags
& INCLUDE_PRESERVE3D_ANCESTORS
) && frame
&&
6246 frame
->Combines3DTransformWithAncestors()) {
6247 // Include the transform set on our parent
6248 nsIFrame
* parentFrame
=
6249 frame
->GetClosestFlattenedTreeAncestorPrimaryFrame();
6250 NS_ASSERTION(parentFrame
&& parentFrame
->IsTransformed() &&
6251 parentFrame
->Extend3DContext(),
6252 "Preserve3D mismatch!");
6253 TransformReferenceBox
refBox(parentFrame
);
6254 FrameTransformProperties
props(parentFrame
, refBox
, aAppUnitsPerPixel
);
6257 aFlags
& (INCLUDE_PRESERVE3D_ANCESTORS
| INCLUDE_PERSPECTIVE
);
6259 // If this frame isn't transformed (but we exist for backface-visibility),
6260 // then we're not a reference frame so no offset to origin will be added.
6261 // Otherwise we need to manually translate into our parent's coordinate
6263 if (frame
->IsTransformed()) {
6264 nsLayoutUtils::PostTranslate(result
, frame
->GetPosition(),
6265 aAppUnitsPerPixel
, shouldRound
);
6267 Matrix4x4 parent
= GetResultingTransformMatrixInternal(
6268 props
, refBox
, nsPoint(0, 0), aAppUnitsPerPixel
, flags
);
6269 result
= result
* parent
;
6272 if (aFlags
& OFFSET_BY_ORIGIN
) {
6273 nsLayoutUtils::PostTranslate(result
, aOrigin
, aAppUnitsPerPixel
,
6280 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder
* aBuilder
) {
6281 static constexpr nsCSSPropertyIDSet opacitySet
=
6282 nsCSSPropertyIDSet::OpacityProperties();
6283 if (ActiveLayerTracker::IsStyleAnimated(aBuilder
, mFrame
, opacitySet
)) {
6287 EffectCompositor::SetPerformanceWarning(
6289 AnimationPerformanceWarning(
6290 AnimationPerformanceWarning::Type::OpacityFrameInactive
));
6295 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder
* aBuilder
) {
6296 return mPrerenderDecision
!= PrerenderDecision::No
;
6299 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
6300 nsDisplayListBuilder
* aBuilder
) {
6301 return StaticPrefs::gfx_omta_background_color();
6304 static bool IsInStickyPositionedSubtree(const nsIFrame
* aFrame
) {
6305 for (const nsIFrame
* frame
= aFrame
; frame
;
6306 frame
= nsLayoutUtils::GetCrossDocParentFrameInProcess(frame
)) {
6307 if (frame
->IsStickyPositioned()) {
6314 static bool ShouldUsePartialPrerender(const nsIFrame
* aFrame
) {
6315 return StaticPrefs::layout_animation_prerender_partial() &&
6316 // Bug 1642547: Support partial prerender for position:sticky elements.
6317 !IsInStickyPositionedSubtree(aFrame
);
6321 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
6322 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsRect
* aDirtyRect
)
6324 PrerenderInfo result
;
6325 // If we are in a preserve-3d tree, and we've disallowed async animations, we
6326 // return No prerender decision directly.
6327 if ((aFrame
->Extend3DContext() ||
6328 aFrame
->Combines3DTransformWithAncestors()) &&
6329 !aBuilder
->GetPreserves3DAllowAsyncAnimation()) {
6333 // Elements whose transform has been modified recently, or which
6334 // have a compositor-animated transform, can be prerendered. An element
6335 // might have only just had its transform animated in which case
6336 // the ActiveLayerManager may not have been notified yet.
6337 static constexpr nsCSSPropertyIDSet transformSet
=
6338 nsCSSPropertyIDSet::TransformLikeProperties();
6339 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame
) &&
6340 !EffectCompositor::HasAnimationsForCompositor(
6341 aFrame
, DisplayItemType::TYPE_TRANSFORM
)) {
6342 EffectCompositor::SetPerformanceWarning(
6343 aFrame
, transformSet
,
6344 AnimationPerformanceWarning(
6345 AnimationPerformanceWarning::Type::TransformFrameInactive
));
6347 // This case happens when we're sure that the frame is not animated and its
6348 // preserve-3d ancestors are not, either. So we don't need to pre-render.
6349 // However, this decision shouldn't affect the decisions for other frames in
6350 // the preserve-3d context. We need this flag to determine whether we should
6351 // block async animations on other frames in the current preserve-3d tree.
6352 result
.mHasAnimations
= false;
6356 // We should not allow prerender if any ancestor container element has
6357 // mask/clip-path effects.
6359 // With prerender and async transform animation, we do not need to restyle an
6360 // animated element to respect position changes, since that transform is done
6361 // by layer animation. As a result, the container element is not aware of
6362 // position change of that containing element and loses the chance to update
6363 // the content of mask/clip-path.
6365 // Why do we need to update a mask? This is relative to how we generate a
6366 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
6367 // mask layer, to reduce memory usage, we did not choose the size of the
6368 // masked element as mask size. Instead, we read the union of bounds of all
6369 // children display items by nsDisplayWrapList::GetBounds, which is smaller
6370 // than or equal to the masked element's boundary, and use it as the position
6371 // size of the mask layer. That union bounds is actually affected by the
6372 // geometry of the animated element. To keep the content of mask up to date,
6373 // forbidding of prerender is required.
6374 for (nsIFrame
* container
=
6375 nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame
);
6377 container
= nsLayoutUtils::GetCrossDocParentFrameInProcess(container
)) {
6378 const nsStyleSVGReset
* svgReset
= container
->StyleSVGReset();
6379 if (svgReset
->HasMask() || svgReset
->HasClipPath()) {
6384 // If the incoming dirty rect already contains the entire overflow area,
6385 // we are already rendering the entire content.
6386 nsRect overflow
= aFrame
->InkOverflowRectRelativeToSelf();
6387 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
6388 // in cases of non-invertible transforms, so we set `untransformedRect` to
6389 // `aDirtyRect` as an initial value for such cases.
6390 nsRect untransformedDirtyRect
= *aDirtyRect
;
6391 UntransformRect(*aDirtyRect
, overflow
, aFrame
, &untransformedDirtyRect
);
6392 if (untransformedDirtyRect
.Contains(overflow
)) {
6393 *aDirtyRect
= untransformedDirtyRect
;
6394 result
.mDecision
= PrerenderDecision::Full
;
6398 float viewportRatio
=
6399 StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
6400 uint32_t absoluteLimitX
=
6401 StaticPrefs::layout_animation_prerender_absolute_limit_x();
6402 uint32_t absoluteLimitY
=
6403 StaticPrefs::layout_animation_prerender_absolute_limit_y();
6404 nsSize refSize
= aBuilder
->RootReferenceFrame()->GetSize();
6406 float resolution
= aFrame
->PresShell()->GetCumulativeResolution();
6407 if (resolution
< 1.0f
) {
6409 NSCoordSaturatingNonnegativeMultiply(refSize
.width
, 1.0f
/ resolution
),
6410 NSCoordSaturatingNonnegativeMultiply(refSize
.height
,
6411 1.0f
/ resolution
));
6414 // Only prerender if the transformed frame's size is <= a multiple of the
6415 // reference frame size (~viewport), and less than an absolute limit.
6416 // Both the ratio and the absolute limit are configurable.
6417 nscoord maxLength
= std::max(nscoord(refSize
.width
* viewportRatio
),
6418 nscoord(refSize
.height
* viewportRatio
));
6419 nsSize
relativeLimit(maxLength
, maxLength
);
6420 nsSize
absoluteLimit(
6421 aFrame
->PresContext()->DevPixelsToAppUnits(absoluteLimitX
),
6422 aFrame
->PresContext()->DevPixelsToAppUnits(absoluteLimitY
));
6423 nsSize maxSize
= Min(relativeLimit
, absoluteLimit
);
6425 const auto transform
= nsLayoutUtils::GetTransformToAncestor(
6427 RelativeTo
{nsLayoutUtils::GetDisplayRootFrame(aFrame
)});
6428 const gfxRect transformedBounds
= transform
.TransformAndClipBounds(
6429 gfxRect(overflow
.x
, overflow
.y
, overflow
.width
, overflow
.height
),
6430 gfxRect::MaxIntRect());
6431 const nsSize frameSize
=
6432 nsSize(transformedBounds
.width
, transformedBounds
.height
);
6434 uint64_t maxLimitArea
= uint64_t(maxSize
.width
) * maxSize
.height
;
6435 uint64_t frameArea
= uint64_t(frameSize
.width
) * frameSize
.height
;
6436 if (frameArea
<= maxLimitArea
&& frameSize
<= absoluteLimit
) {
6437 *aDirtyRect
= overflow
;
6438 result
.mDecision
= PrerenderDecision::Full
;
6442 if (ShouldUsePartialPrerender(aFrame
)) {
6443 *aDirtyRect
= nsLayoutUtils::ComputePartialPrerenderArea(
6444 aFrame
, untransformedDirtyRect
, overflow
, maxSize
);
6445 result
.mDecision
= PrerenderDecision::Partial
;
6449 if (frameArea
> maxLimitArea
) {
6450 uint64_t appUnitsPerPixel
= AppUnitsPerCSSPixel();
6451 EffectCompositor::SetPerformanceWarning(
6452 aFrame
, transformSet
,
6453 AnimationPerformanceWarning(
6454 AnimationPerformanceWarning::Type::ContentTooLargeArea
,
6456 int(frameArea
/ (appUnitsPerPixel
* appUnitsPerPixel
)),
6457 int(maxLimitArea
/ (appUnitsPerPixel
* appUnitsPerPixel
)),
6460 EffectCompositor::SetPerformanceWarning(
6461 aFrame
, transformSet
,
6462 AnimationPerformanceWarning(
6463 AnimationPerformanceWarning::Type::ContentTooLarge
,
6465 nsPresContext::AppUnitsToIntCSSPixels(frameSize
.width
),
6466 nsPresContext::AppUnitsToIntCSSPixels(frameSize
.height
),
6467 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit
.width
),
6468 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit
.height
),
6469 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit
.width
),
6470 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit
.height
),
6477 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
6478 * visible or hit. */
6479 static bool IsFrameVisible(nsIFrame
* aFrame
, const Matrix4x4
& aMatrix
) {
6480 if (aMatrix
.IsSingular()) {
6483 if (aFrame
->BackfaceIsHidden() && aMatrix
.IsBackfaceVisible()) {
6489 const Matrix4x4Flagged
& nsDisplayTransform::GetTransform() const {
6494 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6496 if (mHasTransformGetter
) {
6497 mTransform
.emplace((mFrame
->GetTransformGetter())(mFrame
, scale
));
6499 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame
.x
, scale
),
6500 NSAppUnitsToFloatPixels(mToReferenceFrame
.y
, scale
), 0.0f
);
6501 mTransform
->ChangeBasis(newOrigin
.x
, newOrigin
.y
, newOrigin
.z
);
6502 } else if (!mIsTransformSeparator
) {
6503 DebugOnly
<bool> isReference
= mFrame
->IsTransformed() ||
6504 mFrame
->Combines3DTransformWithAncestors() ||
6505 mFrame
->Extend3DContext();
6506 MOZ_ASSERT(isReference
);
6508 GetResultingTransformMatrix(mFrame
, ToReferenceFrame(), scale
,
6509 INCLUDE_PERSPECTIVE
| OFFSET_BY_ORIGIN
));
6511 // Use identity matrix
6512 mTransform
.emplace();
6518 const Matrix4x4Flagged
& nsDisplayTransform::GetInverseTransform() const {
6519 if (mInverseTransform
) {
6520 return *mInverseTransform
;
6523 MOZ_ASSERT(!GetTransform().IsSingular());
6525 mInverseTransform
.emplace(GetTransform().Inverse());
6527 return *mInverseTransform
;
6530 Matrix4x4
nsDisplayTransform::GetTransformForRendering(
6531 LayoutDevicePoint
* aOutOrigin
) const {
6532 if (!mFrame
->HasPerspective() || mHasTransformGetter
||
6533 mIsTransformSeparator
) {
6534 if (!mHasTransformGetter
&& !mIsTransformSeparator
&& aOutOrigin
) {
6535 // If aOutOrigin is provided, put the offset to origin into it, because
6536 // we need to keep it separate for webrender. The combination of
6537 // *aOutOrigin and the returned matrix here should always be equivalent
6538 // to what GetTransform() would have returned.
6539 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6540 *aOutOrigin
= LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale
);
6542 // The rounding behavior should also be the same as GetTransform().
6543 if (nsLayoutUtils::ShouldSnapToGrid(mFrame
)) {
6544 aOutOrigin
->Round();
6546 return GetResultingTransformMatrix(mFrame
, nsPoint(0, 0), scale
,
6547 INCLUDE_PERSPECTIVE
);
6549 return GetTransform().GetMatrix();
6551 MOZ_ASSERT(!mHasTransformGetter
);
6553 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6554 // Don't include perspective transform, or the offset to origin, since
6555 // nsDisplayPerspective will handle both of those.
6556 return GetResultingTransformMatrix(mFrame
, ToReferenceFrame(), scale
, 0);
6559 const Matrix4x4
& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
6560 nsDisplayListBuilder
* aBuilder
) {
6561 MOZ_ASSERT(!mFrame
->Extend3DContext() || IsLeafOf3DContext());
6563 if (!IsLeafOf3DContext()) {
6564 return GetTransform().GetMatrix();
6567 if (!mTransformPreserves3D
) {
6568 const nsIFrame
* establisher
; // Establisher of the 3D rendering context.
6569 for (establisher
= mFrame
;
6570 establisher
&& establisher
->Combines3DTransformWithAncestors();
6572 establisher
->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
6574 const nsIFrame
* establisherReference
= aBuilder
->FindReferenceFrameFor(
6575 nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher
));
6577 nsPoint offset
= establisher
->GetOffsetToCrossDoc(establisherReference
);
6578 float scale
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6580 INCLUDE_PRESERVE3D_ANCESTORS
| INCLUDE_PERSPECTIVE
| OFFSET_BY_ORIGIN
;
6581 mTransformPreserves3D
= MakeUnique
<Matrix4x4
>(
6582 GetResultingTransformMatrix(mFrame
, offset
, scale
, flags
));
6585 return *mTransformPreserves3D
;
6588 bool nsDisplayTransform::CreateWebRenderCommands(
6589 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
6590 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
6591 nsDisplayListBuilder
* aDisplayListBuilder
) {
6592 // We want to make sure we don't pollute the transform property in the WR
6593 // stacking context by including the position of this frame (relative to the
6594 // parent reference frame). We need to keep those separate; the position of
6595 // this frame goes into the stacking context bounds while the transform goes
6596 // into the transform.
6597 LayoutDevicePoint position
;
6598 Matrix4x4 newTransformMatrix
= GetTransformForRendering(&position
);
6600 gfx::Matrix4x4
* transformForSC
= &newTransformMatrix
;
6601 if (newTransformMatrix
.IsIdentity()) {
6602 // If the transform is an identity transform, strip it out so that WR
6603 // doesn't turn this stacking context into a reference frame, as it
6604 // affects positioning. Bug 1345577 tracks a better fix.
6605 transformForSC
= nullptr;
6607 // In ChooseScaleAndSetTransform, we round the offset from the reference
6608 // frame used to adjust the transform, if there is no transform, or it
6609 // is just a translation. We need to do the same here.
6610 if (nsLayoutUtils::ShouldSnapToGrid(mFrame
)) {
6615 auto key
= wr::SpatialKey(uint64_t(mFrame
), GetPerFrameKey(),
6616 wr::SpatialKeyKind::Transform
);
6618 // We don't send animations for transform separator display items.
6619 uint64_t animationsId
=
6620 mIsTransformSeparator
6622 : AddAnimationsForWebRender(
6623 this, aManager
, aDisplayListBuilder
,
6624 IsPartialPrerender() ? Some(position
) : Nothing());
6625 wr::WrAnimationProperty prop
{wr::WrAnimationType::Transform
, animationsId
,
6628 nsDisplayTransform
* deferredTransformItem
= nullptr;
6629 if (ShouldDeferTransform()) {
6630 // If it has perspective, we create a new scroll data via the
6631 // UpdateScrollData call because that scenario is more complex. Otherwise,
6632 // if we don't contain any ASRs then just stash the transform on the
6633 // StackingContextHelper and apply it to any scroll data that are created
6634 // inside this nsDisplayTransform.
6635 deferredTransformItem
= this;
6638 // Determine if we're possibly animated (= would need an active layer in FLB).
6639 bool animated
= !mIsTransformSeparator
&&
6640 ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
6642 wr::StackingContextParams params
;
6643 params
.mBoundTransform
= &newTransformMatrix
;
6644 params
.animation
= animationsId
? &prop
: nullptr;
6646 wr::WrTransformInfo transform_info
;
6647 if (transformForSC
) {
6648 transform_info
.transform
= wr::ToLayoutTransform(newTransformMatrix
);
6649 transform_info
.key
= key
;
6650 params
.mTransformPtr
= &transform_info
;
6652 params
.mTransformPtr
= nullptr;
6655 params
.prim_flags
= !BackfaceIsHidden()
6656 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
6657 : wr::PrimitiveFlags
{0};
6658 params
.paired_with_perspective
= mHasAssociatedPerspective
;
6659 params
.mDeferredTransformItem
= deferredTransformItem
;
6660 params
.mAnimated
= animated
;
6661 // Determine if we would have to rasterize any items in local raster space
6662 // (i.e. disable subpixel AA). We don't always need to rasterize locally even
6663 // if the stacking context is possibly animated (at the cost of potentially
6664 // some false negatives with respect to will-change handling), so we pass in
6665 // this determination separately to accurately match with when FLB would
6666 // normally disable subpixel AA.
6667 params
.mRasterizeLocally
= animated
&& Frame()->HasAnimationOfTransform();
6668 params
.SetPreserve3D(mFrame
->Extend3DContext() && !mIsTransformSeparator
);
6670 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
6672 LayoutDeviceSize boundsSize
= LayoutDeviceSize::FromAppUnits(
6673 mChildBounds
.Size(), mFrame
->PresContext()->AppUnitsPerDevPixel());
6675 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
6676 params
, LayoutDeviceRect(position
, boundsSize
));
6678 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6679 GetChildren(), this, aDisplayListBuilder
, sc
, aBuilder
, aResources
);
6683 bool nsDisplayTransform::UpdateScrollData(
6684 WebRenderScrollData
* aData
, WebRenderLayerScrollData
* aLayerData
) {
6685 if (ShouldDeferTransform()) {
6686 // This case is handled in CreateWebRenderCommands by stashing the transform
6687 // on the stacking context.
6691 aLayerData
->SetTransform(GetTransform().GetMatrix());
6692 aLayerData
->SetTransformIsPerspective(mFrame
->ChildrenHavePerspective());
6697 bool nsDisplayTransform::ShouldSkipTransform(
6698 nsDisplayListBuilder
* aBuilder
) const {
6699 return (aBuilder
->RootReferenceFrame() == mFrame
) &&
6700 aBuilder
->IsForGenerateGlyphMask();
6703 void nsDisplayTransform::Collect3DTransformLeaves(
6704 nsDisplayListBuilder
* aBuilder
, nsTArray
<nsDisplayTransform
*>& aLeaves
) {
6705 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6706 aLeaves
.AppendElement(this);
6710 FlattenedDisplayListIterator
iter(aBuilder
, &mChildren
);
6711 while (iter
.HasNext()) {
6712 nsDisplayItem
* item
= iter
.GetNextItem();
6713 if (item
->GetType() == DisplayItemType::TYPE_PERSPECTIVE
) {
6714 auto* perspective
= static_cast<nsDisplayPerspective
*>(item
);
6715 if (!perspective
->GetChildren()->GetTop()) {
6718 item
= perspective
->GetChildren()->GetTop();
6720 if (item
->GetType() != DisplayItemType::TYPE_TRANSFORM
) {
6721 gfxCriticalError() << "Invalid child item within 3D transform of type: "
6725 static_cast<nsDisplayTransform
*>(item
)->Collect3DTransformLeaves(aBuilder
,
6730 static RefPtr
<gfx::Path
> BuildPathFromPolygon(const RefPtr
<DrawTarget
>& aDT
,
6731 const gfx::Polygon
& aPolygon
) {
6732 MOZ_ASSERT(!aPolygon
.IsEmpty());
6734 RefPtr
<PathBuilder
> pathBuilder
= aDT
->CreatePathBuilder();
6735 const nsTArray
<Point4D
>& points
= aPolygon
.GetPoints();
6737 pathBuilder
->MoveTo(points
[0].As2DPoint());
6739 for (size_t i
= 1; i
< points
.Length(); ++i
) {
6740 pathBuilder
->LineTo(points
[i
].As2DPoint());
6743 pathBuilder
->Close();
6744 return pathBuilder
->Finish();
6747 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6748 nsDisplayListBuilder
* aBuilder
, nsTArray
<TransformPolygon
>& aLeaves
) {
6749 std::list
<TransformPolygon
> inputLayers
;
6751 nsTArray
<nsDisplayTransform
*> leaves
;
6752 Collect3DTransformLeaves(aBuilder
, leaves
);
6753 for (nsDisplayTransform
* item
: leaves
) {
6754 auto bounds
= LayoutDeviceRect::FromAppUnits(
6755 item
->mChildBounds
, item
->mFrame
->PresContext()->AppUnitsPerDevPixel());
6756 Matrix4x4 transform
= item
->GetAccumulatedPreserved3DTransform(aBuilder
);
6758 if (!IsFrameVisible(item
->mFrame
, transform
)) {
6761 gfx::Polygon polygon
=
6762 gfx::Polygon::FromRect(gfx::Rect(bounds
.ToUnknownRect()));
6764 polygon
.TransformToScreenSpace(transform
);
6766 if (polygon
.GetPoints().Length() >= 3) {
6767 inputLayers
.push_back(TransformPolygon(item
, std::move(polygon
)));
6771 if (inputLayers
.empty()) {
6775 BSPTree
<nsDisplayTransform
> tree(inputLayers
);
6776 nsTArray
<TransformPolygon
> orderedLayers(tree
.GetDrawOrder());
6778 for (TransformPolygon
& polygon
: orderedLayers
) {
6780 polygon
.data
->GetAccumulatedPreserved3DTransform(aBuilder
).Inverse();
6782 MOZ_ASSERT(polygon
.geometry
);
6783 polygon
.geometry
->TransformToLayerSpace(inverse
);
6786 aLeaves
= std::move(orderedLayers
);
6789 void nsDisplayTransform::Paint(nsDisplayListBuilder
* aBuilder
,
6791 Paint(aBuilder
, aCtx
, Nothing());
6794 void nsDisplayTransform::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
6795 const Maybe
<gfx::Polygon
>& aPolygon
) {
6796 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6797 MOZ_ASSERT(!aPolygon
);
6798 nsTArray
<TransformPolygon
> leaves
;
6799 CollectSorted3DTransformLeaves(aBuilder
, leaves
);
6800 for (TransformPolygon
& item
: leaves
) {
6801 item
.data
->Paint(aBuilder
, aCtx
, item
.geometry
);
6806 gfxContextMatrixAutoSaveRestore
saveMatrix(aCtx
);
6807 Matrix4x4 trans
= ShouldSkipTransform(aBuilder
)
6809 : GetAccumulatedPreserved3DTransform(aBuilder
);
6810 if (!IsFrameVisible(mFrame
, trans
)) {
6815 if (trans
.CanDraw2D(&trans2d
)) {
6816 aCtx
->Multiply(ThebesMatrix(trans2d
));
6819 RefPtr
<gfx::Path
> path
=
6820 BuildPathFromPolygon(aCtx
->GetDrawTarget(), *aPolygon
);
6821 aCtx
->GetDrawTarget()->PushClip(path
);
6824 GetChildren()->Paint(aBuilder
, aCtx
,
6825 mFrame
->PresContext()->AppUnitsPerDevPixel());
6828 aCtx
->GetDrawTarget()->PopClip();
6833 // TODO: Implement 3d transform handling, including plane splitting and
6834 // sorting. See BasicCompositor.
6835 auto pixelBounds
= LayoutDeviceRect::FromAppUnitsToOutside(
6836 mChildBounds
, mFrame
->PresContext()->AppUnitsPerDevPixel());
6837 RefPtr
<DrawTarget
> untransformedDT
=
6838 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6839 IntSize(pixelBounds
.Width(), pixelBounds
.Height()),
6840 SurfaceFormat::B8G8R8A8
, true);
6841 if (!untransformedDT
|| !untransformedDT
->IsValid()) {
6844 untransformedDT
->SetTransform(
6845 Matrix::Translation(-Point(pixelBounds
.X(), pixelBounds
.Y())));
6847 gfxContext
groupTarget(untransformedDT
, /* aPreserveTransform */ true);
6850 RefPtr
<gfx::Path
> path
=
6851 BuildPathFromPolygon(aCtx
->GetDrawTarget(), *aPolygon
);
6852 aCtx
->GetDrawTarget()->PushClip(path
);
6855 GetChildren()->Paint(aBuilder
, &groupTarget
,
6856 mFrame
->PresContext()->AppUnitsPerDevPixel());
6859 aCtx
->GetDrawTarget()->PopClip();
6862 RefPtr
<SourceSurface
> untransformedSurf
= untransformedDT
->Snapshot();
6864 trans
.PreTranslate(pixelBounds
.X(), pixelBounds
.Y(), 0);
6865 aCtx
->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf
, trans
);
6868 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder
* aBuilder
) const {
6869 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6870 // completely bypass the main thread for this animation, so it is always
6872 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6873 // already involved so there is less to be gained.
6874 // Therefore we check that the *post-transform* bounds of this item are
6875 // big enough to justify an active layer.
6876 return EffectCompositor::HasAnimationsForCompositor(
6877 mFrame
, DisplayItemType::TYPE_TRANSFORM
) ||
6878 (ActiveLayerTracker::IsTransformAnimated(aBuilder
, mFrame
));
6881 nsRect
nsDisplayTransform::TransformUntransformedBounds(
6882 nsDisplayListBuilder
* aBuilder
, const Matrix4x4Flagged
& aMatrix
) const {
6884 const nsRect untransformedBounds
= GetUntransformedBounds(aBuilder
, &snap
);
6885 // GetTransform always operates in dev pixels.
6886 const float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
6887 return nsLayoutUtils::MatrixTransformRect(untransformedBounds
, aMatrix
,
6892 * Returns the bounds for this transform. The bounds are calculated during
6893 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6895 nsRect
nsDisplayTransform::GetBounds(nsDisplayListBuilder
* aBuilder
,
6896 bool* aSnap
) const {
6901 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder
* aBuilder
) {
6902 MOZ_ASSERT(mFrame
->Extend3DContext() || IsLeafOf3DContext());
6904 /* Some transforms can get empty bounds in 2D, but might get transformed again
6905 * and get non-empty bounds. A simple example of this would be a 180 degree
6906 * rotation getting applied twice.
6907 * We should not depend on transforming bounds level by level.
6909 * This function collects the bounds of this transform and stores it in
6910 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6911 * down and include the bounds of the child transforms.
6912 * The bounds are transformed with the accumulated transformation matrix up to
6913 * the 3D context root coordinate space.
6915 nsDisplayListBuilder::AutoAccumulateTransform
accTransform(aBuilder
);
6916 accTransform
.Accumulate(GetTransform().GetMatrix());
6918 // Do not dive into another 3D context.
6919 if (!IsLeafOf3DContext()) {
6920 for (nsDisplayItem
* i
: *GetChildren()) {
6921 i
->DoUpdateBoundsPreserves3D(aBuilder
);
6925 /* The child transforms that extend 3D context further will have empty bounds,
6926 * so the untransformed bounds here is the bounds of all the non-preserve-3d
6927 * content under this transform.
6929 const nsRect rect
= TransformUntransformedBounds(
6930 aBuilder
, accTransform
.GetCurrentTransform());
6931 aBuilder
->AccumulateRect(rect
);
6934 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6935 nsDisplayListBuilder
* aBuilder
) {
6936 MOZ_ASSERT(mFrame
->Combines3DTransformWithAncestors() ||
6937 IsTransformSeparator());
6938 // Updating is not going through to child 3D context.
6939 ComputeBounds(aBuilder
);
6942 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder
* aBuilder
) {
6943 UpdateUntransformedBounds(aBuilder
);
6945 if (IsTransformSeparator()) {
6946 MOZ_ASSERT(GetTransform().IsIdentity());
6947 mBounds
= mChildBounds
;
6951 if (mFrame
->Extend3DContext()) {
6952 if (!Combines3DTransformWithAncestors()) {
6953 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6954 // collect the bounds from the child transforms.
6955 UpdateBoundsFor3D(aBuilder
);
6957 // With nested 3D transforms, the 2D bounds might not be useful.
6964 MOZ_ASSERT(!mFrame
->Extend3DContext());
6966 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
6967 // but mix-blend-mode relies on having bounds set. See bug 1556956.
6969 // A stand-alone transform.
6970 mBounds
= TransformUntransformedBounds(aBuilder
, GetTransform());
6973 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder
* aBuilder
) {
6974 MOZ_ASSERT(mFrame
->Extend3DContext() &&
6975 !mFrame
->Combines3DTransformWithAncestors() &&
6976 !IsTransformSeparator());
6978 // Always start updating from an establisher of a 3D rendering context.
6979 nsDisplayListBuilder::AutoAccumulateRect
accRect(aBuilder
);
6980 nsDisplayListBuilder::AutoAccumulateTransform
accTransform(aBuilder
);
6981 accTransform
.StartRoot();
6982 ComputeBounds(aBuilder
);
6983 mBounds
= aBuilder
->GetAccumulatedRect();
6986 void nsDisplayTransform::UpdateUntransformedBounds(
6987 nsDisplayListBuilder
* aBuilder
) {
6988 mChildBounds
= GetChildren()->GetClippedBoundsWithRespectToASR(
6989 aBuilder
, mActiveScrolledRoot
);
6996 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
6997 void nsDisplayTransform::HitTest(nsDisplayListBuilder
* aBuilder
,
6998 const nsRect
& aRect
, HitTestState
* aState
,
6999 nsTArray
<nsIFrame
*>* aOutFrames
) {
7000 if (aState
->mInPreserves3D
) {
7001 GetChildren()->HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
7005 /* Here's how this works:
7006 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
7008 * 2. Invert the matrix.
7009 * 3. Use it to transform the rect into the correct space.
7010 * 4. Pass that rect down through to the list's version of HitTest.
7012 // GetTransform always operates in dev pixels.
7013 float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7014 Matrix4x4 matrix
= GetAccumulatedPreserved3DTransform(aBuilder
);
7016 if (!IsFrameVisible(mFrame
, matrix
)) {
7020 const bool oldHitOccludingItem
= aState
->mHitOccludingItem
;
7022 /* We want to go from transformed-space to regular space.
7023 * Thus we have to invert the matrix, which normally does
7024 * the reverse operation (e.g. regular->transformed)
7027 /* Now, apply the transform and pass it down the channel. */
7029 nsRect resultingRect
;
7030 // Magic width/height indicating we're hit testing a point, not a rect
7031 const bool testingPoint
= aRect
.width
== 1 && aRect
.height
== 1;
7034 matrix
.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect
.x
, factor
),
7035 NSAppUnitsToFloatPixels(aRect
.y
, factor
)));
7036 if (!point
.HasPositiveWCoord()) {
7040 Point point2d
= point
.As2DPoint();
7043 nsRect(NSFloatPixelsToAppUnits(float(point2d
.x
), factor
),
7044 NSFloatPixelsToAppUnits(float(point2d
.y
), factor
), 1, 1);
7047 Rect
originalRect(NSAppUnitsToFloatPixels(aRect
.x
, factor
),
7048 NSAppUnitsToFloatPixels(aRect
.y
, factor
),
7049 NSAppUnitsToFloatPixels(aRect
.width
, factor
),
7050 NSAppUnitsToFloatPixels(aRect
.height
, factor
));
7052 Rect
childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds
.x
, factor
),
7053 NSAppUnitsToFloatPixels(mChildBounds
.y
, factor
),
7054 NSAppUnitsToFloatPixels(mChildBounds
.width
, factor
),
7055 NSAppUnitsToFloatPixels(mChildBounds
.height
, factor
));
7057 Rect rect
= matrix
.ProjectRectBounds(originalRect
, childGfxBounds
);
7060 nsRect(NSFloatPixelsToAppUnits(float(rect
.X()), factor
),
7061 NSFloatPixelsToAppUnits(float(rect
.Y()), factor
),
7062 NSFloatPixelsToAppUnits(float(rect
.Width()), factor
),
7063 NSFloatPixelsToAppUnits(float(rect
.Height()), factor
));
7066 if (resultingRect
.IsEmpty()) {
7071 printf("Frame: %p\n", dynamic_cast<void*>(mFrame
));
7072 printf(" Untransformed point: (%f, %f)\n", resultingRect
.X(),
7074 uint32_t originalFrameCount
= aOutFrames
.Length();
7077 GetChildren()->HitTest(aBuilder
, resultingRect
, aState
, aOutFrames
);
7079 if (aState
->mHitOccludingItem
&& !testingPoint
&&
7080 !mChildBounds
.Contains(aRect
)) {
7081 MOZ_ASSERT(aBuilder
->HitTestIsForVisibility());
7082 // We're hit-testing a rect that's bigger than our child bounds, but
7083 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so
7084 // we can't stop hit-testing altogether.
7086 // FIXME(emilio): I think this means that theoretically we might include
7087 // some frames fully behind other transformed-but-opaque frames? Then again
7088 // that's our pre-existing behavior for other untransformed content that
7089 // doesn't fill the whole rect. To be fully correct I think we'd need proper
7090 // "known occluded region" tracking, but that might be overkill for our
7092 aState
->mHitOccludingItem
= oldHitOccludingItem
;
7096 if (originalFrameCount
!= aOutFrames
.Length())
7097 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7098 dynamic_cast<void*>(aOutFrames
.ElementAt(0)));
7099 printf("=== end of hit test ===\n");
7103 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder
* aBuilder
,
7104 const nsPoint
& aPoint
) {
7105 // GetTransform always operates in dev pixels.
7106 float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7107 Matrix4x4 matrix
= GetAccumulatedPreserved3DTransform(aBuilder
);
7109 NS_ASSERTION(IsFrameVisible(mFrame
, matrix
),
7110 "We can't have hit a frame that isn't visible!");
7112 Matrix4x4 inverse
= matrix
;
7115 inverse
.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint
.x
, factor
),
7116 NSAppUnitsToFloatPixels(aPoint
.y
, factor
)));
7118 Point point2d
= point
.As2DPoint();
7120 Point3D transformed
= matrix
.TransformPoint(Point3D(point2d
.x
, point2d
.y
, 0));
7121 return transformed
.z
;
7124 /* The transform is opaque iff the transform consists solely of scales and
7125 * translations and if the underlying content is opaque. Thus if the transform
7132 * We need b and c to be zero.
7134 * We also need to check whether the underlying opaque content completely fills
7135 * our visible rect. We use UntransformRect which expands to the axis-aligned
7136 * bounding rect, but that's OK since if
7137 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7138 * certainly contains the actual (non-axis-aligned) untransformed rect.
7140 nsRegion
nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
7141 bool* aSnap
) const {
7144 nsRect untransformedVisible
;
7145 if (!UntransformBuildingRect(aBuilder
, &untransformedVisible
)) {
7149 const Matrix4x4Flagged
& matrix
= GetTransform();
7151 if (!matrix
.Is2D(&matrix2d
) || !matrix2d
.PreservesAxisAlignedRectangles()) {
7158 const nsRect bounds
= GetUntransformedBounds(aBuilder
, &tmpSnap
);
7159 const nsRegion opaque
=
7160 ::mozilla::GetOpaqueRegion(aBuilder
, GetChildren(), bounds
);
7162 if (opaque
.Contains(untransformedVisible
)) {
7163 result
= GetBuildingRect().Intersect(GetBounds(aBuilder
, &tmpSnap
));
7168 nsRect
nsDisplayTransform::GetComponentAlphaBounds(
7169 nsDisplayListBuilder
* aBuilder
) const {
7170 if (GetChildren()->GetComponentAlphaBounds(aBuilder
).IsEmpty()) {
7175 return GetBounds(aBuilder
, &snap
);
7178 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7179 * the smallest rectangle (in app space) containing the transformed image of
7180 * that rectangle. That is, it takes the four corners of the rectangle,
7181 * transforms them according to the matrix associated with the specified frame,
7182 * then returns the smallest rectangle containing the four transformed points.
7184 * @param aUntransformedBounds The rectangle (in app units) to transform.
7185 * @param aFrame The frame whose transformation should be applied.
7186 * @param aOrigin The delta from the frame origin to the coordinate space origin
7187 * @return The smallest rectangle containing the image of the transformed
7190 nsRect
nsDisplayTransform::TransformRect(const nsRect
& aUntransformedBounds
,
7191 const nsIFrame
* aFrame
,
7192 TransformReferenceBox
& aRefBox
) {
7193 MOZ_ASSERT(aFrame
, "Can't take the transform based on a null frame!");
7195 float factor
= aFrame
->PresContext()->AppUnitsPerDevPixel();
7197 FrameTransformProperties
props(aFrame
, aRefBox
, factor
);
7198 return nsLayoutUtils::MatrixTransformRect(
7199 aUntransformedBounds
,
7200 GetResultingTransformMatrixInternal(props
, aRefBox
, nsPoint(), factor
,
7201 kTransformRectFlags
),
7205 bool nsDisplayTransform::UntransformRect(const nsRect
& aTransformedBounds
,
7206 const nsRect
& aChildBounds
,
7207 const nsIFrame
* aFrame
,
7209 MOZ_ASSERT(aFrame
, "Can't take the transform based on a null frame!");
7211 float factor
= aFrame
->PresContext()->AppUnitsPerDevPixel();
7212 Matrix4x4 transform
= GetResultingTransformMatrix(aFrame
, nsPoint(), factor
,
7213 kTransformRectFlags
);
7214 return UntransformRect(aTransformedBounds
, aChildBounds
, transform
, factor
,
7218 bool nsDisplayTransform::UntransformRect(const nsRect
& aTransformedBounds
,
7219 const nsRect
& aChildBounds
,
7220 const Matrix4x4
& aMatrix
,
7221 float aAppUnitsPerPixel
,
7223 if (aMatrix
.IsSingular()) {
7228 NSAppUnitsToFloatPixels(aTransformedBounds
.x
, aAppUnitsPerPixel
),
7229 NSAppUnitsToFloatPixels(aTransformedBounds
.y
, aAppUnitsPerPixel
),
7230 NSAppUnitsToFloatPixels(aTransformedBounds
.width
, aAppUnitsPerPixel
),
7231 NSAppUnitsToFloatPixels(aTransformedBounds
.height
, aAppUnitsPerPixel
));
7233 RectDouble
childGfxBounds(
7234 NSAppUnitsToFloatPixels(aChildBounds
.x
, aAppUnitsPerPixel
),
7235 NSAppUnitsToFloatPixels(aChildBounds
.y
, aAppUnitsPerPixel
),
7236 NSAppUnitsToFloatPixels(aChildBounds
.width
, aAppUnitsPerPixel
),
7237 NSAppUnitsToFloatPixels(aChildBounds
.height
, aAppUnitsPerPixel
));
7239 result
= aMatrix
.Inverse().ProjectRectBounds(result
, childGfxBounds
);
7240 *aOutRect
= nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result
),
7245 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder
* aBuilder
,
7246 const nsRect
& aRect
,
7247 nsRect
* aOutRect
) const {
7248 if (GetTransform().IsSingular()) {
7252 // GetTransform always operates in dev pixels.
7253 float factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7254 RectDouble
result(NSAppUnitsToFloatPixels(aRect
.x
, factor
),
7255 NSAppUnitsToFloatPixels(aRect
.y
, factor
),
7256 NSAppUnitsToFloatPixels(aRect
.width
, factor
),
7257 NSAppUnitsToFloatPixels(aRect
.height
, factor
));
7260 nsRect childBounds
= GetUntransformedBounds(aBuilder
, &snap
);
7261 RectDouble
childGfxBounds(
7262 NSAppUnitsToFloatPixels(childBounds
.x
, factor
),
7263 NSAppUnitsToFloatPixels(childBounds
.y
, factor
),
7264 NSAppUnitsToFloatPixels(childBounds
.width
, factor
),
7265 NSAppUnitsToFloatPixels(childBounds
.height
, factor
));
7267 /* We want to untransform the matrix, so invert the transformation first! */
7268 result
= GetInverseTransform().ProjectRectBounds(result
, childGfxBounds
);
7270 *aOutRect
= nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result
), factor
);
7275 void nsDisplayTransform::WriteDebugInfo(std::stringstream
& aStream
) {
7276 aStream
<< GetTransform().GetMatrix();
7277 if (IsTransformSeparator()) {
7278 aStream
<< " transform-separator";
7280 if (IsLeafOf3DContext()) {
7281 aStream
<< " 3d-context-leaf";
7283 if (mFrame
->Extend3DContext()) {
7284 aStream
<< " extends-3d-context";
7286 if (mFrame
->Combines3DTransformWithAncestors()) {
7287 aStream
<< " combines-3d-with-ancestors";
7290 aStream
<< " prerender(";
7291 switch (mPrerenderDecision
) {
7292 case PrerenderDecision::No
:
7295 case PrerenderDecision::Partial
:
7296 aStream
<< "partial";
7298 case PrerenderDecision::Full
:
7303 aStream
<< " childrenBuildingRect" << mChildrenBuildingRect
;
7306 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder
* aBuilder
,
7308 nsDisplayList
* aList
)
7309 : nsPaintedDisplayItem(aBuilder
, aFrame
), mList(aBuilder
) {
7310 mList
.AppendToTop(aList
);
7311 MOZ_ASSERT(mList
.Length() == 1);
7312 MOZ_ASSERT(mList
.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM
);
7315 void nsDisplayPerspective::Paint(nsDisplayListBuilder
* aBuilder
,
7317 // Just directly recurse into children, since we'll include the persepctive
7318 // value in any nsDisplayTransform children.
7319 GetChildren()->Paint(aBuilder
, aCtx
,
7320 mFrame
->PresContext()->AppUnitsPerDevPixel());
7323 nsRegion
nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
7324 bool* aSnap
) const {
7325 if (!GetChildren()->GetTop()) {
7330 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder
, aSnap
);
7333 bool nsDisplayPerspective::CreateWebRenderCommands(
7334 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
7335 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
7336 nsDisplayListBuilder
* aDisplayListBuilder
) {
7337 float appUnitsPerPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7338 Matrix4x4 perspectiveMatrix
;
7339 DebugOnly
<bool> hasPerspective
= nsDisplayTransform::ComputePerspectiveMatrix(
7340 mFrame
, appUnitsPerPixel
, perspectiveMatrix
);
7341 MOZ_ASSERT(hasPerspective
, "Why did we create nsDisplayPerspective?");
7344 * ClipListToRange can remove our child after we were created.
7346 if (!GetChildren()->GetTop()) {
7351 * The resulting matrix is still in the coordinate space of the transformed
7352 * frame. Append a translation to the reference frame coordinates.
7354 nsDisplayTransform
* transform
=
7355 static_cast<nsDisplayTransform
*>(GetChildren()->GetTop());
7358 Point3D(NSAppUnitsToFloatPixels(transform
->ToReferenceFrame().x
,
7360 NSAppUnitsToFloatPixels(transform
->ToReferenceFrame().y
,
7363 Point3D
roundedOrigin(NS_round(newOrigin
.x
), NS_round(newOrigin
.y
), 0);
7365 perspectiveMatrix
.PostTranslate(roundedOrigin
);
7367 nsIFrame
* perspectiveFrame
=
7368 mFrame
->GetClosestFlattenedTreeAncestorPrimaryFrame();
7370 // Passing true here is always correct, since perspective always combines
7371 // transforms with the descendants. However that'd make WR do a lot of work
7372 // that it doesn't really need to do if there aren't other transforms forming
7373 // part of the 3D context.
7375 // WR knows how to treat perspective in that case, so the only thing we need
7376 // to do is to ensure we pass true when we're involved in a 3d context in any
7377 // other way via the transform-style property on either the transformed frame
7378 // or the perspective frame in order to not confuse WR's preserve-3d code in
7381 mFrame
->Extend3DContext() || perspectiveFrame
->Extend3DContext();
7383 wr::StackingContextParams params
;
7385 wr::WrTransformInfo transform_info
;
7386 transform_info
.transform
= wr::ToLayoutTransform(perspectiveMatrix
);
7387 transform_info
.key
= wr::SpatialKey(uint64_t(mFrame
), GetPerFrameKey(),
7388 wr::SpatialKeyKind::Perspective
);
7389 params
.mTransformPtr
= &transform_info
;
7391 params
.reference_frame_kind
= wr::WrReferenceFrameKind::Perspective
;
7392 params
.prim_flags
= !BackfaceIsHidden()
7393 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7394 : wr::PrimitiveFlags
{0};
7395 params
.SetPreserve3D(preserve3D
);
7397 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
7399 Maybe
<uint64_t> scrollingRelativeTo
;
7400 for (const auto* asr
= GetActiveScrolledRoot(); asr
; asr
= asr
->mParent
) {
7401 // In OOP documents, the root scrollable frame of the in-process root
7402 // document is always active, so using IsAncestorFrameCrossDocInProcess
7403 // should be fine here.
7404 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7405 asr
->mScrollableFrame
->GetScrolledFrame(), perspectiveFrame
)) {
7406 scrollingRelativeTo
.emplace(asr
->GetViewId());
7411 // We put the perspective reference frame wrapping the transformed frame,
7412 // even though there may be arbitrarily nested scroll frames in between.
7414 // We need to know how many ancestor scroll-frames are we nested in, in order
7415 // for the async scrolling code in WebRender to calculate the right
7416 // transformation for the perspective contents.
7417 params
.scrolling_relative_to
= scrollingRelativeTo
.ptrOr(nullptr);
7419 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
7422 aManager
->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7423 GetChildren(), this, aDisplayListBuilder
, sc
, aBuilder
, aResources
);
7428 nsDisplayText::nsDisplayText(nsDisplayListBuilder
* aBuilder
,
7429 nsTextFrame
* aFrame
)
7430 : nsPaintedDisplayItem(aBuilder
, aFrame
),
7433 MOZ_COUNT_CTOR(nsDisplayText
);
7434 mBounds
= mFrame
->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7436 mBounds
.Inflate(mFrame
->PresContext()->AppUnitsPerDevPixel());
7437 mVisibleRect
= aBuilder
->GetVisibleRect() +
7438 aBuilder
->GetCurrentFrameOffsetToReferenceFrame();
7441 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager
* aManager
,
7442 nsDisplayListBuilder
* aBuilder
) const {
7443 auto* f
= static_cast<nsTextFrame
*>(mFrame
);
7445 if (f
->IsSelected()) {
7449 const nsStyleText
* textStyle
= f
->StyleText();
7450 if (textStyle
->HasTextShadow()) {
7454 nsTextFrame::TextDecorations decorations
;
7455 f
->GetTextDecorations(f
->PresContext(), nsTextFrame::eResolvedColors
,
7457 return !decorations
.HasDecorationLines();
7460 void nsDisplayText::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
7461 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS
);
7462 // We don't pass mVisibleRect here, since this can be called from within
7463 // the WebRender fallback painting path, and we don't want to issue
7464 // recorded commands that are dependent on the visible/building rect.
7465 RenderToContext(aCtx
, aBuilder
, GetPaintRect(aBuilder
, aCtx
));
7467 auto* textFrame
= static_cast<nsTextFrame
*>(mFrame
);
7468 LCPTextFrameHelper::MaybeUnionTextFrame(textFrame
,
7469 mBounds
- ToReferenceFrame());
7472 bool nsDisplayText::CreateWebRenderCommands(
7473 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
7474 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
7475 nsDisplayListBuilder
* aDisplayListBuilder
) {
7476 auto* f
= static_cast<nsTextFrame
*>(mFrame
);
7477 auto appUnitsPerDevPixel
= f
->PresContext()->AppUnitsPerDevPixel();
7479 nsRect bounds
= f
->WebRenderBounds() + ToReferenceFrame();
7481 bounds
.Inflate(appUnitsPerDevPixel
);
7483 if (bounds
.IsEmpty()) {
7487 // For large font sizes, punt to a blob image, to avoid the blurry rendering
7488 // that results from WR clamping the glyph size used for rasterization.
7490 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7492 // This is not strictly accurate, as final used font sizes might not be the
7493 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7494 // altering the used size of the font actually used).
7495 // It also fails to consider how transforms might affect the device-font-size
7496 // that webrender uses (and clamps).
7497 // But it should be near enough for practical purposes; the limitations just
7498 // mean we might sometimes end up with webrender still applying some bitmap
7499 // scaling, or bail out when we didn't really need to.
7500 constexpr float kWebRenderFontSizeLimit
= 320.0;
7501 f
->EnsureTextRun(nsTextFrame::eInflated
);
7502 gfxTextRun
* textRun
= f
->GetTextRun(nsTextFrame::eInflated
);
7504 textRun
->GetFontGroup()->GetStyle()->size
> kWebRenderFontSizeLimit
) {
7508 gfx::Point deviceOffset
=
7509 LayoutDevicePoint::FromAppUnits(bounds
.TopLeft(), appUnitsPerDevPixel
)
7512 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7513 // frames) lets us early reject a bunch of things.
7514 nsRect visible
= mVisibleRect
;
7516 // Add the "source rect" area from which the given shadows could intersect
7517 // with mVisibleRect, and which therefore needs to included in the paint
7518 // operation, to the `visible` rect that we will use to limit the bounds of
7519 // what we send to the renderer.
7520 auto addShadowSourceToVisible
= [&](Span
<const StyleSimpleShadow
> aShadows
) {
7521 for (const auto& shadow
: aShadows
) {
7522 nsRect sourceRect
= mVisibleRect
;
7523 // Negate the offsets, because we're looking for the "source" rect that
7524 // could cast a shadow into the visible rect, rather than a "target" area
7525 // onto which the visible rect would cast a shadow.
7526 sourceRect
.MoveBy(-shadow
.horizontal
.ToAppUnits(),
7527 -shadow
.vertical
.ToAppUnits());
7528 // Inflate to account for the shadow blur.
7529 sourceRect
.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
7530 shadow
.blur
.ToAppUnits(), appUnitsPerDevPixel
));
7531 visible
.OrWith(sourceRect
);
7535 // Shadows can translate things back into view, so we enlarge the notional
7536 // "visible" rect to ensure we don't skip painting relevant parts that might
7537 // cast a shadow within the visible area.
7538 addShadowSourceToVisible(f
->StyleText()->mTextShadow
.AsSpan());
7540 // Similarly for shadows that may be cast by ::selection.
7541 if (f
->IsSelected()) {
7542 nsTextPaintStyle
textPaint(f
);
7543 Span
<const StyleSimpleShadow
> shadows
;
7544 f
->GetSelectionTextShadow(SelectionType::eNormal
, textPaint
, &shadows
);
7545 addShadowSourceToVisible(shadows
);
7548 // Inflate a little extra to allow for potential antialiasing "blur".
7549 visible
.Inflate(3 * appUnitsPerDevPixel
);
7550 bounds
= bounds
.Intersect(visible
);
7552 gfxContext
* textDrawer
= aBuilder
.GetTextContext(aResources
, aSc
, aManager
,
7553 this, bounds
, deviceOffset
);
7555 LCPTextFrameHelper::MaybeUnionTextFrame(f
, bounds
- ToReferenceFrame());
7557 aBuilder
.StartGroup(this);
7559 RenderToContext(textDrawer
, aDisplayListBuilder
, mVisibleRect
,
7560 aBuilder
.GetInheritedOpacity(), true);
7561 const bool result
= textDrawer
->GetTextDrawer()->Finish();
7564 aBuilder
.FinishGroup();
7566 aBuilder
.CancelGroup(true);
7572 void nsDisplayText::RenderToContext(gfxContext
* aCtx
,
7573 nsDisplayListBuilder
* aBuilder
,
7574 const nsRect
& aVisibleRect
, float aOpacity
,
7575 bool aIsRecording
) {
7576 nsTextFrame
* f
= static_cast<nsTextFrame
*>(mFrame
);
7578 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7579 // antialiased pixels beyond the measured text extents.
7580 // This is temporary until we do this in the actual calculation of text
7582 auto A2D
= mFrame
->PresContext()->AppUnitsPerDevPixel();
7583 LayoutDeviceRect extraVisible
=
7584 LayoutDeviceRect::FromAppUnits(aVisibleRect
, A2D
);
7585 extraVisible
.Inflate(1);
7587 gfxRect
pixelVisible(extraVisible
.x
, extraVisible
.y
, extraVisible
.width
,
7588 extraVisible
.height
);
7589 pixelVisible
.Inflate(2);
7590 pixelVisible
.RoundOut();
7592 gfxClipAutoSaveRestore
autoSaveClip(aCtx
);
7593 if (!aBuilder
->IsForGenerateGlyphMask() && !aIsRecording
) {
7594 autoSaveClip
.Clip(pixelVisible
);
7597 NS_ASSERTION(mVisIStartEdge
>= 0, "illegal start edge");
7598 NS_ASSERTION(mVisIEndEdge
>= 0, "illegal end edge");
7600 gfxContextMatrixAutoSaveRestore matrixSR
;
7602 nsPoint framePt
= ToReferenceFrame();
7603 if (f
->Style()->IsTextCombined()) {
7604 float scaleFactor
= nsTextFrame::GetTextCombineScaleFactor(f
);
7605 if (scaleFactor
!= 1.0f
) {
7606 if (auto* textDrawer
= aCtx
->GetTextDrawer()) {
7607 // WebRender doesn't support scaling text like this yet
7608 textDrawer
->FoundUnsupportedFeature();
7611 matrixSR
.SetContext(aCtx
);
7612 // Setup matrix to compress text for text-combine-upright if
7613 // necessary. This is done here because we want selection be
7614 // compressed at the same time as text.
7615 gfxPoint pt
= nsLayoutUtils::PointToGfxPoint(framePt
, A2D
);
7616 gfxTextRun
* textRun
= f
->GetTextRun(nsTextFrame::eInflated
);
7617 if (textRun
&& textRun
->IsRightToLeft()) {
7618 pt
.x
+= gfxFloat(f
->GetSize().width
) / A2D
;
7620 gfxMatrix mat
= aCtx
->CurrentMatrixDouble()
7622 .PreScale(scaleFactor
, 1.0)
7624 aCtx
->SetMatrixDouble(mat
);
7627 nsTextFrame::PaintTextParams
params(aCtx
);
7628 params
.framePt
= gfx::Point(framePt
.x
, framePt
.y
);
7629 params
.dirtyRect
= extraVisible
;
7631 if (aBuilder
->IsForGenerateGlyphMask()) {
7632 params
.state
= nsTextFrame::PaintTextParams::GenerateTextMask
;
7634 params
.state
= nsTextFrame::PaintTextParams::PaintText
;
7637 f
->PaintText(params
, mVisIStartEdge
, mVisIEndEdge
, ToReferenceFrame(),
7638 f
->IsSelected(), aOpacity
);
7641 // This could go to nsDisplayListInvalidation.h, but
7642 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7643 // would produce circular dependencies.
7644 class nsDisplayTextGeometry
: public nsDisplayItemGenericGeometry
{
7646 nsDisplayTextGeometry(nsDisplayText
* aItem
, nsDisplayListBuilder
* aBuilder
)
7647 : nsDisplayItemGenericGeometry(aItem
, aBuilder
),
7648 mVisIStartEdge(aItem
->VisIStartEdge()),
7649 mVisIEndEdge(aItem
->VisIEndEdge()) {
7650 nsTextFrame
* f
= static_cast<nsTextFrame
*>(aItem
->Frame());
7651 f
->GetTextDecorations(f
->PresContext(), nsTextFrame::eResolvedColors
,
7656 * We store the computed text decorations here since they are
7657 * computed using style data from parent frames. Any changes to these
7658 * styles will only invalidate the parent frame and not this frame.
7660 nsTextFrame::TextDecorations mDecorations
;
7661 nscoord mVisIStartEdge
;
7662 nscoord mVisIEndEdge
;
7665 nsDisplayItemGeometry
* nsDisplayText::AllocateGeometry(
7666 nsDisplayListBuilder
* aBuilder
) {
7667 return new nsDisplayTextGeometry(this, aBuilder
);
7670 void nsDisplayText::ComputeInvalidationRegion(
7671 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
7672 nsRegion
* aInvalidRegion
) const {
7673 const nsDisplayTextGeometry
* geometry
=
7674 static_cast<const nsDisplayTextGeometry
*>(aGeometry
);
7675 nsTextFrame
* f
= static_cast<nsTextFrame
*>(mFrame
);
7677 nsTextFrame::TextDecorations decorations
;
7678 f
->GetTextDecorations(f
->PresContext(), nsTextFrame::eResolvedColors
,
7682 const nsRect
& newRect
= geometry
->mBounds
;
7683 nsRect oldRect
= GetBounds(aBuilder
, &snap
);
7684 if (decorations
!= geometry
->mDecorations
||
7685 mVisIStartEdge
!= geometry
->mVisIStartEdge
||
7686 mVisIEndEdge
!= geometry
->mVisIEndEdge
||
7687 !oldRect
.IsEqualInterior(newRect
) ||
7688 !geometry
->mBorderRect
.IsEqualInterior(GetBorderRect())) {
7689 aInvalidRegion
->Or(oldRect
, newRect
);
7693 void nsDisplayText::WriteDebugInfo(std::stringstream
& aStream
) {
7697 nsTextFrame
* f
= static_cast<nsTextFrame
*>(mFrame
);
7701 aStream
<< buf
.get() << "\")";
7705 nsDisplayEffectsBase::nsDisplayEffectsBase(
7706 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
7707 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aClearClipChain
)
7708 : nsDisplayWrapList(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
,
7710 MOZ_COUNT_CTOR(nsDisplayEffectsBase
);
7713 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder
* aBuilder
,
7715 nsDisplayList
* aList
)
7716 : nsDisplayWrapList(aBuilder
, aFrame
, aList
) {
7717 MOZ_COUNT_CTOR(nsDisplayEffectsBase
);
7720 nsRegion
nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
7721 bool* aSnap
) const {
7726 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder
* aBuilder
,
7727 const nsRect
& aRect
, HitTestState
* aState
,
7728 nsTArray
<nsIFrame
*>* aOutFrames
) {
7729 nsPoint
rectCenter(aRect
.x
+ aRect
.width
/ 2, aRect
.y
+ aRect
.height
/ 2);
7730 if (SVGIntegrationUtils::HitTestFrameForEffects(
7731 mFrame
, rectCenter
- ToReferenceFrame())) {
7732 mList
.HitTest(aBuilder
, aRect
, aState
, aOutFrames
);
7736 gfxRect
nsDisplayEffectsBase::BBoxInUserSpace() const {
7737 return SVGUtils::GetBBox(mFrame
);
7740 gfxPoint
nsDisplayEffectsBase::UserSpaceOffset() const {
7741 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame
);
7744 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7745 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
7746 nsRegion
* aInvalidRegion
) const {
7747 const auto* geometry
=
7748 static_cast<const nsDisplaySVGEffectGeometry
*>(aGeometry
);
7750 nsRect bounds
= GetBounds(aBuilder
, &snap
);
7751 if (geometry
->mFrameOffsetToReferenceFrame
!= ToReferenceFrame() ||
7752 geometry
->mUserSpaceOffset
!= UserSpaceOffset() ||
7753 !geometry
->mBBox
.IsEqualInterior(BBoxInUserSpace())) {
7754 // Filter and mask output can depend on the location of the frame's user
7755 // space and on the frame's BBox. We need to invalidate if either of these
7756 // change relative to the reference frame.
7757 // Invalidations from our inactive layer manager are not enough to catch
7758 // some of these cases because filters can produce output even if there's
7759 // nothing in the filter input.
7760 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
7764 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7765 if (mFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
7766 ISVGDisplayableFrame
* svgFrame
= do_QueryFrame(mFrame
);
7770 if (auto* svgElement
= SVGElement::FromNode(mFrame
->GetContent())) {
7771 // The SVG spec says only to draw filters if the element
7772 // has valid dimensions.
7773 return svgElement
->HasValidDimensions();
7781 using PaintFramesParams
= SVGIntegrationUtils::PaintFramesParams
;
7783 static void ComputeMaskGeometry(PaintFramesParams
& aParams
) {
7784 // Properties are added lazily and may have been removed by a restyle, so
7785 // make sure all applicable ones are set again.
7786 nsIFrame
* firstFrame
=
7787 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams
.frame
);
7789 const nsStyleSVGReset
* svgReset
= firstFrame
->StyleSVGReset();
7791 nsTArray
<SVGMaskFrame
*> maskFrames
;
7792 // XXX check return value?
7793 SVGObserverUtils::GetAndObserveMasks(firstFrame
, &maskFrames
);
7795 if (maskFrames
.Length() == 0) {
7799 gfxContext
& ctx
= aParams
.ctx
;
7800 nsIFrame
* frame
= aParams
.frame
;
7802 nsPoint offsetToUserSpace
=
7803 nsLayoutUtils::ComputeOffsetToUserSpace(aParams
.builder
, aParams
.frame
);
7805 auto cssToDevScale
= frame
->PresContext()->CSSToDevPixelScale();
7806 int32_t appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
7808 gfxPoint devPixelOffsetToUserSpace
=
7809 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace
, appUnitsPerDevPixel
);
7811 gfxContextMatrixAutoSaveRestore
matSR(&ctx
);
7812 ctx
.SetMatrixDouble(
7813 ctx
.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace
));
7815 // Convert boaderArea and dirtyRect to user space.
7816 nsRect userSpaceBorderArea
= aParams
.borderArea
- offsetToUserSpace
;
7817 nsRect userSpaceDirtyRect
= aParams
.dirtyRect
- offsetToUserSpace
;
7819 // Union all mask layer rectangles in user space.
7820 LayoutDeviceRect maskInUserSpace
;
7821 for (size_t i
= 0; i
< maskFrames
.Length(); i
++) {
7822 SVGMaskFrame
* maskFrame
= maskFrames
[i
];
7823 LayoutDeviceRect currentMaskSurfaceRect
;
7826 auto rect
= maskFrame
->GetMaskArea(aParams
.frame
);
7827 currentMaskSurfaceRect
=
7828 CSSRect::FromUnknownRect(ToRect(rect
)) * cssToDevScale
;
7830 nsCSSRendering::ImageLayerClipState clipState
;
7831 nsCSSRendering::GetImageLayerClip(
7832 svgReset
->mMask
.mLayers
[i
], frame
, *frame
->StyleBorder(),
7833 userSpaceBorderArea
, userSpaceDirtyRect
,
7834 /* aWillPaintBorder = */ false, appUnitsPerDevPixel
, &clipState
);
7835 currentMaskSurfaceRect
= LayoutDeviceRect::FromUnknownRect(
7836 ToRect(clipState
.mDirtyRectInDevPx
));
7839 maskInUserSpace
= maskInUserSpace
.Union(currentMaskSurfaceRect
);
7842 if (!maskInUserSpace
.IsEmpty()) {
7843 aParams
.maskRect
= Some(maskInUserSpace
);
7845 aParams
.maskRect
= Nothing();
7849 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7850 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
, nsDisplayList
* aList
,
7851 const ActiveScrolledRoot
* aActiveScrolledRoot
, bool aWrapsBackdropFilter
)
7852 : nsDisplayEffectsBase(aBuilder
, aFrame
, aList
, aActiveScrolledRoot
, true),
7853 mWrapsBackdropFilter(aWrapsBackdropFilter
) {
7854 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths
);
7856 nsPresContext
* presContext
= mFrame
->PresContext();
7858 aBuilder
->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE
;
7859 const nsStyleSVGReset
* svgReset
= aFrame
->StyleSVGReset();
7860 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i
, svgReset
->mMask
) {
7861 const auto& layer
= svgReset
->mMask
.mLayers
[i
];
7862 if (!layer
.mImage
.IsResolved()) {
7865 const nsRect
& borderArea
= mFrame
->GetRectRelativeToSelf();
7866 // NOTE(emilio): We only care about the dest rect so we don't bother
7867 // computing a clip.
7868 bool isTransformedFixed
= false;
7869 nsBackgroundLayerState state
= nsCSSRendering::PrepareImageLayer(
7870 presContext
, aFrame
, flags
, borderArea
, borderArea
, layer
,
7871 &isTransformedFixed
);
7872 mDestRects
.AppendElement(state
.mDestArea
);
7876 static bool CanMergeDisplayMaskFrame(nsIFrame
* aFrame
) {
7877 // Do not merge items for box-decoration-break:clone elements,
7878 // since each box should have its own mask in that case.
7879 if (aFrame
->StyleBorder()->mBoxDecorationBreak
==
7880 StyleBoxDecorationBreak::Clone
) {
7884 // Do not merge if either frame has a mask. Continuation frames should apply
7885 // the mask independently (just like nsDisplayBackgroundImage).
7886 if (aFrame
->StyleSVGReset()->HasMask()) {
7893 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem
* aItem
) const {
7894 // Items for the same content element should be merged into a single
7895 // compositing group.
7896 if (!HasDifferentFrame(aItem
) || !HasSameTypeAndClip(aItem
) ||
7897 !HasSameContent(aItem
)) {
7901 return CanMergeDisplayMaskFrame(mFrame
) &&
7902 CanMergeDisplayMaskFrame(aItem
->Frame());
7905 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7906 if (!ValidateSVGFrame()) {
7910 return SVGUtils::DetermineMaskUsage(mFrame
, false).UsingMaskOrClipPath();
7913 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder
* aBuilder
,
7914 gfxContext
* aMaskContext
,
7915 bool aHandleOpacity
,
7916 bool* aMaskPainted
) {
7917 MOZ_ASSERT(aMaskContext
->GetDrawTarget()->GetFormat() == SurfaceFormat::A8
);
7919 imgDrawingParams
imgParams(aBuilder
->GetImageDecodeFlags());
7920 nsRect borderArea
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
7921 PaintFramesParams
params(*aMaskContext
, mFrame
, mBounds
, borderArea
, aBuilder
,
7922 aHandleOpacity
, imgParams
);
7923 ComputeMaskGeometry(params
);
7924 bool maskIsComplete
= false;
7925 bool painted
= SVGIntegrationUtils::PaintMask(params
, maskIsComplete
);
7927 *aMaskPainted
= painted
;
7930 return maskIsComplete
&&
7931 (imgParams
.result
== ImgDrawResult::SUCCESS
||
7932 imgParams
.result
== ImgDrawResult::SUCCESS_NOT_COMPLETE
||
7933 imgParams
.result
== ImgDrawResult::WRONG_SIZE
);
7936 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7937 nsDisplayListBuilder
* aBuilder
, const nsDisplayItemGeometry
* aGeometry
,
7938 nsRegion
* aInvalidRegion
) const {
7939 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder
, aGeometry
,
7942 const auto* geometry
=
7943 static_cast<const nsDisplayMasksAndClipPathsGeometry
*>(aGeometry
);
7945 nsRect bounds
= GetBounds(aBuilder
, &snap
);
7947 if (mDestRects
.Length() != geometry
->mDestRects
.Length()) {
7948 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
7950 for (size_t i
= 0; i
< mDestRects
.Length(); i
++) {
7951 if (!mDestRects
[i
].IsEqualInterior(geometry
->mDestRects
[i
])) {
7952 aInvalidRegion
->Or(bounds
, geometry
->mBounds
);
7959 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7960 nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
7961 const std::function
<void()>& aPaintChildren
) {
7962 // Clip the drawing target by mVisibleRect, which contains the visible
7963 // region of the target frame and its out-of-flow and inflow descendants.
7964 Rect bounds
= NSRectToRect(GetPaintRect(aBuilder
, aCtx
),
7965 mFrame
->PresContext()->AppUnitsPerDevPixel());
7967 gfxClipAutoSaveRestore
autoSaveClip(aCtx
);
7968 autoSaveClip
.Clip(bounds
);
7970 imgDrawingParams
imgParams(aBuilder
->GetImageDecodeFlags());
7971 nsRect borderArea
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
7972 PaintFramesParams
params(*aCtx
, mFrame
, GetPaintRect(aBuilder
, aCtx
),
7973 borderArea
, aBuilder
, false, imgParams
);
7975 ComputeMaskGeometry(params
);
7977 SVGIntegrationUtils::PaintMaskAndClipPath(params
, aPaintChildren
);
7980 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder
* aBuilder
,
7982 if (!IsValidMask()) {
7985 PaintWithContentsPaintCallback(aBuilder
, aCtx
, [&] {
7986 GetChildren()->Paint(aBuilder
, aCtx
,
7987 mFrame
->PresContext()->AppUnitsPerDevPixel());
7991 static Maybe
<wr::WrClipChainId
> CreateSimpleClipRegion(
7992 const nsDisplayMasksAndClipPaths
& aDisplayItem
,
7993 wr::DisplayListBuilder
& aBuilder
) {
7994 nsIFrame
* frame
= aDisplayItem
.Frame();
7995 const auto* style
= frame
->StyleSVGReset();
7996 MOZ_ASSERT(style
->HasClipPath() || style
->HasMask());
7997 if (!SVGUtils::DetermineMaskUsage(frame
, false).IsSimpleClipShape()) {
8001 const auto& clipPath
= style
->mClipPath
;
8002 const auto& shape
= *clipPath
.AsShape()._0
;
8004 auto appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
8005 const nsRect refBox
=
8006 nsLayoutUtils::ComputeClipPathGeometryBox(frame
, clipPath
.AsShape()._1
);
8008 wr::WrClipId clipId
{};
8010 switch (shape
.tag
) {
8011 case StyleBasicShape::Tag::Rect
: {
8013 ShapeUtils::ComputeInsetRect(shape
.AsRect().rect
, refBox
) +
8014 aDisplayItem
.ToReferenceFrame();
8016 nscoord radii
[8] = {0};
8017 if (ShapeUtils::ComputeRectRadii(shape
.AsRect().round
, refBox
, rect
,
8019 clipId
= aBuilder
.DefineRoundedRectClip(
8021 wr::ToComplexClipRegion(rect
, radii
, appUnitsPerDevPixel
));
8023 clipId
= aBuilder
.DefineRectClip(
8024 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8025 rect
, appUnitsPerDevPixel
)));
8030 case StyleBasicShape::Tag::Ellipse
:
8031 case StyleBasicShape::Tag::Circle
: {
8032 nsPoint center
= ShapeUtils::ComputeCircleOrEllipseCenter(shape
, refBox
);
8035 if (shape
.IsEllipse()) {
8036 radii
= ShapeUtils::ComputeEllipseRadii(shape
, center
, refBox
);
8038 nscoord radius
= ShapeUtils::ComputeCircleRadius(shape
, center
, refBox
);
8039 radii
= {radius
, radius
};
8042 nsRect
ellipseRect(aDisplayItem
.ToReferenceFrame() + center
-
8043 nsPoint(radii
.width
, radii
.height
),
8046 nscoord ellipseRadii
[8];
8047 for (const auto corner
: AllPhysicalHalfCorners()) {
8048 ellipseRadii
[corner
] =
8049 HalfCornerIsX(corner
) ? radii
.width
: radii
.height
;
8052 clipId
= aBuilder
.DefineRoundedRectClip(
8053 Nothing(), wr::ToComplexClipRegion(ellipseRect
, ellipseRadii
,
8054 appUnitsPerDevPixel
));
8059 // Please don't add more exceptions, try to find a way to define the clip
8060 // without using a mask image.
8062 // And if you _really really_ need to add an exception, add it to
8063 // SVGUtils::DetermineMaskUsage
8064 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8068 wr::WrClipChainId clipChainId
= aBuilder
.DefineClipChain({clipId
}, true);
8070 return Some(clipChainId
);
8073 static void FillPolygonDataForDisplayItem(
8074 const nsDisplayMasksAndClipPaths
& aDisplayItem
,
8075 nsTArray
<wr::LayoutPoint
>& aPoints
, wr::FillRule
& aFillRule
) {
8076 nsIFrame
* frame
= aDisplayItem
.Frame();
8077 const auto* style
= frame
->StyleSVGReset();
8078 bool isPolygon
= style
->HasClipPath() && style
->mClipPath
.IsShape() &&
8079 style
->mClipPath
.AsShape()._0
->IsPolygon();
8084 const auto& clipPath
= style
->mClipPath
;
8085 const auto& shape
= *clipPath
.AsShape()._0
;
8086 const nsRect refBox
=
8087 nsLayoutUtils::ComputeClipPathGeometryBox(frame
, clipPath
.AsShape()._1
);
8089 // We only fill polygon data for polygons that are below a complexity
8091 nsTArray
<nsPoint
> vertices
=
8092 ShapeUtils::ComputePolygonVertices(shape
, refBox
);
8093 if (vertices
.Length() > wr::POLYGON_CLIP_VERTEX_MAX
) {
8097 auto appUnitsPerDevPixel
= frame
->PresContext()->AppUnitsPerDevPixel();
8099 for (size_t i
= 0; i
< vertices
.Length(); ++i
) {
8100 wr::LayoutPoint point
= wr::ToLayoutPoint(
8101 LayoutDevicePoint::FromAppUnits(vertices
[i
], appUnitsPerDevPixel
));
8102 aPoints
.AppendElement(point
);
8105 aFillRule
= (shape
.AsPolygon().fill
== StyleFillRule::Nonzero
)
8106 ? wr::FillRule::Nonzero
8107 : wr::FillRule::Evenodd
;
8110 static Maybe
<wr::WrClipChainId
> CreateWRClipPathAndMasks(
8111 nsDisplayMasksAndClipPaths
* aDisplayItem
, const LayoutDeviceRect
& aBounds
,
8112 wr::IpcResourceUpdateQueue
& aResources
, wr::DisplayListBuilder
& aBuilder
,
8113 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8114 nsDisplayListBuilder
* aDisplayListBuilder
) {
8115 if (auto clip
= CreateSimpleClipRegion(*aDisplayItem
, aBuilder
)) {
8119 Maybe
<wr::ImageMask
> mask
= aManager
->CommandBuilder().BuildWrMaskImage(
8120 aDisplayItem
, aBuilder
, aResources
, aSc
, aDisplayListBuilder
, aBounds
);
8125 // We couldn't create a simple clip region, but before we create an image
8126 // mask clip, see if we can get a polygon clip to add to it.
8127 nsTArray
<wr::LayoutPoint
> points
;
8128 wr::FillRule fillRule
= wr::FillRule::Nonzero
;
8129 FillPolygonDataForDisplayItem(*aDisplayItem
, points
, fillRule
);
8131 wr::WrClipId clipId
=
8132 aBuilder
.DefineImageMaskClip(mask
.ref(), points
, fillRule
);
8134 wr::WrClipChainId clipChainId
= aBuilder
.DefineClipChain({clipId
}, true);
8136 return Some(clipChainId
);
8139 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8140 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
8141 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8142 nsDisplayListBuilder
* aDisplayListBuilder
) {
8144 auto appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8145 nsRect displayBounds
= GetBounds(aDisplayListBuilder
, &snap
);
8146 LayoutDeviceRect bounds
=
8147 LayoutDeviceRect::FromAppUnits(displayBounds
, appUnitsPerDevPixel
);
8149 Maybe
<wr::WrClipChainId
> clip
= CreateWRClipPathAndMasks(
8150 this, bounds
, aResources
, aBuilder
, aSc
, aManager
, aDisplayListBuilder
);
8152 float oldOpacity
= aBuilder
.GetInheritedOpacity();
8154 Maybe
<StackingContextHelper
> layer
;
8155 const StackingContextHelper
* sc
= &aSc
;
8157 // Create a new stacking context to attach the mask to, ensuring the mask is
8158 // applied to the aggregate, and not the individual elements.
8160 // The stacking context shouldn't have any offset.
8161 bounds
.MoveTo(0, 0);
8163 Maybe
<float> opacity
=
8164 (SVGUtils::DetermineMaskUsage(mFrame
, false).IsSimpleClipShape() &&
8165 aBuilder
.GetInheritedOpacity() != 1.0f
)
8166 ? Some(aBuilder
.GetInheritedOpacity())
8169 wr::StackingContextParams params
;
8170 params
.clip
= wr::WrStackingContextClip::ClipChain(clip
->id
);
8171 params
.opacity
= opacity
.ptrOr(nullptr);
8172 if (mWrapsBackdropFilter
) {
8173 params
.flags
|= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER
;
8175 layer
.emplace(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
, params
,
8180 aBuilder
.SetInheritedOpacity(1.0f
);
8181 const DisplayItemClipChain
* oldClipChain
= aBuilder
.GetInheritedClipChain();
8182 aBuilder
.SetInheritedClipChain(nullptr);
8183 CreateWebRenderCommandsNewClipListOption(aBuilder
, aResources
, *sc
, aManager
,
8184 aDisplayListBuilder
, layer
.isSome());
8185 aBuilder
.SetInheritedOpacity(oldOpacity
);
8186 aBuilder
.SetInheritedClipChain(oldClipChain
);
8191 Maybe
<nsRect
> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8192 nsDisplayListBuilder
* aBuilder
, const ActiveScrolledRoot
* aASR
) const {
8193 if (const DisplayItemClip
* clip
=
8194 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR
)) {
8195 return Some(clip
->GetClipRect());
8197 // This item does not have a clip with respect to |aASR|. However, we
8198 // might still have finite bounds with respect to |aASR|. Check our
8200 nsDisplayList
* childList
= GetSameCoordinateSystemChildren();
8202 return Some(childList
->GetClippedBoundsWithRespectToASR(aBuilder
, aASR
));
8205 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
8210 #ifdef MOZ_DUMP_PAINTING
8211 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString
& aTo
) {
8212 nsIFrame
* firstFrame
=
8213 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame
);
8215 aTo
+= " effects=(";
8216 SVGClipPathFrame
* clipPathFrame
;
8217 // XXX Check return value?
8218 SVGObserverUtils::GetAndObserveClipPath(firstFrame
, &clipPathFrame
);
8219 if (clipPathFrame
) {
8223 aTo
+= nsPrintfCString(
8224 "clip(%s)", clipPathFrame
->IsTrivial() ? "trivial" : "non-trivial");
8226 } else if (mFrame
->StyleSVGReset()->HasClipPath()) {
8230 aTo
+= "clip(basic-shape)";
8234 nsTArray
<SVGMaskFrame
*> masks
;
8235 // XXX check return value?
8236 SVGObserverUtils::GetAndObserveMasks(firstFrame
, &masks
);
8237 if (!masks
.IsEmpty() && masks
[0]) {
8247 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8248 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
8249 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8250 nsDisplayListBuilder
* aDisplayListBuilder
) {
8251 WrFiltersHolder wrFilters
;
8252 const ComputedStyle
& style
= mStyle
? *mStyle
: *mFrame
->Style();
8253 auto filterChain
= style
.StyleEffects()->mBackdropFilters
.AsSpan();
8254 bool initialized
= true;
8255 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain
, mFrame
,
8257 !SVGIntegrationUtils::BuildWebRenderFilters(
8258 mFrame
, filterChain
, StyleFilterType::BackdropFilter
, wrFilters
,
8260 // TODO: If painting backdrop-filters on the content side is implemented,
8261 // consider returning false to fall back to that.
8269 nsCSSRendering::ImageLayerClipState clip
;
8270 nsCSSRendering::GetImageLayerClip(
8271 style
.StyleBackground()->BottomLayer(), mFrame
, *style
.StyleBorder(),
8272 mBackdropRect
, mBackdropRect
, false,
8273 mFrame
->PresContext()->AppUnitsPerDevPixel(), &clip
);
8275 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
8276 mBackdropRect
, mFrame
->PresContext()->AppUnitsPerDevPixel());
8278 wr::ComplexClipRegion region
=
8279 wr::ToComplexClipRegion(clip
.mBGClipArea
, clip
.mRadii
,
8280 mFrame
->PresContext()->AppUnitsPerDevPixel());
8282 aBuilder
.PushBackdropFilter(wr::ToLayoutRect(bounds
), region
,
8283 wrFilters
.filters
, wrFilters
.filter_datas
,
8284 !BackfaceIsHidden());
8286 wr::StackingContextParams params
;
8288 wr::WrStackingContextClip::ClipChain(aBuilder
.CurrentClipChainId());
8289 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
8292 nsDisplayWrapList::CreateWebRenderCommands(aBuilder
, aResources
, sc
, aManager
,
8293 aDisplayListBuilder
);
8297 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder
* aBuilder
,
8299 // TODO: Implement backdrop filters
8300 GetChildren()->Paint(aBuilder
, aCtx
,
8301 mFrame
->PresContext()->AppUnitsPerDevPixel());
8304 nsRect
nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder
* aBuilder
,
8305 bool* aSnap
) const {
8306 nsRect childBounds
= nsDisplayWrapList::GetBounds(aBuilder
, aSnap
);
8310 return mBackdropRect
.Union(childBounds
);
8314 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder
* aBuilder
,
8315 nsIFrame
* aFrame
, nsDisplayList
* aList
,
8316 nsIFrame
* aStyleFrame
,
8317 bool aWrapsBackdropFilter
)
8318 : nsDisplayEffectsBase(aBuilder
, aFrame
, aList
),
8319 mStyle(aFrame
== aStyleFrame
? nullptr : aStyleFrame
->Style()),
8320 mEffectsBounds(aFrame
->InkOverflowRectRelativeToSelf()),
8321 mWrapsBackdropFilter(aWrapsBackdropFilter
) {
8322 MOZ_COUNT_CTOR(nsDisplayFilters
);
8323 mVisibleRect
= aBuilder
->GetVisibleRect() +
8324 aBuilder
->GetCurrentFrameOffsetToReferenceFrame();
8327 void nsDisplayFilters::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
8328 PaintWithContentsPaintCallback(aBuilder
, aCtx
, [&](gfxContext
* aContext
) {
8329 GetChildren()->Paint(aBuilder
, aContext
,
8330 mFrame
->PresContext()->AppUnitsPerDevPixel());
8334 void nsDisplayFilters::PaintWithContentsPaintCallback(
8335 nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
,
8336 const std::function
<void(gfxContext
* aContext
)>& aPaintChildren
) {
8337 imgDrawingParams
imgParams(aBuilder
->GetImageDecodeFlags());
8338 nsRect borderArea
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
8339 PaintFramesParams
params(*aCtx
, mFrame
, mVisibleRect
, borderArea
, aBuilder
,
8342 gfxPoint userSpaceToFrameSpaceOffset
=
8343 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame
, params
);
8345 auto filterChain
= mStyle
? mStyle
->StyleEffects()->mFilters
.AsSpan()
8346 : mFrame
->StyleEffects()->mFilters
.AsSpan();
8347 SVGIntegrationUtils::PaintFilter(
8348 params
, filterChain
,
8349 [&](gfxContext
& aContext
, imgDrawingParams
&, const gfxMatrix
*,
8351 gfxContextMatrixAutoSaveRestore
autoSR(&aContext
);
8352 aContext
.SetMatrixDouble(aContext
.CurrentMatrixDouble().PreTranslate(
8353 -userSpaceToFrameSpaceOffset
));
8354 aPaintChildren(&aContext
);
8358 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8359 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame
);
8362 bool nsDisplayFilters::CreateWebRenderCommands(
8363 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
8364 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8365 nsDisplayListBuilder
* aDisplayListBuilder
) {
8366 WrFiltersHolder wrFilters
;
8367 const ComputedStyle
& style
= mStyle
? *mStyle
: *mFrame
->Style();
8368 auto filterChain
= style
.StyleEffects()->mFilters
.AsSpan();
8369 bool initialized
= true;
8370 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain
, mFrame
,
8372 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame
, filterChain
,
8373 StyleFilterType::Filter
,
8374 wrFilters
, initialized
)) {
8376 // TODO(bug 1769223): Support fallback filters in the root code-path,
8377 // perhaps. For now treat it the same way as invalid filters.
8380 // Draw using fallback.
8386 // https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
8388 // If the filter references a non-existent object or the referenced object
8389 // is not a filter element, then the whole filter chain is ignored. No
8390 // filter is applied to the object.
8392 // Note that other engines have a weird discrepancy between SVG and HTML
8393 // content here, but the spec is clear.
8397 uint64_t clipChainId
;
8398 if (wrFilters
.post_filters_clip
) {
8399 auto devPxRect
= LayoutDeviceRect::FromAppUnits(
8400 wrFilters
.post_filters_clip
.value() + ToReferenceFrame(),
8401 mFrame
->PresContext()->AppUnitsPerDevPixel());
8403 aBuilder
.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect
));
8404 clipChainId
= aBuilder
.DefineClipChain({clipId
}, true).id
;
8406 clipChainId
= aBuilder
.CurrentClipChainId();
8408 wr::WrStackingContextClip clip
=
8409 wr::WrStackingContextClip::ClipChain(clipChainId
);
8411 float opacity
= aBuilder
.GetInheritedOpacity();
8412 aBuilder
.SetInheritedOpacity(1.0f
);
8413 const DisplayItemClipChain
* oldClipChain
= aBuilder
.GetInheritedClipChain();
8414 aBuilder
.SetInheritedClipChain(nullptr);
8415 wr::StackingContextParams params
;
8416 params
.mFilters
= std::move(wrFilters
.filters
);
8417 params
.mFilterDatas
= std::move(wrFilters
.filter_datas
);
8418 params
.opacity
= opacity
!= 1.0f
? &opacity
: nullptr;
8420 if (mWrapsBackdropFilter
) {
8421 params
.flags
|= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER
;
8423 StackingContextHelper
sc(aSc
, GetActiveScrolledRoot(), mFrame
, this, aBuilder
,
8426 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder
, aResources
, sc
,
8427 aManager
, aDisplayListBuilder
);
8428 aBuilder
.SetInheritedOpacity(opacity
);
8429 aBuilder
.SetInheritedClipChain(oldClipChain
);
8434 #ifdef MOZ_DUMP_PAINTING
8435 void nsDisplayFilters::PrintEffects(nsACString
& aTo
) {
8436 nsIFrame
* firstFrame
=
8437 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame
);
8439 aTo
+= " effects=(";
8440 // We may exist for a mix of CSS filter functions and/or references to SVG
8441 // filters. If we have invalid references to SVG filters then we paint
8442 // nothing, but otherwise we will apply one or more filters.
8443 if (SVGObserverUtils::GetAndObserveFilters(firstFrame
, nullptr) !=
8444 SVGObserverUtils::eHasRefsSomeInvalid
) {
8454 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder
* aBuilder
,
8455 nsIFrame
* aFrame
, nsDisplayList
* aList
)
8456 : nsDisplayWrapList(aBuilder
, aFrame
, aList
) {
8457 MOZ_COUNT_CTOR(nsDisplaySVGWrapper
);
8460 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) {
8461 return !aBuilder
->GetWidgetLayerManager();
8464 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8465 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
8466 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8467 nsDisplayListBuilder
* aDisplayListBuilder
) {
8468 return CreateWebRenderCommandsNewClipListOption(
8469 aBuilder
, aResources
, aSc
, aManager
, aDisplayListBuilder
, false);
8472 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder
* aBuilder
,
8474 nsDisplayList
* aList
)
8475 : nsDisplayWrapList(aBuilder
, aFrame
, aList
) {
8476 MOZ_COUNT_CTOR(nsDisplayForeignObject
);
8479 #ifdef NS_BUILD_REFCNT_LOGGING
8480 nsDisplayForeignObject::~nsDisplayForeignObject() {
8481 MOZ_COUNT_DTOR(nsDisplayForeignObject
);
8485 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) {
8486 return !aBuilder
->GetWidgetLayerManager();
8489 bool nsDisplayForeignObject::CreateWebRenderCommands(
8490 wr::DisplayListBuilder
& aBuilder
, wr::IpcResourceUpdateQueue
& aResources
,
8491 const StackingContextHelper
& aSc
, RenderRootStateManager
* aManager
,
8492 nsDisplayListBuilder
* aDisplayListBuilder
) {
8493 AutoRestore
<bool> restoreDoGrouping(aManager
->CommandBuilder().mDoGrouping
);
8494 aManager
->CommandBuilder().mDoGrouping
= false;
8495 return CreateWebRenderCommandsNewClipListOption(
8496 aBuilder
, aResources
, aSc
, aManager
, aDisplayListBuilder
, false);
8499 void nsDisplayLink::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
8500 auto appPerDev
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8501 aCtx
->GetDrawTarget()->Link(
8502 mLinkSpec
.get(), NSRectToRect(GetPaintRect(aBuilder
, aCtx
), appPerDev
));
8505 void nsDisplayDestination::Paint(nsDisplayListBuilder
* aBuilder
,
8507 auto appPerDev
= mFrame
->PresContext()->AppUnitsPerDevPixel();
8508 aCtx
->GetDrawTarget()->Destination(
8509 mDestinationName
.get(),
8510 NSPointToPoint(GetPaintRect(aBuilder
, aCtx
).TopLeft(), appPerDev
));
8513 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8514 nsDisplayList
* aOutResultList
, nsIContent
* aContent
) {
8515 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
8516 // in content document order and SortByZOrder is a stable sort which
8517 // guarantees that boxes produced by the same element are placed together
8518 // in the sort. Consider a position:relative inline element that breaks
8519 // across lines and has absolutely positioned children; all the abs-pos
8520 // children should be z-ordered after all the boxes for the position:relative
8522 PositionedDescendants()->SortByZOrder();
8524 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8525 // 1,2: backgrounds and borders
8526 aOutResultList
->AppendToTop(BorderBackground());
8527 // 3: negative z-index children.
8528 for (auto* item
: PositionedDescendants()->TakeItems()) {
8529 if (item
->ZIndex() < 0) {
8530 aOutResultList
->AppendToTop(item
);
8532 PositionedDescendants()->AppendToTop(item
);
8536 // 4: block backgrounds
8537 aOutResultList
->AppendToTop(BlockBorderBackgrounds());
8539 aOutResultList
->AppendToTop(Floats());
8540 // 7: general content
8541 aOutResultList
->AppendToTop(Content());
8542 // 7.5: outlines, in content tree order. We need to sort by content order
8543 // because an element with outline that breaks and has children with outline
8544 // might have placed child outline items between its own outline items.
8545 // The element's outline items need to all come before any child outline
8548 Outlines()->SortByContentOrder(aContent
);
8550 aOutResultList
->AppendToTop(Outlines());
8551 // 8, 9: non-negative z-index children
8552 aOutResultList
->AppendToTop(PositionedDescendants());
8555 uint32_t PaintTelemetry::sPaintLevel
= 0;
8557 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8558 // Don't record nested paints.
8559 if (sPaintLevel
++ > 0) {
8563 mStart
= TimeStamp::Now();
8566 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8567 MOZ_ASSERT(sPaintLevel
!= 0);
8568 if (--sPaintLevel
> 0) {
8572 // If we're in multi-process mode, don't include paint times for the parent
8574 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8578 // Record the total time.
8579 mozilla::glean::gfx_content::paint_time
.AccumulateRawDuration(
8580 TimeStamp::Now() - mStart
);
8583 static nsIFrame
* GetSelfOrPlaceholderFor(nsIFrame
* aFrame
) {
8584 if (aFrame
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
8588 if (aFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
) &&
8589 !aFrame
->GetPrevInFlow()) {
8590 return aFrame
->GetPlaceholderFrame();
8596 static nsIFrame
* GetAncestorFor(nsIFrame
* aFrame
) {
8597 nsIFrame
* f
= GetSelfOrPlaceholderFor(aFrame
);
8599 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f
);
8602 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8603 nsDisplayListBuilder
* aBuilder
, nsIFrame
* aForChild
,
8604 const nsRect
& aVisibleRect
, const nsRect
& aDirtyRect
,
8605 const bool aIsTransformed
)
8606 : mBuilder(aBuilder
),
8607 mPrevFrame(aBuilder
->mCurrentFrame
),
8608 mPrevReferenceFrame(aBuilder
->mCurrentReferenceFrame
),
8609 mPrevOffset(aBuilder
->mCurrentOffsetToReferenceFrame
),
8610 mPrevAdditionalOffset(aBuilder
->mAdditionalOffset
),
8611 mPrevVisibleRect(aBuilder
->mVisibleRect
),
8612 mPrevDirtyRect(aBuilder
->mDirtyRect
),
8613 mPrevCompositorHitTestInfo(aBuilder
->mCompositorHitTestInfo
),
8614 mPrevAncestorHasApzAwareEventHandler(
8615 aBuilder
->mAncestorHasApzAwareEventHandler
),
8616 mPrevBuildingInvisibleItems(aBuilder
->mBuildingInvisibleItems
),
8617 mPrevInInvalidSubtree(aBuilder
->mInInvalidSubtree
) {
8618 if (aIsTransformed
) {
8619 aBuilder
->mCurrentOffsetToReferenceFrame
=
8620 aBuilder
->AdditionalOffset().refOr(nsPoint());
8621 aBuilder
->mCurrentReferenceFrame
= aForChild
;
8622 } else if (aBuilder
->mCurrentFrame
== aForChild
->GetParent()) {
8623 aBuilder
->mCurrentOffsetToReferenceFrame
+= aForChild
->GetPosition();
8625 aBuilder
->mCurrentReferenceFrame
= aBuilder
->FindReferenceFrameFor(
8626 aForChild
, &aBuilder
->mCurrentOffsetToReferenceFrame
);
8629 // If aForChild is being visited from a frame other than it's ancestor frame,
8630 // mInInvalidSubtree will need to be recalculated the slow way.
8631 if (aForChild
== mPrevFrame
|| GetAncestorFor(aForChild
) == mPrevFrame
) {
8632 aBuilder
->mInInvalidSubtree
=
8633 aBuilder
->mInInvalidSubtree
|| aForChild
->IsFrameModified();
8635 aBuilder
->mInInvalidSubtree
= AnyContentAncestorModified(aForChild
);
8638 aBuilder
->mCurrentFrame
= aForChild
;
8639 aBuilder
->mVisibleRect
= aVisibleRect
;
8640 aBuilder
->mDirtyRect
=
8641 aBuilder
->mInInvalidSubtree
? aVisibleRect
: aDirtyRect
;
8644 } // namespace mozilla