Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / painting / nsDisplayList.cpp
blob52e26b9def41b5cb4623c8c17f985c2ba2758e99
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/.
6 */
8 /*
9 * structures that represent things to be painted (ordered in z-order),
10 * used during painting and hit testing
13 #include "nsDisplayList.h"
15 #include <stdint.h>
16 #include <algorithm>
17 #include <limits>
19 #include "gfxContext.h"
20 #include "gfxUtils.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/gfx/2D.h"
32 #include "mozilla/PresShell.h"
33 #include "mozilla/ShapeUtils.h"
34 #include "mozilla/StaticPrefs_apz.h"
35 #include "mozilla/StaticPrefs_gfx.h"
36 #include "mozilla/StaticPrefs_layers.h"
37 #include "mozilla/StaticPrefs_layout.h"
38 #include "mozilla/StaticPrefs_print.h"
39 #include "mozilla/SVGIntegrationUtils.h"
40 #include "mozilla/SVGUtils.h"
41 #include "mozilla/ViewportUtils.h"
42 #include "nsCSSRendering.h"
43 #include "nsCSSRenderingGradients.h"
44 #include "nsCaseTreatment.h"
45 #include "nsRefreshDriver.h"
46 #include "nsRegion.h"
47 #include "nsStyleStructInlines.h"
48 #include "nsStyleTransformMatrix.h"
49 #include "nsTransitionManager.h"
50 #include "gfxMatrix.h"
51 #include "nsLayoutUtils.h"
52 #include "nsIScrollableFrame.h"
53 #include "nsIFrameInlines.h"
54 #include "nsStyleConsts.h"
55 #include "BorderConsts.h"
56 #include "mozilla/MathAlgorithms.h"
58 #include "imgIContainer.h"
59 #include "nsImageFrame.h"
60 #include "nsSubDocumentFrame.h"
61 #include "nsViewManager.h"
62 #include "ImageContainer.h"
63 #include "nsCanvasFrame.h"
64 #include "nsSubDocumentFrame.h"
65 #include "StickyScrollContainer.h"
66 #include "mozilla/AnimationPerformanceWarning.h"
67 #include "mozilla/AnimationUtils.h"
68 #include "mozilla/AutoRestore.h"
69 #include "mozilla/EffectCompositor.h"
70 #include "mozilla/EffectSet.h"
71 #include "mozilla/HashTable.h"
72 #include "mozilla/LookAndFeel.h"
73 #include "mozilla/OperatorNewExtensions.h"
74 #include "mozilla/PendingAnimationTracker.h"
75 #include "mozilla/Preferences.h"
76 #include "mozilla/ProfilerLabels.h"
77 #include "mozilla/ProfilerMarkers.h"
78 #include "mozilla/StyleAnimationValue.h"
79 #include "mozilla/ServoBindings.h"
80 #include "mozilla/SVGClipPathFrame.h"
81 #include "mozilla/SVGMaskFrame.h"
82 #include "mozilla/SVGObserverUtils.h"
83 #include "mozilla/Telemetry.h"
84 #include "mozilla/UniquePtr.h"
85 #include "mozilla/Unused.h"
86 #include "mozilla/ViewportFrame.h"
87 #include "mozilla/gfx/gfxVars.h"
88 #include "ActiveLayerTracker.h"
89 #include "nsEscape.h"
90 #include "nsPrintfCString.h"
91 #include "UnitTransforms.h"
92 #include "LayerAnimationInfo.h"
93 #include "mozilla/EventStateManager.h"
94 #include "nsCaret.h"
95 #include "nsDOMTokenList.h"
96 #include "nsCSSProps.h"
97 #include "nsTableCellFrame.h"
98 #include "nsTableColFrame.h"
99 #include "nsTextFrame.h"
100 #include "nsTextPaintStyle.h"
101 #include "nsSliderFrame.h"
102 #include "nsFocusManager.h"
103 #include "TextDrawTarget.h"
104 #include "mozilla/layers/AnimationHelper.h"
105 #include "mozilla/layers/CompositorThread.h"
106 #include "mozilla/layers/InputAPZContext.h"
107 #include "mozilla/layers/RenderRootStateManager.h"
108 #include "mozilla/layers/StackingContextHelper.h"
109 #include "mozilla/layers/TreeTraversal.h"
110 #include "mozilla/layers/WebRenderBridgeChild.h"
111 #include "mozilla/layers/WebRenderLayerManager.h"
112 #include "mozilla/layers/WebRenderMessages.h"
113 #include "mozilla/layers/WebRenderScrollData.h"
115 namespace mozilla {
117 using namespace dom;
118 using namespace gfx;
119 using namespace layout;
120 using namespace layers;
121 using namespace image;
123 LazyLogModule sContentDisplayListLog("dl.content");
124 LazyLogModule sParentDisplayListLog("dl.parent");
126 LazyLogModule& GetLoggerByProcess() {
127 return XRE_IsContentProcess() ? sContentDisplayListLog
128 : sParentDisplayListLog;
131 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
132 void AssertUniqueItem(nsDisplayItem* aItem) {
133 for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) {
134 if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
135 i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
136 if (i->IsPreProcessedItem() || i->IsPreProcessed()) {
137 continue;
139 MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
143 #endif
145 bool ShouldBuildItemForEvents(const DisplayItemType aType) {
146 return aType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO ||
147 (GetDisplayItemFlagsForType(aType) & TYPE_IS_CONTAINER);
150 static bool ItemTypeSupportsHitTesting(const DisplayItemType aType) {
151 switch (aType) {
152 case DisplayItemType::TYPE_BACKGROUND:
153 case DisplayItemType::TYPE_BACKGROUND_COLOR:
154 case DisplayItemType::TYPE_THEMED_BACKGROUND:
155 return true;
156 default:
157 return false;
161 void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder,
162 nsPaintedDisplayItem* aItem,
163 const DisplayItemType aType) {
164 if (ItemTypeSupportsHitTesting(aType)) {
165 aItem->InitializeHitTestInfo(aBuilder);
169 /* static */
170 already_AddRefed<ActiveScrolledRoot> ActiveScrolledRoot::CreateASRForFrame(
171 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame,
172 bool aIsRetained) {
173 nsIFrame* f = do_QueryFrame(aScrollableFrame);
175 RefPtr<ActiveScrolledRoot> asr;
176 if (aIsRetained) {
177 asr = f->GetProperty(ActiveScrolledRootCache());
180 if (!asr) {
181 asr = new ActiveScrolledRoot();
183 if (aIsRetained) {
184 RefPtr<ActiveScrolledRoot> ref = asr;
185 f->SetProperty(ActiveScrolledRootCache(), ref.forget().take());
188 asr->mParent = aParent;
189 asr->mScrollableFrame = aScrollableFrame;
190 asr->mViewId = Nothing();
191 asr->mDepth = aParent ? aParent->mDepth + 1 : 1;
192 asr->mRetained = aIsRetained;
194 return asr.forget();
197 /* static */
198 bool ActiveScrolledRoot::IsAncestor(const ActiveScrolledRoot* aAncestor,
199 const ActiveScrolledRoot* aDescendant) {
200 if (!aAncestor) {
201 // nullptr is the root
202 return true;
204 if (Depth(aAncestor) > Depth(aDescendant)) {
205 return false;
207 const ActiveScrolledRoot* asr = aDescendant;
208 while (asr) {
209 if (asr == aAncestor) {
210 return true;
212 asr = asr->mParent;
214 return false;
217 /* static */
218 bool ActiveScrolledRoot::IsProperAncestor(
219 const ActiveScrolledRoot* aAncestor,
220 const ActiveScrolledRoot* aDescendant) {
221 return aAncestor != aDescendant && IsAncestor(aAncestor, aDescendant);
224 /* static */
225 nsCString ActiveScrolledRoot::ToString(
226 const ActiveScrolledRoot* aActiveScrolledRoot) {
227 nsAutoCString str;
228 for (const auto* asr = aActiveScrolledRoot; asr; asr = asr->mParent) {
229 str.AppendPrintf("<0x%p>", asr->mScrollableFrame);
230 if (asr->mParent) {
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
263 // used.
264 return 0;
267 RefPtr<WebRenderAnimationData> animationData =
268 aManager->CommandBuilder()
269 .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(aItem);
270 AnimationInfo& animationInfo = animationData->GetAnimationInfo();
271 animationInfo.AddAnimationsForDisplayItem(
272 aItem->Frame(), aDisplayListBuilder, aItem, aItem->GetType(),
273 aManager->LayerManager(), aPosition);
274 animationInfo.StartPendingAnimations(
275 aManager->LayerManager()->GetAnimationReadyTime());
277 // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
278 // are no active animations.
279 uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
280 if (!animationInfo.GetAnimations().IsEmpty()) {
281 OpAddCompositorAnimations anim(
282 CompositorAnimations(animationInfo.GetAnimations(), animationsId));
283 aManager->WrBridge()->AddWebRenderParentCommand(anim);
284 aManager->AddActiveCompositorAnimationId(animationsId);
285 } else if (animationsId) {
286 aManager->AddCompositorAnimationsIdForDiscard(animationsId);
287 animationsId = 0;
290 return animationsId;
293 static bool GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
294 const nsRect& aFillRect,
295 nsDisplayListBuilder* aBuilder) {
296 if (aBuilder->IsForGenerateGlyphMask()) {
297 return false;
300 SVGObserverUtils::GetAndObserveBackgroundClip(aFrame);
302 // The main function of enabling background-clip:text property value.
303 // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
304 // this function to
305 // 1. Generate a mask by all descendant text frames
306 // 2. Push the generated mask into aContext.
308 gfxContext* sourceCtx = aContext;
309 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
310 aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel());
312 // Create a mask surface.
313 RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget();
314 RefPtr<DrawTarget> maskDT = sourceTarget->CreateClippedDrawTarget(
315 bounds.ToUnknownRect(), SurfaceFormat::A8);
316 if (!maskDT || !maskDT->IsValid()) {
317 return false;
319 gfxContext maskCtx(maskDT, /* aPreserveTransform */ true);
320 maskCtx.Multiply(Matrix::Translation(bounds.TopLeft().ToUnknownPoint()));
322 // Shade text shape into mask A8 surface.
323 nsLayoutUtils::PaintFrame(
324 &maskCtx, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()),
325 NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph);
327 // Push the generated mask into aContext, so that the caller can pop and
328 // blend with it.
330 Matrix currentMatrix = sourceCtx->CurrentMatrix();
331 Matrix invCurrentMatrix = currentMatrix;
332 invCurrentMatrix.Invert();
334 RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
335 sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0,
336 maskSurface, invCurrentMatrix);
338 return true;
341 nsDisplayWrapper* nsDisplayWrapList::CreateShallowCopy(
342 nsDisplayListBuilder* aBuilder) {
343 const nsDisplayWrapList* wrappedItem = AsDisplayWrapList();
344 MOZ_ASSERT(wrappedItem);
346 // Create a new nsDisplayWrapList using a copy-constructor. This is done
347 // to preserve the information about bounds.
348 nsDisplayWrapper* wrapper =
349 new (aBuilder) nsDisplayWrapper(aBuilder, *wrappedItem);
350 wrapper->SetType(nsDisplayWrapper::ItemType());
351 MOZ_ASSERT(wrapper);
353 // Set the display list pointer of the new wrapper item to the display list
354 // of the wrapped item.
355 wrapper->mListPtr = wrappedItem->mListPtr;
356 return wrapper;
359 nsDisplayWrapList* nsDisplayListBuilder::MergeItems(
360 nsTArray<nsDisplayItem*>& aItems) {
361 // For merging, we create a temporary item by cloning the last item of the
362 // mergeable items list. This ensures that the temporary item will have the
363 // correct frame and bounds.
364 nsDisplayWrapList* last = aItems.PopLastElement()->AsDisplayWrapList();
365 MOZ_ASSERT(last);
366 nsDisplayWrapList* merged = last->Clone(this);
367 MOZ_ASSERT(merged);
368 AddTemporaryItem(merged);
370 // Create nsDisplayWrappers that point to the internal display lists of the
371 // items we are merging. These nsDisplayWrappers are added to the display list
372 // of the temporary item.
373 for (nsDisplayItem* item : aItems) {
374 MOZ_ASSERT(item);
375 MOZ_ASSERT(merged->CanMerge(item));
376 merged->Merge(item);
377 MOZ_ASSERT(item->AsDisplayWrapList());
378 merged->GetChildren()->AppendToTop(
379 static_cast<nsDisplayWrapList*>(item)->CreateShallowCopy(this));
382 merged->GetChildren()->AppendToTop(last->CreateShallowCopy(this));
384 return merged;
387 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
388 SetCurrentActiveScrolledRoot(
389 const ActiveScrolledRoot* aActiveScrolledRoot) {
390 MOZ_ASSERT(!mUsed);
392 // Set the builder's mCurrentActiveScrolledRoot.
393 mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;
395 // We also need to adjust the builder's mCurrentContainerASR.
396 // mCurrentContainerASR needs to be an ASR that all the container's
397 // contents have finite bounds with respect to. If aActiveScrolledRoot
398 // is an ancestor ASR of mCurrentContainerASR, that means we need to
399 // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
400 // the items that will be created with aActiveScrolledRoot wouldn't
401 // have finite bounds with respect to mCurrentContainerASR. There's one
402 // exception, in the case where there's a content clip on the builder
403 // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
404 // content clip will clip all items that are created while this
405 // AutoCurrentActiveScrolledRootSetter exists. This means that the items
406 // created during our lifetime will have finite bounds with respect to
407 // the content clip's ASR, even if the items' actual ASR is an ancestor
408 // of that. And it also means that mCurrentContainerASR only needs to be
409 // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
410 // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
411 // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
413 // finiteBoundsASR is the leafmost ASR that all items created during
414 // object's lifetime have finite bounds with respect to.
415 const ActiveScrolledRoot* finiteBoundsASR =
416 ActiveScrolledRoot::PickDescendant(mContentClipASR, aActiveScrolledRoot);
418 // mCurrentContainerASR is adjusted so that it's still an ancestor of
419 // finiteBoundsASR.
420 mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
421 mBuilder->mCurrentContainerASR, finiteBoundsASR);
423 // If we are entering out-of-flow content inside a CSS filter, mark
424 // scroll frames wrt. which the content is fixed as containing such content.
425 if (mBuilder->mFilterASR && ActiveScrolledRoot::IsAncestor(
426 aActiveScrolledRoot, mBuilder->mFilterASR)) {
427 for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
428 asr && asr != aActiveScrolledRoot; asr = asr->mParent) {
429 asr->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
433 mUsed = true;
436 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
437 InsertScrollFrame(nsIScrollableFrame* aScrollableFrame) {
438 MOZ_ASSERT(!mUsed);
439 size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
440 const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot;
441 const ActiveScrolledRoot* asr =
442 mBuilder->AllocateActiveScrolledRoot(parentASR, aScrollableFrame);
443 mBuilder->mCurrentActiveScrolledRoot = asr;
445 // All child ASRs of parentASR that were created while this
446 // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
447 // now. Reparent them to asr.
448 for (size_t i = mDescendantsStartIndex; i < descendantsEndIndex; i++) {
449 ActiveScrolledRoot* descendantASR = mBuilder->mActiveScrolledRoots[i];
450 if (ActiveScrolledRoot::IsAncestor(parentASR, descendantASR)) {
451 descendantASR->IncrementDepth();
452 if (descendantASR->mParent == parentASR) {
453 descendantASR->mParent = asr;
458 mUsed = true;
461 nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
462 nsDisplayListBuilder* aBuilder)
463 : mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) {
464 mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot;
467 nsPresContext* nsDisplayListBuilder::CurrentPresContext() {
468 return CurrentPresShellState()->mPresShell->GetPresContext();
471 /* static */
472 nsRect nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
473 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
474 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
475 nsRect* aOutDirtyRect) {
476 nsRect visible = aVisibleRect;
477 nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
479 bool inPartialUpdate =
480 aBuilder->IsRetainingDisplayList() && aBuilder->IsPartialUpdate();
481 if (StaticPrefs::apz_allow_zooming() &&
482 DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame) &&
483 aBuilder->IsPaintingToWindow() && !inPartialUpdate) {
484 dirtyRectRelativeToDirtyFrame =
485 nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize());
487 // If there's a visual viewport size set, restrict the amount of the
488 // fixed-position element we paint to the visual viewport. (In general
489 // the fixed-position element can be as large as the layout viewport,
490 // which at a high zoom level can cause us to paint too large of an
491 // area.)
492 PresShell* presShell = aFrame->PresShell();
493 if (presShell->IsVisualViewportSizeSet()) {
494 dirtyRectRelativeToDirtyFrame =
495 nsRect(presShell->GetVisualViewportOffsetRelativeToLayoutViewport(),
496 presShell->GetVisualViewportSize());
497 // But if we have a displayport, expand it to the displayport, so
498 // that async-scrolling the visual viewport within the layout viewport
499 // will not checkerboard.
500 if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
501 nsRect displayport;
502 // Note that the displayport here is already in the right coordinate
503 // space: it's relative to the scroll port (= layout viewport), but
504 // covers the visual viewport with some margins around it, which is
505 // exactly what we want.
506 if (DisplayPortUtils::GetDisplayPort(
507 rootScrollFrame->GetContent(), &displayport,
508 DisplayPortOptions().With(ContentGeometryType::Fixed))) {
509 dirtyRectRelativeToDirtyFrame = displayport;
513 visible = dirtyRectRelativeToDirtyFrame;
514 if (StaticPrefs::apz_test_logging_enabled() &&
515 presShell->GetDocument()->IsContentDocument()) {
516 nsLayoutUtils::LogAdditionalTestData(
517 aBuilder, "fixedPosDisplayport",
518 ToString(CSSSize::FromAppUnits(visible)));
522 *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
523 visible -= aFrame->GetPosition();
525 nsRect overflowRect = aFrame->InkOverflowRect();
527 if (aFrame->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
528 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
530 * Add a fuzz factor to the overflow rectangle so that elements only
531 * just out of view are pulled into the display list, so they can be
532 * prerendered if necessary.
534 overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
537 visible.IntersectRect(visible, overflowRect);
538 aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect);
540 return visible;
543 nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder* aBuilder,
544 nsIFrame* aFrame,
545 nsDisplayList* aList)
546 : mList(aList) {
547 // Find the element that we need to check for link-ness, bailing out if
548 // we can't find one.
549 Element* elem = Element::FromNodeOrNull(aFrame->GetContent());
550 if (!elem) {
551 return;
554 // If the element has an id and/or name attribute, generate a destination
555 // for possible internal linking.
556 auto maybeGenerateDest = [&](const nsAtom* aAttr) {
557 nsAutoString attrValue;
558 elem->GetAttr(aAttr, attrValue);
559 if (!attrValue.IsEmpty()) {
560 NS_ConvertUTF16toUTF8 dest(attrValue);
561 // Ensure that we only emit a given destination once, although there may
562 // be multiple frames associated with a given element; we'll simply use
563 // the first of them as the target of any links to it.
564 // XXX(jfkthame) This prevents emitting duplicate destinations *on the
565 // same page*, but does not prevent duplicates on subsequent pages, as
566 // each new page is handled by a new temporary DisplayListBuilder. This
567 // seems to be harmless in practice, though a bit wasteful of space. To
568 // fix, we need to maintain the set of already-seen destinations globally
569 // for the print job, rather than attached to the (per-page) builder.
570 if (aBuilder->mDestinations.EnsureInserted(dest)) {
571 auto* destination = MakeDisplayItem<nsDisplayDestination>(
572 aBuilder, aFrame, dest.get(), aFrame->GetRect().TopLeft());
573 mList->AppendToTop(destination);
578 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
579 if (elem->HasID()) {
580 maybeGenerateDest(nsGkAtoms::id);
582 if (elem->HasName()) {
583 maybeGenerateDest(nsGkAtoms::name);
587 // Links don't nest, so if the builder already has a destination, no need to
588 // check for a link element here.
589 if (!aBuilder->mLinkSpec.IsEmpty()) {
590 return;
593 // Check if we have actually found a link.
594 if (!elem->IsLink()) {
595 return;
598 nsCOMPtr<nsIURI> uri = elem->GetHrefURI();
599 if (!uri) {
600 return;
603 // Is it a local (in-page) destination?
604 bool hasRef, eqExRef;
605 nsIURI* docURI;
606 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
607 NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef &&
608 (docURI = aFrame->PresContext()->Document()->GetDocumentURI()) &&
609 NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &eqExRef)) && eqExRef) {
610 if (NS_FAILED(uri->GetRef(aBuilder->mLinkSpec)) ||
611 aBuilder->mLinkSpec.IsEmpty()) {
612 return;
614 // The destination name is simply a string; we don't want URL-escaping
615 // applied to it.
616 NS_UnescapeURL(aBuilder->mLinkSpec);
617 // Mark the link spec as being an internal destination
618 aBuilder->mLinkSpec.Insert('#', 0);
619 } else {
620 if (NS_FAILED(uri->GetSpec(aBuilder->mLinkSpec)) ||
621 aBuilder->mLinkSpec.IsEmpty()) {
622 return;
626 // Record that we need to reset the builder's state on destruction.
627 mBuilderToReset = aBuilder;
630 void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
631 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
632 // Note that we may generate a link here even if the constructor bailed out
633 // without updating aBuilder->LinkSpec(), because it may have been set by
634 // an ancestor that was associated with a link element.
635 if (!aBuilder->mLinkSpec.IsEmpty()) {
636 auto* link = MakeDisplayItem<nsDisplayLink>(
637 aBuilder, aFrame, aBuilder->mLinkSpec.get(), aFrame->GetRect());
638 mList->AppendToTop(link);
642 uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1);
644 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
645 nsDisplayListBuilderMode aMode,
646 bool aBuildCaret,
647 bool aRetainingDisplayList)
648 : mReferenceFrame(aReferenceFrame),
649 mIgnoreScrollFrame(nullptr),
650 mCurrentActiveScrolledRoot(nullptr),
651 mCurrentContainerASR(nullptr),
652 mCurrentFrame(aReferenceFrame),
653 mCurrentReferenceFrame(aReferenceFrame),
654 mCaretFrame(nullptr),
655 mScrollInfoItemsForHoisting(nullptr),
656 mFirstClipChainToDestroy(nullptr),
657 mTableBackgroundSet(nullptr),
658 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
659 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
660 mFilterASR(nullptr),
661 mDirtyRect(-1, -1, -1, -1),
662 mBuildingExtraPagesForPageNum(0),
663 mMode(aMode),
664 mContainsBlendMode(false),
665 mIsBuildingScrollbar(false),
666 mCurrentScrollbarWillHaveLayer(false),
667 mBuildCaret(aBuildCaret),
668 mRetainingDisplayList(aRetainingDisplayList),
669 mPartialUpdate(false),
670 mIgnoreSuppression(false),
671 mIncludeAllOutOfFlows(false),
672 mDescendIntoSubdocuments(true),
673 mSelectedFramesOnly(false),
674 mAllowMergingAndFlattening(true),
675 mInTransform(false),
676 mInEventsOnly(false),
677 mInFilter(false),
678 mInPageSequence(false),
679 mIsInChromePresContext(false),
680 mSyncDecodeImages(false),
681 mIsPaintingToWindow(false),
682 mUseHighQualityScaling(false),
683 mIsPaintingForWebRender(false),
684 mIsCompositingCheap(false),
685 mAncestorHasApzAwareEventHandler(false),
686 mHaveScrollableDisplayPort(false),
687 mWindowDraggingAllowed(false),
688 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
689 mForceLayerForScrollParent(false),
690 mContainsNonMinimalDisplayPort(false),
691 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
692 mBuildingInvisibleItems(false),
693 mIsBuilding(false),
694 mInInvalidSubtree(false),
695 mDisablePartialUpdates(false),
696 mPartialBuildFailed(false),
697 mIsInActiveDocShell(false),
698 mBuildAsyncZoomContainer(false),
699 mIsRelativeToLayoutViewport(false),
700 mUseOverlayScrollbars(false),
701 mAlwaysLayerizeScrollbars(false) {
702 MOZ_COUNT_CTOR(nsDisplayListBuilder);
704 mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting();
706 ShouldRebuildDisplayListDueToPrefChange();
708 mUseOverlayScrollbars =
709 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
711 mAlwaysLayerizeScrollbars =
712 StaticPrefs::layout_scrollbars_always_layerize_track();
714 static_assert(
715 static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
716 "Check TYPE_MAX should not overflow");
718 mIsReusingStackingContextItems =
719 mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
722 static PresShell* GetFocusedPresShell() {
723 nsPIDOMWindowOuter* focusedWnd =
724 nsFocusManager::GetFocusManager()->GetFocusedWindow();
725 if (!focusedWnd) {
726 return nullptr;
729 nsCOMPtr<nsIDocShell> focusedDocShell = focusedWnd->GetDocShell();
730 if (!focusedDocShell) {
731 return nullptr;
734 return focusedDocShell->GetPresShell();
737 void nsDisplayListBuilder::BeginFrame() {
738 nsCSSRendering::BeginFrameTreesLocked();
740 mIsPaintingToWindow = false;
741 mUseHighQualityScaling = false;
742 mIgnoreSuppression = false;
743 mInTransform = false;
744 mInFilter = false;
745 mSyncDecodeImages = false;
747 if (!mBuildCaret) {
748 return;
751 RefPtr<PresShell> presShell = GetFocusedPresShell();
752 if (presShell) {
753 RefPtr<nsCaret> caret = presShell->GetCaret();
754 mCaretFrame = caret->GetPaintGeometry(&mCaretRect);
756 // The focused pres shell may not be in the document that we're
757 // painting, or be in a popup. Check if the display root for
758 // the caret matches the display root that we're painting, and
759 // only use it if it matches.
760 if (mCaretFrame &&
761 nsLayoutUtils::GetDisplayRootFrame(mCaretFrame) !=
762 nsLayoutUtils::GetDisplayRootFrame(mReferenceFrame)) {
763 mCaretFrame = nullptr;
768 void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser* aBrowser,
769 const dom::EffectsInfo& aUpdate) {
770 dom::EffectsInfo update = aUpdate;
771 // For printing we create one display item for each page that an iframe
772 // appears on, the proper visible rect is the union of all the visible rects
773 // we get from each display item.
774 nsPresContext* pc =
775 mReferenceFrame ? mReferenceFrame->PresContext() : nullptr;
776 if (pc && pc->Type() != nsPresContext::eContext_Galley) {
777 Maybe<dom::EffectsInfo> existing = mEffectsUpdates.MaybeGet(aBrowser);
778 if (existing) {
779 // Only the visible rect should differ, the scales should match.
780 MOZ_ASSERT(existing->mRasterScale == aUpdate.mRasterScale &&
781 existing->mTransformToAncestorScale ==
782 aUpdate.mTransformToAncestorScale);
783 if (existing->mVisibleRect) {
784 if (update.mVisibleRect) {
785 update.mVisibleRect =
786 Some(update.mVisibleRect->Union(*existing->mVisibleRect));
787 } else {
788 update.mVisibleRect = existing->mVisibleRect;
793 mEffectsUpdates.InsertOrUpdate(aBrowser, update);
796 void nsDisplayListBuilder::EndFrame() {
797 NS_ASSERTION(!mInInvalidSubtree,
798 "Someone forgot to cleanup mInInvalidSubtree!");
799 mCurrentContainerASR = nullptr;
800 mActiveScrolledRoots.Clear();
801 mEffectsUpdates.Clear();
802 FreeClipChains();
803 FreeTemporaryItems();
804 nsCSSRendering::EndFrameTreesLocked();
805 mCaretFrame = nullptr;
808 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
809 const nsIFrame* aStopAtFrame) {
810 mFramesMarkedForDisplay.AppendElement(aFrame);
811 for (nsIFrame* f = aFrame; f;
812 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
813 if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
814 return;
816 f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
817 if (f == aStopAtFrame) {
818 // we've reached a frame that we know will be painted, so we can stop.
819 break;
824 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) {
825 mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
828 static void MarkFrameForDisplayIfVisibleInternal(nsIFrame* aFrame,
829 const nsIFrame* aStopAtFrame) {
830 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
831 if (f->ForceDescendIntoIfVisible()) {
832 return;
834 f->SetForceDescendIntoIfVisible(true);
836 // This condition must match the condition in
837 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
838 // nsLayoutUtils::GetDisplayListParent
839 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
840 nsIFrame* parent = f->GetParent();
841 if (parent && !parent->ForceDescendIntoIfVisible()) {
842 // If the GetDisplayListParent call is going to walk to a placeholder,
843 // in rare cases the placeholder might be contained in a different
844 // continuation from the oof. So we have to make sure to mark the oofs
845 // parent. In the common case this doesn't make us do any extra work,
846 // just changes the order in which we visit the frames since walking
847 // through placeholders will walk through the parent, and we stop when
848 // we find a ForceDescendIntoIfVisible bit set.
849 MarkFrameForDisplayIfVisibleInternal(parent, aStopAtFrame);
853 if (f == aStopAtFrame) {
854 // we've reached a frame that we know will be painted, so we can stop.
855 break;
860 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
861 nsIFrame* aFrame, const nsIFrame* aStopAtFrame) {
862 AddFrameMarkedForDisplayIfVisible(aFrame);
864 MarkFrameForDisplayIfVisibleInternal(aFrame, aStopAtFrame);
867 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
868 mIsRelativeToLayoutViewport = true;
869 UpdateShouldBuildAsyncZoomContainer();
872 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
873 const Document* document = mReferenceFrame->PresContext()->Document();
874 mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
875 !document->Fullscreen() &&
876 nsLayoutUtils::AllowZoomingForDocument(document);
879 // Certain prefs may cause display list items to be added or removed when they
880 // are toggled. In those cases, we need to fully rebuild the display list.
881 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
882 // If we transition between wrapping the RCD-RSF contents into an async
883 // zoom container vs. not, we need to rebuild the display list. This only
884 // happens when the zooming or container scrolling prefs are toggled
885 // (manually by the user, or during test setup).
886 bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer;
887 UpdateShouldBuildAsyncZoomContainer();
889 bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars;
890 mUseOverlayScrollbars =
891 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
893 bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars;
894 mAlwaysLayerizeScrollbars =
895 StaticPrefs::layout_scrollbars_always_layerize_track();
897 if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) {
898 return true;
901 if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) {
902 return true;
905 if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) {
906 return true;
909 return false;
912 void nsDisplayListBuilder::AddScrollFrameToNotify(
913 nsIScrollableFrame* aScrollFrame) {
914 mScrollFramesToNotify.insert(aScrollFrame);
917 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
918 for (const auto& it : mScrollFramesToNotify) {
919 it->NotifyApzTransaction();
921 mScrollFramesToNotify.clear();
924 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
925 nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect,
926 const nsRect& aDirtyRect) {
927 MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
928 nsRect dirty;
929 nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
930 this, aFrame, aVisibleRect, aDirtyRect, &dirty);
931 if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
932 visible.IsEmpty()) {
933 return false;
936 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
937 // frame, then it will also mark any outer frames to ensure that building
938 // reaches the dirty feame.
939 if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
940 MarkFrameForDisplay(aFrame, aDirtyFrame);
943 return true;
946 static void UnmarkFrameForDisplay(nsIFrame* aFrame,
947 const nsIFrame* aStopAtFrame) {
948 for (nsIFrame* f = aFrame; f;
949 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
950 if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
951 return;
953 f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
954 if (f == aStopAtFrame) {
955 // we've reached a frame that we know will be painted, so we can stop.
956 break;
961 static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
962 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
963 if (!f->ForceDescendIntoIfVisible()) {
964 return;
966 f->SetForceDescendIntoIfVisible(false);
968 // This condition must match the condition in
969 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
970 // nsLayoutUtils::GetDisplayListParent
971 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
972 nsIFrame* parent = f->GetParent();
973 if (parent && parent->ForceDescendIntoIfVisible()) {
974 // If the GetDisplayListParent call is going to walk to a placeholder,
975 // in rare cases the placeholder might be contained in a different
976 // continuation from the oof. So we have to make sure to mark the oofs
977 // parent. In the common case this doesn't make us do any extra work,
978 // just changes the order in which we visit the frames since walking
979 // through placeholders will walk through the parent, and we stop when
980 // we find a ForceDescendIntoIfVisible bit set.
981 UnmarkFrameForDisplayIfVisible(f);
987 nsDisplayListBuilder::~nsDisplayListBuilder() {
988 NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
989 "All frames should have been unmarked");
990 NS_ASSERTION(mFramesWithOOFData.Length() == 0,
991 "All OOF data should have been removed");
992 NS_ASSERTION(mPresShellStates.Length() == 0,
993 "All presshells should have been exited");
995 DisplayItemClipChain* c = mFirstClipChainToDestroy;
996 while (c) {
997 DisplayItemClipChain* next = c->mNextClipChainToDestroy;
998 c->DisplayItemClipChain::~DisplayItemClipChain();
999 c = next;
1002 MOZ_COUNT_DTOR(nsDisplayListBuilder);
1005 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
1006 uint32_t flags = 0;
1007 if (mSyncDecodeImages) {
1008 flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
1010 if (mIsPaintingToWindow) {
1011 flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
1013 if (mUseHighQualityScaling) {
1014 flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING;
1016 return flags;
1019 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
1020 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
1021 uint32_t flags = 0;
1022 if (mSyncDecodeImages) {
1023 flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
1025 if (mIsPaintingToWindow) {
1026 flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
1028 if (mUseHighQualityScaling) {
1029 flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
1031 return flags;
1034 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1035 uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
1036 if (mSyncDecodeImages) {
1037 flags |= imgIContainer::FLAG_SYNC_DECODE;
1038 } else {
1039 flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
1041 if (mIsPaintingToWindow || mUseHighQualityScaling) {
1042 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1044 return flags;
1047 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
1048 const nsRegion& aRegion) {
1049 if (aRegion.IsEmpty()) {
1050 return;
1053 nsRegion tmp;
1054 tmp.Sub(*aVisibleRegion, aRegion);
1055 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1056 // to its bounds either, which can be very bad (see bug 516740).
1057 // Do let aVisibleRegion get more complex if by doing so we reduce its
1058 // area by at least half.
1059 if (tmp.GetNumRects() <= 15 || tmp.Area() <= aVisibleRegion->Area() / 2) {
1060 *aVisibleRegion = tmp;
1064 nsCaret* nsDisplayListBuilder::GetCaret() {
1065 RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
1066 return caret;
1069 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) {
1070 if (mIsPaintingToWindow) {
1071 aPresShell->IncrementPaintCount();
1075 void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
1076 bool aPointerEventsNoneDoc) {
1077 PresShellState* state = mPresShellStates.AppendElement();
1078 state->mPresShell = aReferenceFrame->PresShell();
1079 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
1080 state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
1082 nsIScrollableFrame* sf = state->mPresShell->GetRootScrollFrameAsScrollable();
1083 if (sf && IsInSubdocument()) {
1084 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1085 // that the canvas background color will be set correctly, and that only one
1086 // unscrollable item will be created.
1087 // This is done to avoid, for example, a case where only scrollbar frames
1088 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1089 // and possibly end up with an extra nsDisplaySolidColor item.
1090 // We skip this for the root document, since we don't want to use
1091 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1092 // do it manually there.
1093 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
1094 if (canvasFrame) {
1095 MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
1099 #ifdef DEBUG
1100 state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
1101 nsLayoutPhase::DisplayListBuilding);
1102 #endif
1104 state->mPresShell->UpdateCanvasBackground();
1106 bool buildCaret = mBuildCaret;
1107 if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
1108 state->mIsBackgroundOnly = false;
1109 } else {
1110 state->mIsBackgroundOnly = true;
1111 buildCaret = false;
1114 bool pointerEventsNone = aPointerEventsNoneDoc;
1115 if (IsInSubdocument()) {
1116 pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
1117 .mInsidePointerEventsNoneDoc;
1119 state->mInsidePointerEventsNoneDoc = pointerEventsNone;
1121 state->mPresShellIgnoreScrollFrame =
1122 state->mPresShell->IgnoringViewportScrolling()
1123 ? state->mPresShell->GetRootScrollFrame()
1124 : nullptr;
1126 nsPresContext* pc = aReferenceFrame->PresContext();
1127 mIsInChromePresContext = pc->IsChrome();
1128 nsIDocShell* docShell = pc->GetDocShell();
1130 if (docShell) {
1131 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1134 state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);
1136 if (!buildCaret) {
1137 return;
1140 // Caret frames add visual area to their frame, but we don't update the
1141 // overflow area. Use flags to make sure we build display items for that frame
1142 // instead.
1143 if (mCaretFrame && mCaretFrame->PresShell() == state->mPresShell) {
1144 MarkFrameForDisplay(mCaretFrame, aReferenceFrame);
1148 // A non-blank paint is a paint that does not just contain the canvas
1149 // background.
1150 static bool DisplayListIsNonBlank(nsDisplayList* aList) {
1151 for (nsDisplayItem* i : *aList) {
1152 switch (i->GetType()) {
1153 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
1154 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
1155 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
1156 continue;
1157 case DisplayItemType::TYPE_SOLID_COLOR:
1158 case DisplayItemType::TYPE_BACKGROUND:
1159 case DisplayItemType::TYPE_BACKGROUND_COLOR:
1160 if (i->Frame()->IsCanvasFrame()) {
1161 continue;
1163 return true;
1164 default:
1165 return true;
1168 return false;
1171 // A contentful paint is a paint that does contains DOM content (text,
1172 // images, non-blank canvases, SVG): "First Contentful Paint entry
1173 // contains a DOMHighResTimeStamp reporting the time when the browser
1174 // first rendered any text, image (including background images),
1175 // non-white canvas or SVG. This excludes any content of iframes, but
1176 // includes text with pending webfonts. This is the first time users
1177 // could start consuming page content."
1178 static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
1179 nsDisplayList* aList) {
1180 for (nsDisplayItem* i : *aList) {
1181 DisplayItemType type = i->GetType();
1182 nsDisplayList* children = i->GetChildren();
1184 switch (type) {
1185 case DisplayItemType::TYPE_SUBDOCUMENT: // iframes are ignored
1186 break;
1187 // CANVASes check if they may have been modified (as a stand-in
1188 // actually tracking all modifications)
1189 default:
1190 if (i->IsContentful()) {
1191 bool dummy;
1192 nsRect bound = i->GetBounds(aBuilder, &dummy);
1193 if (!bound.IsEmpty()) {
1194 return true;
1197 if (children) {
1198 if (DisplayListIsContentful(aBuilder, children)) {
1199 return true;
1202 break;
1205 return false;
1208 void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
1209 nsDisplayList* aPaintedContents) {
1210 NS_ASSERTION(
1211 CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
1212 "Presshell mismatch");
1214 if (mIsPaintingToWindow && aPaintedContents) {
1215 nsPresContext* pc = aReferenceFrame->PresContext();
1216 if (!pc->HadNonBlankPaint()) {
1217 if (!CurrentPresShellState()->mIsBackgroundOnly &&
1218 DisplayListIsNonBlank(aPaintedContents)) {
1219 pc->NotifyNonBlankPaint();
1222 nsRootPresContext* rootPresContext = pc->GetRootPresContext();
1223 if (!pc->HadFirstContentfulPaint() && rootPresContext) {
1224 if (!CurrentPresShellState()->mIsBackgroundOnly) {
1225 if (pc->HasEverBuiltInvisibleText() ||
1226 DisplayListIsContentful(this, aPaintedContents)) {
1227 pc->NotifyContentfulPaint();
1233 ResetMarkedFramesForDisplayList(aReferenceFrame);
1234 mPresShellStates.RemoveLastElement();
1236 if (!mPresShellStates.IsEmpty()) {
1237 nsPresContext* pc = CurrentPresContext();
1238 nsIDocShell* docShell = pc->GetDocShell();
1239 if (docShell) {
1240 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1242 mIsInChromePresContext = pc->IsChrome();
1243 } else {
1244 for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
1245 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
1247 mFramesMarkedForDisplayIfVisible.SetLength(0);
1251 void nsDisplayListBuilder::FreeClipChains() {
1252 // Iterate the clip chains from newest to oldest (forward
1253 // iteration), so that we destroy descendants first which
1254 // will drop the ref count on their ancestors.
1255 DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;
1257 while (*indirect) {
1258 if (!(*indirect)->mRefCount) {
1259 DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;
1261 mClipDeduplicator.erase(*indirect);
1262 (*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
1263 Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);
1265 *indirect = next;
1266 } else {
1267 indirect = &(*indirect)->mNextClipChainToDestroy;
1272 void nsDisplayListBuilder::FreeTemporaryItems() {
1273 for (nsDisplayItem* i : mTemporaryItems) {
1274 // Temporary display items are not added to the frames.
1275 MOZ_ASSERT(i->Frame());
1276 i->RemoveFrame(i->Frame());
1277 i->Destroy(this);
1280 mTemporaryItems.Clear();
1283 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1284 const nsIFrame* aReferenceFrame) {
1285 // Unmark and pop off the frames marked for display in this pres shell.
1286 uint32_t firstFrameForShell =
1287 CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1288 for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
1289 ++i) {
1290 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
1292 mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1294 firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
1295 for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
1296 mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
1298 mFramesWithOOFData.SetLength(firstFrameForShell);
1301 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1302 CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
1305 void nsDisplayListBuilder::MarkFramesForDisplayList(
1306 nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
1307 nsRect visibleRect = GetVisibleRect();
1308 nsRect dirtyRect = GetDirtyRect();
1310 // If we are entering content that is fixed to the RCD-RSF, we are
1311 // crossing the async zoom container boundary, and need to convert from
1312 // visual to layout coordinates.
1313 if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
1314 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1315 viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
1316 if (viewportFrame->PresShell()->GetRootScrollFrame()) {
1317 #ifdef DEBUG
1318 for (nsIFrame* f : aFrames) {
1319 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
1321 #endif
1322 visibleRect = ViewportUtils::VisualToLayout(visibleRect,
1323 viewportFrame->PresShell());
1324 dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
1325 viewportFrame->PresShell());
1327 #ifdef DEBUG
1328 else {
1329 // This is an edge case that should only happen if we are in a
1330 // document with a XUL root element so that it does not have a root
1331 // scroll frame but it has fixed pos content and all of the frames in
1332 // aFrames are that fixed pos content.
1333 for (nsIFrame* f : aFrames) {
1334 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
1335 f->GetParent() == aDirtyFrame &&
1336 f->StyleDisplay()->mPosition ==
1337 StylePositionProperty::Fixed);
1339 // There's no root scroll frame so there can't be any zooming or async
1340 // panning so we don't need to adjust the visible and dirty rects.
1342 #endif
1346 bool markedFrames = false;
1347 for (nsIFrame* e : aFrames) {
1348 // Skip the AccessibleCaret frame when building no caret.
1349 if (!IsBuildingCaret()) {
1350 nsIContent* content = e->GetContent();
1351 if (content && content->IsInNativeAnonymousSubtree() &&
1352 content->IsElement()) {
1353 const nsAttrValue* classes = content->AsElement()->GetClasses();
1354 if (classes &&
1355 classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) {
1356 continue;
1360 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
1361 markedFrames = true;
1365 if (markedFrames) {
1366 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1367 // to objects on the stack, so we need to clone the chain.
1368 const DisplayItemClipChain* clipChain =
1369 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1370 const DisplayItemClipChain* combinedClipChain =
1371 mClipState.GetCurrentCombinedClipChain(this);
1372 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1374 OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
1375 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1376 visibleRect, dirtyRect);
1377 aDirtyFrame->SetProperty(
1378 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
1379 mFramesWithOOFData.AppendElement(aDirtyFrame);
1382 if (!aDirtyFrame->GetParent()) {
1383 // This is the viewport frame of aDirtyFrame's presshell.
1384 // Store the current display data so that it can be used for fixed
1385 // background images.
1386 NS_ASSERTION(
1387 CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
1388 "Presshell mismatch");
1389 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
1390 "already traversed this presshell's root frame?");
1392 const DisplayItemClipChain* clipChain =
1393 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1394 const DisplayItemClipChain* combinedClipChain =
1395 mClipState.GetCurrentCombinedClipChain(this);
1396 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1397 CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
1398 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1399 GetVisibleRect(), GetDirtyRect());
1404 * Mark all preserve-3d children with
1405 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1406 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1407 * dirty rect for preserve-3d children.
1409 * @param aDirtyFrame is the frame to mark children extending context.
1411 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1412 nsIFrame* aDirtyFrame) {
1413 for (const auto& childList : aDirtyFrame->ChildLists()) {
1414 for (nsIFrame* child : childList.mList) {
1415 if (child->Combines3DTransformWithAncestors()) {
1416 MarkFrameForDisplay(child, aDirtyFrame);
1419 if (child->IsBlockWrapper()) {
1420 // Mark preserve-3d frames inside the block wrapper.
1421 MarkPreserve3DFramesForDisplayList(child);
1427 ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1428 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame) {
1429 RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
1430 aParent, aScrollableFrame, IsRetainingDisplayList());
1431 mActiveScrolledRoots.AppendElement(asr);
1432 return asr;
1435 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1436 const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
1437 const DisplayItemClipChain* aParent) {
1438 MOZ_DIAGNOSTIC_ASSERT(!(aParent && aParent->mOnStack));
1439 void* p = Allocate(sizeof(DisplayItemClipChain),
1440 DisplayListArenaObjectId::CLIPCHAIN);
1441 DisplayItemClipChain* c = new (KnownNotNull, p)
1442 DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy);
1443 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
1444 c->mOnStack = false;
1445 #endif
1446 auto result = mClipDeduplicator.insert(c);
1447 if (!result.second) {
1448 // An equivalent clip chain item was already created, so let's return that
1449 // instead. Destroy the one we just created.
1450 // Note that this can cause clip chains from different coordinate systems to
1451 // collapse into the same clip chain object, because clip chains do not keep
1452 // track of the reference frame that they were created in.
1453 c->DisplayItemClipChain::~DisplayItemClipChain();
1454 Destroy(DisplayListArenaObjectId::CLIPCHAIN, c);
1455 return *(result.first);
1457 mFirstClipChainToDestroy = c;
1458 return c;
1461 struct ClipChainItem {
1462 DisplayItemClip clip;
1463 const ActiveScrolledRoot* asr;
1466 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
1467 const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
1468 for (const ActiveScrolledRoot* asr =
1469 ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
1470 asr; asr = asr->mParent) {
1471 if (aOne == aTwo) {
1472 return aOne;
1474 if (aOne->mASR == asr) {
1475 aOne = aOne->mParent;
1477 if (aTwo->mASR == asr) {
1478 aTwo = aTwo->mParent;
1480 if (!aOne) {
1481 return aTwo;
1483 if (!aTwo) {
1484 return aOne;
1487 return nullptr;
1490 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1491 const DisplayItemClipChain* aAncestor,
1492 const DisplayItemClipChain* aLeafClip1,
1493 const DisplayItemClipChain* aLeafClip2) {
1494 AutoTArray<ClipChainItem, 8> intersectedClips;
1496 const DisplayItemClipChain* clip1 = aLeafClip1;
1497 const DisplayItemClipChain* clip2 = aLeafClip2;
1499 const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
1500 clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);
1502 // Build up the intersection from the leaf to the root and put it into
1503 // intersectedClips. The loop below will convert intersectedClips into an
1504 // actual DisplayItemClipChain.
1505 // (We need to do this in two passes because we need the parent clip in order
1506 // to create the DisplayItemClipChain object, but the parent clip has not
1507 // been created at that point.)
1508 while (!aAncestor || asr != aAncestor->mASR) {
1509 if (clip1 && clip1->mASR == asr) {
1510 if (clip2 && clip2->mASR == asr) {
1511 DisplayItemClip intersection = clip1->mClip;
1512 intersection.IntersectWith(clip2->mClip);
1513 intersectedClips.AppendElement(ClipChainItem{intersection, asr});
1514 clip2 = clip2->mParent;
1515 } else {
1516 intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
1518 clip1 = clip1->mParent;
1519 } else if (clip2 && clip2->mASR == asr) {
1520 intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
1521 clip2 = clip2->mParent;
1523 if (!asr) {
1524 MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
1525 break;
1527 asr = asr->mParent;
1530 // Convert intersectedClips into a DisplayItemClipChain.
1531 const DisplayItemClipChain* parentSC = aAncestor;
1532 for (auto& sc : Reversed(intersectedClips)) {
1533 parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
1535 return parentSC;
1538 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1539 const DisplayItemClipChain* aLeafClip1,
1540 const DisplayItemClipChain* aLeafClip2) {
1541 // aLeafClip2 might be a reference to a clip on the stack. We need to make
1542 // sure that CreateClipChainIntersection will allocate the actual intersected
1543 // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1544 // we supply nullptr as the common ancestor so that
1545 // CreateClipChainIntersection clones the whole chain.
1546 const DisplayItemClipChain* ancestorClip =
1547 aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
1548 : nullptr;
1550 return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2);
1553 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
1554 const DisplayItemClipChain* aClipChain) {
1555 return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
1558 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
1559 const nsIFrame* aFrame, nsPoint* aOffset) const {
1560 auto MaybeApplyAdditionalOffset = [&]() {
1561 if (auto offset = AdditionalOffset()) {
1562 *aOffset += *offset;
1566 if (aFrame == mCurrentFrame) {
1567 if (aOffset) {
1568 *aOffset = mCurrentOffsetToReferenceFrame;
1570 return mCurrentReferenceFrame;
1573 for (const nsIFrame* f = aFrame; f;
1574 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
1575 if (f == mReferenceFrame || f->IsTransformed()) {
1576 if (aOffset) {
1577 *aOffset = aFrame->GetOffsetToCrossDoc(f);
1578 MaybeApplyAdditionalOffset();
1580 return f;
1584 if (aOffset) {
1585 *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1586 MaybeApplyAdditionalOffset();
1589 return mReferenceFrame;
1592 // Sticky frames are active if their nearest scrollable frame is also active.
1593 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
1594 nsIFrame* aFrame) {
1595 MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
1596 StylePositionProperty::Sticky);
1598 StickyScrollContainer* stickyScrollContainer =
1599 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
1600 return stickyScrollContainer &&
1601 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1604 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
1605 nsIFrame** aParent) {
1606 if (aFrame == mReferenceFrame) {
1607 return true;
1610 if (!IsPaintingToWindow()) {
1611 if (aParent) {
1612 *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1614 return false;
1617 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1618 if (!parent) {
1619 return true;
1621 *aParent = parent;
1623 if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
1624 IsStickyFrameActive(this, aFrame)) {
1625 return true;
1628 if (aFrame->IsTransformed()) {
1629 if (EffectCompositor::HasAnimationsForCompositor(
1630 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
1631 return true;
1635 LayoutFrameType parentType = parent->Type();
1636 if (parentType == LayoutFrameType::Scroll ||
1637 parentType == LayoutFrameType::ListControl) {
1638 nsIScrollableFrame* sf = do_QueryFrame(parent);
1639 if (sf->GetScrolledFrame() == aFrame) {
1640 MOZ_ASSERT(!aFrame->IsTransformed());
1641 return sf->IsMaybeAsynchronouslyScrolled();
1645 return false;
1648 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1649 nsIFrame* aFrame) {
1650 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1651 RootReferenceFrame(), aFrame));
1652 nsIFrame* cursor = aFrame;
1653 while (cursor != RootReferenceFrame()) {
1654 nsIFrame* next;
1655 if (IsAnimatedGeometryRoot(cursor, &next)) {
1656 return cursor;
1658 cursor = next;
1660 return cursor;
1663 static nsRect ApplyAllClipNonRoundedIntersection(
1664 const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
1665 nsRect result = aRect;
1666 while (aClipChain) {
1667 result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
1668 aClipChain = aClipChain->mParent;
1670 return result;
1673 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
1674 if (!mWindowDraggingAllowed || !IsForPainting()) {
1675 return;
1678 const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
1679 if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
1680 // This frame has the default value and doesn't influence the window
1681 // dragging region.
1682 return;
1685 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;
1687 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1688 nsIFrame* referenceFrame =
1689 const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1691 if (IsInTransform()) {
1692 // Only support 2d rectilinear transforms. Transform support is needed for
1693 // the horizontal flip transform that's applied to the urlbar textbox in
1694 // RTL mode - it should be able to exclude itself from the draggable region.
1695 referenceFrameToRootReferenceFrame =
1696 ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
1697 nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
1698 RelativeTo{mReferenceFrame})
1699 .GetMatrix());
1700 Matrix referenceFrameToRootReferenceFrame2d;
1701 if (!referenceFrameToRootReferenceFrame.Is2D(
1702 &referenceFrameToRootReferenceFrame2d) ||
1703 !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1704 return;
1706 } else {
1707 MOZ_ASSERT(referenceFrame == mReferenceFrame,
1708 "referenceFrameToRootReferenceFrame needs to be adjusted");
1711 // We do some basic visibility checking on the frame's border box here.
1712 // We intersect it both with the current dirty rect and with the current
1713 // clip. Either one is just a conservative approximation on its own, but
1714 // their intersection luckily works well enough for our purposes, so that
1715 // we don't have to do full-blown visibility computations.
1716 // The most important case we need to handle is the scrolled-off tab:
1717 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1718 // should not be allowed to interfere with the window dragging region. Using
1719 // just the current DisplayItemClip is not enough to cover this case
1720 // completely because clips are reset while building stacking context
1721 // contents, so for example we'd fail to clip frames that have a clip path
1722 // applied to them. But the current dirty rect doesn't get reset in that
1723 // case, so we use it to make this case work.
1724 nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
1725 borderBox += ToReferenceFrame(aFrame);
1726 const DisplayItemClipChain* clip =
1727 ClipState().GetCurrentCombinedClipChain(this);
1728 borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
1729 if (borderBox.IsEmpty()) {
1730 return;
1733 LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
1734 borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1736 LayoutDeviceRect transformedDevPixelBorderBox =
1737 TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1738 transformedDevPixelBorderBox.Round();
1739 LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1741 if (!transformedDevPixelBorderBox.ToIntRect(
1742 &transformedDevPixelBorderBoxInt)) {
1743 return;
1746 LayoutDeviceIntRegion& region =
1747 styleUI->mWindowDragging == StyleWindowDragging::Drag
1748 ? mWindowDraggingRegion
1749 : mWindowNoDraggingRegion;
1751 if (!IsRetainingDisplayList()) {
1752 region.OrWith(transformedDevPixelBorderBoxInt);
1753 return;
1756 gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
1757 if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
1758 mRetainedWindowDraggingRegion.Add(aFrame, rect);
1759 } else {
1760 mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
1764 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
1765 LayoutDeviceIntRegion result;
1766 if (!IsRetainingDisplayList()) {
1767 result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
1768 return result;
1771 LayoutDeviceIntRegion dragRegion =
1772 mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();
1774 LayoutDeviceIntRegion noDragRegion =
1775 mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();
1777 result.Sub(dragRegion, noDragRegion);
1778 return result;
1781 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1782 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes);
1783 aSizes.mLayoutRetainedDisplayListSize +=
1784 aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get());
1787 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1788 mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);
1790 size_t n = 0;
1791 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
1792 n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1793 n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1794 n += mEffectsUpdates.ShallowSizeOfExcludingThis(mallocSizeOf);
1795 n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1796 n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1797 n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
1798 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1800 aSizes.mLayoutRetainedDisplayListSize += n;
1803 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1804 for (nsDisplayItem* item : *this) {
1805 item->AddSizeOfExcludingThis(aSizes);
1806 if (RetainedDisplayList* children = item->GetChildren()) {
1807 children->AddSizeOfExcludingThis(aSizes);
1811 size_t n = 0;
1813 n += mDAG.mDirectPredecessorList.ShallowSizeOfExcludingThis(
1814 aSizes.mState.mMallocSizeOf);
1815 n += mDAG.mNodesInfo.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1816 n += mOldItems.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1818 aSizes.mLayoutRetainedDisplayListSize += n;
1821 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1822 MallocSizeOf aMallocSizeOf) const {
1823 size_t n = 0;
1824 n += mFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
1825 for (const auto& frame : mFrames) {
1826 const UniquePtr<WeakFrame>& weakFrame = frame.mWeakFrame;
1827 n += aMallocSizeOf(weakFrame.get());
1829 n += mRects.ShallowSizeOfExcludingThis(aMallocSizeOf);
1830 return n;
1834 * Removes modified frames and rects from this WeakFrameRegion.
1836 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1837 MOZ_ASSERT(mFrames.Length() == mRects.Length());
1839 uint32_t i = 0;
1840 uint32_t length = mFrames.Length();
1842 while (i < length) {
1843 auto& wrapper = mFrames[i];
1845 if (!wrapper.mWeakFrame->IsAlive() ||
1846 AnyContentAncestorModified(wrapper.mWeakFrame->GetFrame())) {
1847 // To avoid multiple O(n) shifts in the array, move the last element of
1848 // the array to the current position and decrease the array length.
1849 mFrameSet.Remove(wrapper.mFrame);
1850 mFrames[i] = std::move(mFrames[length - 1]);
1851 mRects[i] = std::move(mRects[length - 1]);
1852 length--;
1853 } else {
1854 i++;
1858 mFrames.TruncateLength(length);
1859 mRects.TruncateLength(length);
1862 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1863 mRetainedWindowDraggingRegion.RemoveModifiedFramesAndRects();
1864 mRetainedWindowNoDraggingRegion.RemoveModifiedFramesAndRects();
1865 mRetainedWindowOpaqueRegion.RemoveModifiedFramesAndRects();
1868 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1869 mRetainedWindowDraggingRegion.Clear();
1870 mRetainedWindowNoDraggingRegion.Clear();
1871 mRetainedWindowOpaqueRegion.Clear();
1874 const uint32_t gWillChangeAreaMultiplier = 3;
1875 static uint32_t GetLayerizationCost(const nsSize& aSize) {
1876 // There's significant overhead for each layer created from Gecko
1877 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1878 // Therefore we set a minimum cost threshold of a 64x64 area.
1879 const int minBudgetCost = 64 * 64;
1881 const uint32_t budgetCost = std::max(
1882 minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
1883 nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
1885 return budgetCost;
1888 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
1889 const nsSize& aSize) {
1890 MOZ_ASSERT(IsForPainting());
1892 if (aFrame->MayHaveWillChangeBudget()) {
1893 // The frame is already in the will-change budget.
1894 return true;
1897 const nsPresContext* presContext = aFrame->PresContext();
1898 const nsRect area = presContext->GetVisibleArea();
1899 const uint32_t budgetLimit =
1900 nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1901 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1902 const uint32_t cost = GetLayerizationCost(aSize);
1904 DocumentWillChangeBudget& documentBudget =
1905 mDocumentWillChangeBudgets.LookupOrInsert(presContext);
1907 const bool onBudget =
1908 (documentBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
1910 if (onBudget) {
1911 documentBudget += cost;
1912 mFrameWillChangeBudgets.InsertOrUpdate(
1913 aFrame, FrameWillChangeBudget(presContext, cost));
1914 aFrame->SetMayHaveWillChangeBudget(true);
1917 return onBudget;
1920 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
1921 const nsSize& aSize) {
1922 if (!IsForPainting()) {
1923 // If this nsDisplayListBuilder is not for painting, the layerization should
1924 // not matter. Do the simple thing and return false.
1925 return false;
1928 const bool onBudget = AddToWillChangeBudget(aFrame, aSize);
1929 if (onBudget) {
1930 return true;
1933 auto* pc = aFrame->PresContext();
1934 auto* doc = pc->Document();
1935 if (!doc->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget)) {
1936 AutoTArray<nsString, 2> params;
1937 params.AppendElement()->AppendInt(gWillChangeAreaMultiplier);
1939 nsRect area = pc->GetVisibleArea();
1940 uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1941 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1942 params.AppendElement()->AppendInt(budgetLimit);
1944 doc->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget, false, params);
1947 return false;
1950 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame* aFrame) {
1951 MOZ_ASSERT(IsForPainting());
1953 if (!aFrame->MayHaveWillChangeBudget()) {
1954 return;
1957 aFrame->SetMayHaveWillChangeBudget(false);
1958 RemoveFromWillChangeBudgets(aFrame);
1961 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame* aFrame) {
1962 if (auto entry = mFrameWillChangeBudgets.Lookup(aFrame)) {
1963 const FrameWillChangeBudget& frameBudget = entry.Data();
1965 auto documentBudget =
1966 mDocumentWillChangeBudgets.Lookup(frameBudget.mPresContext);
1968 if (documentBudget) {
1969 *documentBudget -= frameBudget.mUsage;
1972 entry.Remove();
1976 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1977 mFrameWillChangeBudgets.Clear();
1978 mDocumentWillChangeBudgets.Clear();
1981 void nsDisplayListBuilder::EnterSVGEffectsContents(
1982 nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) {
1983 MOZ_ASSERT(aHoistedItemsStorage);
1984 if (mSVGEffectsFrames.IsEmpty()) {
1985 MOZ_ASSERT(!mScrollInfoItemsForHoisting);
1986 mScrollInfoItemsForHoisting = aHoistedItemsStorage;
1988 mSVGEffectsFrames.AppendElement(aEffectsFrame);
1991 void nsDisplayListBuilder::ExitSVGEffectsContents() {
1992 MOZ_ASSERT(!mSVGEffectsFrames.IsEmpty());
1993 mSVGEffectsFrames.RemoveLastElement();
1994 MOZ_ASSERT(mScrollInfoItemsForHoisting);
1995 if (mSVGEffectsFrames.IsEmpty()) {
1996 mScrollInfoItemsForHoisting = nullptr;
2000 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
2002 * Note: if changing the conditions under which scroll info layers
2003 * are created, make a corresponding change to
2004 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
2006 for (nsIFrame* frame : mSVGEffectsFrames) {
2007 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame)) {
2008 return true;
2011 return false;
2014 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
2015 nsDisplayScrollInfoLayer* aScrollInfoItem) {
2016 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2017 MOZ_ASSERT(mScrollInfoItemsForHoisting);
2018 mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
2021 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2022 nsIFrame* aFrame, nsDisplayList* aList) {
2023 MOZ_ASSERT(aFrame);
2024 MOZ_ASSERT(aList);
2026 if (!BuildCompositorHitTestInfo()) {
2027 return;
2030 const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
2031 if (info != CompositorHitTestInvisibleToHit) {
2032 aList->AppendNewToTop<nsDisplayCompositorHitTestInfo>(this, aFrame);
2036 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem* aItem) {
2037 mReuseableItems.Insert(aItem);
2040 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem* aItem) {
2041 MOZ_ASSERT(aItem->IsReusedItem());
2042 mReuseableItems.Remove(aItem);
2045 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2046 const size_t total = mReuseableItems.Count();
2048 size_t reused = 0;
2049 for (auto* item : mReuseableItems) {
2050 if (item->IsReusedItem()) {
2051 reused++;
2052 item->SetReusable();
2053 } else {
2054 item->Destroy(this);
2058 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
2059 mReuseableItems.Clear();
2062 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
2063 const auto* previous = mCurrentContainerASR;
2064 const auto* asr = aItem->GetActiveScrolledRoot();
2065 mCurrentContainerASR =
2066 ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
2068 if (previous != mCurrentContainerASR) {
2069 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
2070 mCurrentContainerASR);
2073 aItem->SetReusedItem();
2076 void nsDisplayListSet::CopyTo(const nsDisplayListSet& aDestination) const {
2077 for (size_t i = 0; i < mLists.size(); ++i) {
2078 auto* from = mLists[i];
2079 auto* to = aDestination.mLists[i];
2081 from->CopyTo(to);
2085 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
2086 aDestination.BorderBackground()->AppendToTop(BorderBackground());
2087 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2088 aDestination.Floats()->AppendToTop(Floats());
2089 aDestination.Content()->AppendToTop(Content());
2090 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
2091 aDestination.Outlines()->AppendToTop(Outlines());
2094 nsRect nsDisplayList::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2095 nsRect bounds;
2096 for (nsDisplayItem* i : *this) {
2097 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
2099 return bounds;
2102 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR(
2103 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
2104 nsRect* aBuildingRect) const {
2105 nsRect bounds;
2106 for (nsDisplayItem* i : *this) {
2107 nsRect r = i->GetClippedBounds(aBuilder);
2108 if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
2109 if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) {
2110 r = clip.ref();
2113 if (aBuildingRect) {
2114 aBuildingRect->UnionRect(*aBuildingRect, i->GetBuildingRect());
2116 bounds.UnionRect(bounds, r);
2118 return bounds;
2121 nsRect nsDisplayList::GetBuildingRect() const {
2122 nsRect result;
2123 for (nsDisplayItem* i : *this) {
2124 result.UnionRect(result, i->GetBuildingRect());
2126 return result;
2129 static void TriggerPendingAnimations(Document& aDoc,
2130 const TimeStamp& aReadyTime) {
2131 MOZ_ASSERT(!aReadyTime.IsNull(),
2132 "Animation ready time is not set. Perhaps we're using a layer"
2133 " manager that doesn't update it");
2134 if (PendingAnimationTracker* tracker = aDoc.GetPendingAnimationTracker()) {
2135 PresShell* presShell = aDoc.GetPresShell();
2136 // If paint-suppression is in effect then we haven't finished painting
2137 // this document yet so we shouldn't start animations
2138 if (!presShell || !presShell->IsPaintingSuppressed()) {
2139 tracker->TriggerPendingAnimationsOnNextTick(aReadyTime);
2142 auto recurse = [&aReadyTime](Document& aDoc) {
2143 TriggerPendingAnimations(aDoc, aReadyTime);
2144 return CallState::Continue;
2146 aDoc.EnumerateSubDocuments(recurse);
2149 WindowRenderer* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView** aView) {
2150 if (aView) {
2151 *aView = RootReferenceFrame()->GetView();
2153 if (RootReferenceFrame() !=
2154 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2155 return nullptr;
2157 nsIWidget* window = RootReferenceFrame()->GetNearestWidget();
2158 if (window) {
2159 return window->GetWindowRenderer();
2161 return nullptr;
2164 WebRenderLayerManager* nsDisplayListBuilder::GetWidgetLayerManager(
2165 nsView** aView) {
2166 WindowRenderer* renderer = GetWidgetWindowRenderer();
2167 if (renderer) {
2168 return renderer->AsWebRender();
2170 return nullptr;
2173 void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2174 int32_t aAppUnitsPerDevPixel) {
2175 FlattenedDisplayListIterator iter(aBuilder, this);
2176 while (iter.HasNext()) {
2177 nsPaintedDisplayItem* item = iter.GetNextItem()->AsPaintedDisplayItem();
2178 if (!item) {
2179 continue;
2182 nsRect visible = item->GetClippedBounds(aBuilder);
2183 visible = visible.Intersect(item->GetPaintRect(aBuilder, aCtx));
2184 if (visible.IsEmpty()) {
2185 continue;
2188 DisplayItemClip currentClip = item->GetClip();
2189 if (currentClip.HasClip()) {
2190 aCtx->Save();
2191 if (currentClip.IsRectClippedByRoundedCorner(visible)) {
2192 currentClip.ApplyTo(aCtx, aAppUnitsPerDevPixel);
2193 } else {
2194 currentClip.ApplyRectTo(aCtx, aAppUnitsPerDevPixel);
2197 aCtx->NewPath();
2199 item->Paint(aBuilder, aCtx);
2201 if (currentClip.HasClip()) {
2202 aCtx->Restore();
2208 * We paint by executing a layer manager transaction, constructing a
2209 * single layer representing the display list, and then making it the
2210 * root of the layer manager, drawing into the PaintedLayers.
2212 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2213 uint32_t aFlags,
2214 Maybe<double> aDisplayListBuildTime) {
2215 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS);
2217 RefPtr<WebRenderLayerManager> layerManager;
2218 WindowRenderer* renderer = nullptr;
2219 bool widgetTransaction = false;
2220 bool doBeginTransaction = true;
2221 nsView* view = nullptr;
2222 if (aFlags & PAINT_USE_WIDGET_LAYERS) {
2223 renderer = aBuilder->GetWidgetWindowRenderer(&view);
2224 if (renderer) {
2225 // The fallback renderer doesn't retain any content, so it's
2226 // not meaningful to use it when drawing to an external context.
2227 if (aCtx && renderer->AsFallback()) {
2228 MOZ_ASSERT(!(aFlags & PAINT_EXISTING_TRANSACTION));
2229 renderer = nullptr;
2230 } else {
2231 layerManager = renderer->AsWebRender();
2232 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
2233 widgetTransaction = true;
2238 nsIFrame* frame = aBuilder->RootReferenceFrame();
2239 nsPresContext* presContext = frame->PresContext();
2240 PresShell* presShell = presContext->PresShell();
2241 Document* document = presShell->GetDocument();
2243 ScopeExit g([&]() {
2244 #ifdef DEBUG
2245 MOZ_ASSERT(!layerManager || !layerManager->GetTarget());
2246 #endif
2248 // For layers-free mode, we check the invalidation state bits in the
2249 // EndTransaction. So we clear the invalidation state bits after
2250 // EndTransaction.
2251 if (widgetTransaction ||
2252 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2253 // but they still need the invalidation state bits cleared in order for
2254 // invalidation for CSS/SMIL animation to work properly.
2255 (document && document->IsBeingUsedAsImage())) {
2256 DL_LOGD("Clearing invalidation state bits");
2257 frame->ClearInvalidationStateBits();
2261 if (!renderer) {
2262 if (!aCtx) {
2263 NS_WARNING("Nowhere to paint into");
2264 return;
2266 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(false);
2267 Paint(aBuilder, aCtx, presContext->AppUnitsPerDevPixel());
2269 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2270 return;
2273 if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) {
2274 MOZ_ASSERT(layerManager);
2275 if (doBeginTransaction) {
2276 if (aCtx) {
2277 if (!layerManager->BeginTransactionWithTarget(aCtx, nsCString())) {
2278 return;
2280 } else {
2281 if (!layerManager->BeginTransaction(nsCString())) {
2282 return;
2287 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(true);
2288 layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
2290 bool sent = false;
2291 if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
2292 MOZ_ASSERT(!aCtx);
2293 sent = layerManager->EndEmptyTransaction();
2296 if (!sent) {
2297 auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
2299 nsIDocShell* docShell = presContext->GetDocShell();
2300 WrFiltersHolder wrFilters;
2301 gfx::Matrix5x4* colorMatrix =
2302 nsDocShell::Cast(docShell)->GetColorMatrix();
2303 if (colorMatrix) {
2304 wrFilters.filters.AppendElement(
2305 wr::FilterOp::ColorMatrix(colorMatrix->components));
2308 wrManager->EndTransactionWithoutLayer(this, aBuilder,
2309 std::move(wrFilters), nullptr,
2310 aDisplayListBuildTime.valueOr(0.0));
2313 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2314 if (document && widgetTransaction) {
2315 TriggerPendingAnimations(*document,
2316 layerManager->GetAnimationReadyTime());
2319 if (presContext->RefreshDriver()->HasScheduleFlush()) {
2320 presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2321 frame->GetRect());
2324 return;
2327 FallbackRenderer* fallback = renderer->AsFallback();
2328 MOZ_ASSERT(fallback);
2330 if (doBeginTransaction) {
2331 MOZ_ASSERT(!aCtx);
2332 if (!fallback->BeginTransaction()) {
2333 return;
2337 bool temp = aBuilder->SetIsCompositingCheap(renderer->IsCompositingCheap());
2338 fallback->EndTransactionWithList(aBuilder, this,
2339 presContext->AppUnitsPerDevPixel(),
2340 WindowRenderer::END_DEFAULT);
2342 aBuilder->SetIsCompositingCheap(temp);
2344 if (document && widgetTransaction) {
2345 TriggerPendingAnimations(*document, renderer->GetAnimationReadyTime());
2349 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
2350 for (auto* item : TakeItems()) {
2351 item->Destroy(aBuilder);
2355 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) {
2356 return aFrame->Style()->PointerEvents() != StylePointerEvents::None;
2359 // A list of frames, and their z depth. Used for sorting
2360 // the results of hit testing.
2361 struct FramesWithDepth {
2362 explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {}
2364 bool operator<(const FramesWithDepth& aOther) const {
2365 if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
2366 // We want to sort so that the shallowest item (highest depth value) is
2367 // first
2368 return mDepth > aOther.mDepth;
2370 return false;
2372 bool operator==(const FramesWithDepth& aOther) const {
2373 return this == &aOther;
2376 float mDepth;
2377 nsTArray<nsIFrame*> mFrames;
2380 // Sort the frames by depth and then moves all the contained frames to the
2381 // destination
2382 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource,
2383 nsTArray<nsIFrame*>* aDest) {
2384 if (aSource.IsEmpty()) {
2385 return;
2387 aSource.StableSort();
2388 uint32_t length = aSource.Length();
2389 for (uint32_t i = 0; i < length; i++) {
2390 aDest->AppendElements(std::move(aSource[i].mFrames));
2392 aSource.Clear();
2395 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2396 nsDisplayItem::HitTestState* aState,
2397 nsTArray<nsIFrame*>* aOutFrames) const {
2398 nsDisplayItem* item;
2400 if (aState->mInPreserves3D) {
2401 // Collect leaves of the current 3D rendering context.
2402 for (nsDisplayItem* item : *this) {
2403 auto itemType = item->GetType();
2404 if (itemType != DisplayItemType::TYPE_TRANSFORM ||
2405 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2406 item->HitTest(aBuilder, aRect, aState, aOutFrames);
2407 } else {
2408 // One of leaves in the current 3D rendering context.
2409 aState->mItemBuffer.AppendElement(item);
2412 return;
2415 int32_t itemBufferStart = aState->mItemBuffer.Length();
2416 for (nsDisplayItem* item : *this) {
2417 aState->mItemBuffer.AppendElement(item);
2420 AutoTArray<FramesWithDepth, 16> temp;
2421 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart;
2422 --i) {
2423 // Pop element off the end of the buffer. We want to shorten the buffer
2424 // so that recursive calls to HitTest have more buffer space.
2425 item = aState->mItemBuffer[i];
2426 aState->mItemBuffer.SetLength(i);
2428 bool snap;
2429 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
2430 auto itemType = item->GetType();
2431 bool same3DContext =
2432 (itemType == DisplayItemType::TYPE_TRANSFORM &&
2433 static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
2434 (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
2435 item->Frame()->Extend3DContext());
2436 if (same3DContext &&
2437 (itemType != DisplayItemType::TYPE_TRANSFORM ||
2438 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
2439 if (!item->GetClip().MayIntersect(aRect)) {
2440 continue;
2442 AutoTArray<nsIFrame*, 1> neverUsed;
2443 // Start gathering leaves of the 3D rendering context, and
2444 // append leaves at the end of mItemBuffer. Leaves are
2445 // processed at following iterations.
2446 aState->mInPreserves3D = true;
2447 item->HitTest(aBuilder, aRect, aState, &neverUsed);
2448 aState->mInPreserves3D = false;
2449 i = aState->mItemBuffer.Length();
2450 continue;
2452 if (same3DContext || item->GetClip().MayIntersect(r)) {
2453 AutoTArray<nsIFrame*, 16> outFrames;
2454 item->HitTest(aBuilder, aRect, aState, &outFrames);
2456 // For 3d transforms with preserve-3d we add hit frames into the temp list
2457 // so we can sort them later, otherwise we add them directly to the output
2458 // list.
2459 nsTArray<nsIFrame*>* writeFrames = aOutFrames;
2460 if (item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
2461 static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2462 if (outFrames.Length()) {
2463 nsDisplayTransform* transform =
2464 static_cast<nsDisplayTransform*>(item);
2465 nsPoint point = aRect.TopLeft();
2466 // A 1x1 rect means a point, otherwise use the center of the rect
2467 if (aRect.width != 1 || aRect.height != 1) {
2468 point = aRect.Center();
2470 temp.AppendElement(
2471 FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
2472 writeFrames = &temp[temp.Length() - 1].mFrames;
2474 } else {
2475 // We may have just finished a run of consecutive preserve-3d
2476 // transforms, so flush these into the destination array before
2477 // processing our frame list.
2478 FlushFramesArray(temp, aOutFrames);
2481 for (uint32_t j = 0; j < outFrames.Length(); j++) {
2482 nsIFrame* f = outFrames.ElementAt(j);
2483 // Filter out some frames depending on the type of hittest
2484 // we are doing. For visibility tests, pass through all frames.
2485 // For pointer tests, only pass through frames that are styled
2486 // to receive pointer events.
2487 if (aBuilder->HitTestIsForVisibility() ||
2488 IsFrameReceivingPointerEvents(f)) {
2489 writeFrames->AppendElement(f);
2493 if (aBuilder->HitTestIsForVisibility()) {
2494 aState->mHitOccludingItem = [&] {
2495 if (aState->mHitOccludingItem) {
2496 // We already hit something before.
2497 return true;
2499 if (aState->mCurrentOpacity == 1.0f &&
2500 item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) {
2501 // An opaque item always occludes everything. Note that we need to
2502 // check wrapping opacity and such as well.
2503 return true;
2505 float threshold = aBuilder->VisibilityThreshold();
2506 if (threshold == 1.0f) {
2507 return false;
2509 float itemOpacity = [&] {
2510 switch (item->GetType()) {
2511 case DisplayItemType::TYPE_OPACITY:
2512 return static_cast<nsDisplayOpacity*>(item)->GetOpacity();
2513 case DisplayItemType::TYPE_BACKGROUND_COLOR:
2514 return static_cast<nsDisplayBackgroundColor*>(item)
2515 ->GetOpacity();
2516 default:
2517 // Be conservative and assume it won't occlude other items.
2518 return 0.0f;
2520 }();
2521 return itemOpacity * aState->mCurrentOpacity >= threshold;
2522 }();
2524 if (aState->mHitOccludingItem) {
2525 // We're exiting early, so pop the remaining items off the buffer.
2526 aState->mItemBuffer.TruncateLength(itemBufferStart);
2527 break;
2532 // Clear any remaining preserve-3d transforms.
2533 FlushFramesArray(temp, aOutFrames);
2534 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
2535 "How did we forget to pop some elements?");
2538 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, Document* aDoc) {
2539 nsIFrame* f = aItem->Frame();
2540 while (f) {
2541 nsPresContext* pc = f->PresContext();
2542 if (pc->Document() == aDoc) {
2543 return f->GetContent();
2545 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(
2546 pc->PresShell()->GetRootFrame());
2548 return nullptr;
2551 struct ZSortItem {
2552 nsDisplayItem* item;
2553 int32_t zIndex;
2555 explicit ZSortItem(nsDisplayItem* aItem)
2556 : item(aItem), zIndex(aItem->ZIndex()) {}
2558 operator nsDisplayItem*() { return item; }
2561 struct ZOrderComparator {
2562 bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const {
2563 // Note that we can't just take the difference of the two
2564 // z-indices here, because that might overflow a 32-bit int.
2565 return aLeft.zIndex < aRight.zIndex;
2569 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); }
2571 struct ContentComparator {
2572 nsIContent* mCommonAncestor;
2574 explicit ContentComparator(nsIContent* aCommonAncestor)
2575 : mCommonAncestor(aCommonAncestor) {}
2577 bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const {
2578 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2579 // subdocument of commonAncestor, because display items for subdocuments
2580 // have been mixed into the same list. Ensure that we're looking at content
2581 // in commonAncestor's document.
2582 Document* commonAncestorDoc = mCommonAncestor->OwnerDoc();
2583 nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc);
2584 nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc);
2585 if (!content1 || !content2) {
2586 NS_ERROR("Document trees are mixed up!");
2587 // Something weird going on
2588 return true;
2590 return nsLayoutUtils::CompareTreePosition(content1, content2,
2591 mCommonAncestor) < 0;
2595 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) {
2596 Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor));
2599 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2600 static_assert(sizeof(nsDisplayItem) <= 176, "nsDisplayItem has grown");
2601 #endif
2603 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2604 : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
2606 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2607 const ActiveScrolledRoot* aActiveScrolledRoot)
2608 : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) {
2609 MOZ_COUNT_CTOR(nsDisplayItem);
2610 MOZ_ASSERT(mFrame);
2611 if (aBuilder->IsRetainingDisplayList()) {
2612 mFrame->AddDisplayItem(this);
2615 aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
2616 NS_ASSERTION(
2617 aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
2618 "visible rect not set");
2620 mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2622 // The visible rect is for mCurrentFrame, so we have to use
2623 // mCurrentOffsetToReferenceFrame
2624 nsRect visible = aBuilder->GetVisibleRect() +
2625 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
2626 SetBuildingRect(visible);
2628 const nsStyleDisplay* disp = mFrame->StyleDisplay();
2629 if (mFrame->BackfaceIsHidden(disp)) {
2630 mItemFlags += ItemFlag::BackfaceHidden;
2632 if (mFrame->Combines3DTransformWithAncestors()) {
2633 mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
2637 void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
2639 bool nsDisplayItem::HasDeletedFrame() const {
2640 bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) ||
2641 (GetType() == DisplayItemType::TYPE_REMOTE &&
2642 !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader());
2643 MOZ_ASSERT(retval || mFrame);
2644 return retval;
2647 /* static */
2648 bool nsDisplayItem::ForceActiveLayers() {
2649 return StaticPrefs::layers_force_active();
2652 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); }
2654 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
2655 bool aStore) {
2656 mClipChain = aClipChain;
2659 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
2660 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2661 if (const DisplayItemClip* clip =
2662 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
2663 return Some(clip->GetClipRect());
2665 #ifdef DEBUG
2666 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
2667 #endif
2668 return Nothing();
2671 const DisplayItemClip& nsDisplayItem::GetClip() const {
2672 const DisplayItemClip* clip =
2673 DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot);
2674 return clip ? *clip : DisplayItemClip::NoClip();
2677 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
2678 const DisplayItemClipChain* aOther,
2679 bool aStore) {
2680 if (!aOther || mClipChain == aOther) {
2681 return;
2684 // aOther might be a reference to a clip on the stack. We need to make sure
2685 // that CreateClipChainIntersection will allocate the actual intersected
2686 // clip in the builder's arena, so for the mClipChain == nullptr case,
2687 // we supply nullptr as the common ancestor so that
2688 // CreateClipChainIntersection clones the whole chain.
2689 const DisplayItemClipChain* ancestorClip =
2690 mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther)
2691 : nullptr;
2693 SetClipChain(
2694 aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther),
2695 aStore);
2698 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2699 bool snap;
2700 nsRect r = GetBounds(aBuilder, &snap);
2701 return GetClip().ApplyNonRoundedIntersection(r);
2704 nsDisplayContainer::nsDisplayContainer(
2705 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2706 const ActiveScrolledRoot* aActiveScrolledRoot, nsDisplayList* aList)
2707 : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
2708 mChildren(aBuilder) {
2709 MOZ_COUNT_CTOR(nsDisplayContainer);
2710 mChildren.AppendToTop(aList);
2711 UpdateBounds(aBuilder);
2713 // Clear and store the clip chain set by nsDisplayItem constructor.
2714 nsDisplayItem::SetClipChain(nullptr, true);
2717 nsRect nsDisplayItem::GetPaintRect(nsDisplayListBuilder* aBuilder,
2718 gfxContext* aCtx) {
2719 bool dummy;
2720 nsRect result = GetBounds(aBuilder, &dummy);
2721 if (aCtx) {
2722 result.IntersectRect(result,
2723 nsLayoutUtils::RoundGfxRectToAppRect(
2724 aCtx->GetClipExtents(),
2725 mFrame->PresContext()->AppUnitsPerDevPixel()));
2727 return result;
2730 bool nsDisplayContainer::CreateWebRenderCommands(
2731 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2732 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2733 nsDisplayListBuilder* aDisplayListBuilder) {
2734 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2735 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
2736 false);
2737 return true;
2740 nsRect nsDisplayContainer::GetBounds(nsDisplayListBuilder* aBuilder,
2741 bool* aSnap) const {
2742 *aSnap = false;
2743 return mBounds;
2746 nsRect nsDisplayContainer::GetComponentAlphaBounds(
2747 nsDisplayListBuilder* aBuilder) const {
2748 return mChildren.GetComponentAlphaBounds(aBuilder);
2751 static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2752 nsDisplayList* aList,
2753 const nsRect& aListBounds) {
2754 return aList->GetOpaqueRegion(aBuilder);
2757 nsRegion nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2758 bool* aSnap) const {
2759 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
2760 GetBounds(aBuilder, aSnap));
2763 Maybe<nsRect> nsDisplayContainer::GetClipWithRespectToASR(
2764 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2765 // Our children should have finite bounds with respect to |aASR|.
2766 if (aASR == mActiveScrolledRoot) {
2767 return Some(mBounds);
2770 return Some(mChildren.GetClippedBoundsWithRespectToASR(aBuilder, aASR));
2773 void nsDisplayContainer::HitTest(nsDisplayListBuilder* aBuilder,
2774 const nsRect& aRect, HitTestState* aState,
2775 nsTArray<nsIFrame*>* aOutFrames) {
2776 mChildren.HitTest(aBuilder, aRect, aState, aOutFrames);
2779 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder* aBuilder) {
2780 // Container item bounds are expected to be clipped.
2781 mBounds =
2782 mChildren.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
2785 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder,
2786 bool* aSnap) const {
2787 *aSnap = true;
2788 return mBounds;
2791 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
2792 gfxContext* aCtx) {
2793 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2794 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2795 Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx),
2796 appUnitsPerDevPixel, *drawTarget);
2797 drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
2800 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) {
2801 aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
2802 << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
2803 << ")";
2806 bool nsDisplaySolidColor::CreateWebRenderCommands(
2807 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2808 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2809 nsDisplayListBuilder* aDisplayListBuilder) {
2810 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
2811 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
2812 wr::LayoutRect r = wr::ToLayoutRect(bounds);
2813 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, mIsCheckerboardBackground,
2814 wr::ToColorF(ToDeviceColor(mColor)));
2816 return true;
2819 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
2820 bool* aSnap) const {
2821 *aSnap = true;
2822 return mRegion.GetBounds();
2825 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
2826 gfxContext* aCtx) {
2827 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2828 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2829 ColorPattern color(ToDeviceColor(mColor));
2830 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2831 Rect rect =
2832 NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
2833 drawTarget->FillRect(rect, color);
2837 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) {
2838 aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255)
2839 << "," << int(mColor.b * 255) << "," << mColor.a << ")";
2842 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2843 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2844 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2845 nsDisplayListBuilder* aDisplayListBuilder) {
2846 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2847 nsRect rect = iter.Get();
2848 LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
2849 rect, mFrame->PresContext()->AppUnitsPerDevPixel());
2850 wr::LayoutRect r = wr::ToLayoutRect(layerRects);
2851 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
2852 wr::ToColorF(ToDeviceColor(mColor)));
2855 return true;
2858 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder,
2859 nsDisplayItem* aItem, nsIFrame* aFrame,
2860 nsITheme::ThemeGeometryType aType) {
2861 if (aBuilder->IsInChromeDocumentOrPopup()) {
2862 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2863 bool preservesAxisAlignedRectangles = false;
2864 nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(
2865 aFrame, aFrame->GetRectRelativeToSelf(), displayRoot,
2866 &preservesAxisAlignedRectangles);
2867 if (preservesAxisAlignedRectangles) {
2868 aBuilder->RegisterThemeGeometry(
2869 aType, aItem,
2870 LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels(
2871 aFrame->PresContext()->AppUnitsPerDevPixel())));
2876 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2877 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
2878 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
2879 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
2880 nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
2881 nsRect rootRect = rootFrame->GetRectRelativeToSelf();
2882 if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) ==
2883 nsLayoutUtils::TRANSFORM_SUCCEEDED) {
2884 return Some(rootRect + aBuilder->ToReferenceFrame(aFrame));
2886 return Nothing();
2889 /* static */ nsDisplayBackgroundImage::InitData
2890 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder,
2891 nsIFrame* aFrame, uint16_t aLayer,
2892 const nsRect& aBackgroundRect,
2893 const ComputedStyle* aBackgroundStyle) {
2894 nsPresContext* presContext = aFrame->PresContext();
2895 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2896 const nsStyleImageLayers::Layer& layer =
2897 aBackgroundStyle->StyleBackground()->mImage.mLayers[aLayer];
2899 bool isTransformedFixed;
2900 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
2901 presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer,
2902 &isTransformedFixed);
2904 // background-attachment:fixed is treated as background-attachment:scroll
2905 // if it's affected by a transform.
2906 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2907 bool shouldTreatAsFixed =
2908 layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2909 !isTransformedFixed;
2911 bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsNone();
2912 bool isRasterImage = state.mImageRenderer.IsRasterImage();
2913 nsCOMPtr<imgIContainer> image;
2914 if (isRasterImage) {
2915 image = state.mImageRenderer.GetImage();
2917 return InitData{aBuilder, aBackgroundStyle, image,
2918 aBackgroundRect, state.mFillArea, state.mDestArea,
2919 aLayer, isRasterImage, shouldFixToViewport};
2922 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2923 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aInitData,
2924 nsIFrame* aFrameForBounds)
2925 : nsPaintedDisplayItem(aBuilder, aFrame),
2926 mBackgroundStyle(aInitData.backgroundStyle),
2927 mImage(aInitData.image),
2928 mDependentFrame(nullptr),
2929 mBackgroundRect(aInitData.backgroundRect),
2930 mFillRect(aInitData.fillArea),
2931 mDestRect(aInitData.destArea),
2932 mLayer(aInitData.layer),
2933 mIsRasterImage(aInitData.isRasterImage),
2934 mShouldFixToViewport(aInitData.shouldFixToViewport) {
2935 MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
2936 #ifdef DEBUG
2937 if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) {
2938 // If this changes, then you also need to adjust css::ImageLoader to
2939 // invalidate mFrame as needed.
2940 MOZ_ASSERT(mFrame->IsCanvasFrame() ||
2941 mFrame->IsFrameOfType(nsIFrame::eTablePart));
2943 #endif
2945 mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
2946 if (mShouldFixToViewport) {
2947 // Expand the item's visible rect to cover the entire bounds, limited to the
2948 // viewport rect. This is necessary because the background's clip can move
2949 // asynchronously.
2950 if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
2951 aInitData.builder, mFrame)) {
2952 SetBuildingRect(mBounds.Intersect(*viewportRect));
2957 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2958 MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
2959 if (mDependentFrame) {
2960 mDependentFrame->RemoveDisplayItem(this);
2964 static void SetBackgroundClipRegion(
2965 DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame,
2966 const nsStyleImageLayers::Layer& aLayer, const nsRect& aBackgroundRect,
2967 bool aWillPaintBorder) {
2968 nsCSSRendering::ImageLayerClipState clip;
2969 nsCSSRendering::GetImageLayerClip(
2970 aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect,
2971 aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
2973 if (clip.mHasAdditionalBGClipArea) {
2974 aClipState.ClipContentDescendants(
2975 clip.mAdditionalBGClipArea, clip.mBGClipArea,
2976 clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2977 } else {
2978 aClipState.ClipContentDescendants(
2979 clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2984 * This is used for the find bar highlighter overlay. It's only accessible
2985 * through the AnonymousContent API, so it's not exposed to general web pages.
2987 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
2988 nsIFrame* aFrame,
2989 const nsRect& aBackgroundRect,
2990 nsDisplayList* aList, nscolor aColor) {
2991 nsIContent* content = aFrame->GetContent();
2992 if (!content) {
2993 return false;
2996 void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
2997 if (!cutoutRegion) {
2998 return false;
3001 if (NS_GET_A(aColor) == 0) {
3002 return true;
3005 nsRegion region;
3006 region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
3007 region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
3008 aList->AppendNewToTop<nsDisplaySolidColorRegion>(aBuilder, aFrame, region,
3009 aColor);
3011 return true;
3014 enum class TableType : uint8_t {
3015 Table,
3016 TableCol,
3017 TableColGroup,
3018 TableRow,
3019 TableRowGroup,
3020 TableCell,
3022 MAX,
3025 enum class TableTypeBits : uint8_t { Count = 3 };
3027 static_assert(static_cast<uint8_t>(TableType::MAX) <
3028 (1 << (static_cast<uint8_t>(TableTypeBits::Count) + 1)),
3029 "TableType cannot fit with TableTypeBits::Count");
3030 TableType GetTableTypeFromFrame(nsIFrame* aFrame);
3032 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex,
3033 const TableType aType) {
3034 const uint32_t key = (aIndex << static_cast<uint8_t>(TableTypeBits::Count)) |
3035 static_cast<uint8_t>(aType);
3037 return static_cast<uint16_t>(key);
3040 static nsDisplayBackgroundImage* CreateBackgroundImage(
3041 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3042 const nsDisplayBackgroundImage::InitData& aBgData) {
3043 const auto index = aBgData.layer;
3045 if (aSecondaryFrame) {
3046 const auto tableType = GetTableTypeFromFrame(aFrame);
3047 const uint16_t tableItemIndex = CalculateTablePerFrameKey(index, tableType);
3049 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundImage>(
3050 aBuilder, aSecondaryFrame, tableItemIndex, aBgData, aFrame);
3053 return MakeDisplayItemWithIndex<nsDisplayBackgroundImage>(aBuilder, aFrame,
3054 index, aBgData);
3057 static nsDisplayThemedBackground* CreateThemedBackground(
3058 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3059 const nsRect& aBgRect) {
3060 if (aSecondaryFrame) {
3061 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3062 return MakeDisplayItemWithIndex<nsDisplayTableThemedBackground>(
3063 aBuilder, aSecondaryFrame, index, aBgRect, aFrame);
3066 return MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, aBgRect);
3069 static nsDisplayBackgroundColor* CreateBackgroundColor(
3070 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3071 nsRect& aBgRect, const ComputedStyle* aBgSC, nscolor aColor) {
3072 if (aSecondaryFrame) {
3073 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3074 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>(
3075 aBuilder, aSecondaryFrame, index, aBgRect, aBgSC, aColor, aFrame);
3078 return MakeDisplayItem<nsDisplayBackgroundColor>(aBuilder, aFrame, aBgRect,
3079 aBgSC, aColor);
3082 static void DealWithWindowsAppearanceHacks(nsIFrame* aFrame,
3083 nsDisplayListBuilder* aBuilder) {
3084 const auto& disp = *aFrame->StyleDisplay();
3086 // We use default appearance rather than effective appearance because we want
3087 // to handle when titlebar buttons that have appearance: none.
3088 const auto defaultAppearance = disp.mDefaultAppearance;
3089 if (MOZ_LIKELY(defaultAppearance == StyleAppearance::None)) {
3090 return;
3093 if (auto type = disp.GetWindowButtonType()) {
3094 if (auto* widget = aFrame->GetNearestWidget()) {
3095 auto rect = LayoutDevicePixel::FromAppUnitsToNearest(
3096 nsRect(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize()),
3097 aFrame->PresContext()->AppUnitsPerDevPixel());
3098 widget->SetWindowButtonRect(*type, rect);
3103 /*static*/
3104 AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3105 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3106 const nsRect& aBackgroundRect, nsDisplayList* aList,
3107 bool aAllowWillPaintBorderOptimization, const nsRect& aBackgroundOriginRect,
3108 nsIFrame* aSecondaryReferenceFrame,
3109 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
3110 aAutoBuildingDisplayList) {
3111 MOZ_ASSERT(!aFrame->IsCanvasFrame(),
3112 "We don't expect propagated canvas backgrounds here");
3113 #ifdef DEBUG
3115 nsIFrame* bgFrame = nsCSSRendering::FindBackgroundFrame(aFrame);
3116 MOZ_ASSERT(
3117 !bgFrame || bgFrame == aFrame,
3118 "Should only suppress backgrounds, never propagate to another frame");
3120 #endif
3122 DealWithWindowsAppearanceHacks(aFrame, aBuilder);
3124 const bool isThemed = aFrame->IsThemed();
3126 const ComputedStyle* bgSC = aFrame->Style();
3127 const nsStyleBackground* bg = bgSC->StyleBackground();
3128 const bool needsBackgroundColor =
3129 aBuilder->IsForEventDelivery() ||
3130 (EffectCompositor::HasAnimationsForCompositor(
3131 aFrame, DisplayItemType::TYPE_BACKGROUND_COLOR) &&
3132 !isThemed);
3133 if (!needsBackgroundColor && !isThemed && bg->IsTransparent(bgSC)) {
3134 return AppendedBackgroundType::None;
3137 bool drawBackgroundColor = false;
3138 bool drawBackgroundImage = false;
3139 nscolor color = NS_RGBA(0, 0, 0, 0);
3140 // Don't get background color / images if we propagated our background to the
3141 // canvas (that is, if FindBackgroundFrame is null). But don't early return
3142 // yet, since we might still need a background-color item for hit-testing.
3143 if (!isThemed && nsCSSRendering::FindBackgroundFrame(aFrame)) {
3144 color = nsCSSRendering::DetermineBackgroundColor(
3145 aFrame->PresContext(), bgSC, aFrame, drawBackgroundImage,
3146 drawBackgroundColor);
3149 if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
3150 color)) {
3151 return AppendedBackgroundType::None;
3154 const nsStyleBorder& border = *aFrame->StyleBorder();
3155 const bool willPaintBorder =
3156 aAllowWillPaintBorderOptimization && !isThemed &&
3157 !aFrame->StyleEffects()->HasBoxShadowWithInset(true) &&
3158 border.HasBorder();
3160 auto EnsureBuildingDisplayList = [&] {
3161 if (!aAutoBuildingDisplayList || *aAutoBuildingDisplayList) {
3162 return;
3164 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3165 aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3166 aBuilder->GetVisibleRect() + offset,
3167 aBuilder->GetDirtyRect() + offset);
3170 // An auxiliary list is necessary in case we have background blending; if that
3171 // is the case, background items need to be wrapped by a blend container to
3172 // isolate blending to the background
3173 nsDisplayList bgItemList(aBuilder);
3174 // Even if we don't actually have a background color to paint, we may still
3175 // need to create an item for hit testing and we still need to create an item
3176 // for background-color animations.
3177 if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) ||
3178 needsBackgroundColor) {
3179 EnsureBuildingDisplayList();
3180 Maybe<DisplayListClipState::AutoSaveRestore> clipState;
3181 nsRect bgColorRect = aBackgroundRect;
3182 if (!isThemed && !aBuilder->IsForEventDelivery()) {
3183 // Disable the will-paint-border optimization for background
3184 // colors with no border-radius. Enabling it for background colors
3185 // doesn't help much (there are no tiling issues) and clipping the
3186 // background breaks detection of the element's border-box being
3187 // opaque. For nonzero border-radius we still need it because we
3188 // want to inset the background if possible to avoid antialiasing
3189 // artifacts along the rounded corners.
3190 const bool useWillPaintBorderOptimization =
3191 willPaintBorder &&
3192 nsLayoutUtils::HasNonZeroCorner(border.mBorderRadius);
3194 nsCSSRendering::ImageLayerClipState clip;
3195 nsCSSRendering::GetImageLayerClip(
3196 bg->BottomLayer(), aFrame, border, aBackgroundRect, aBackgroundRect,
3197 useWillPaintBorderOptimization,
3198 aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3200 bgColorRect = bgColorRect.Intersect(clip.mBGClipArea);
3201 if (clip.mHasAdditionalBGClipArea) {
3202 bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea);
3204 if (clip.mHasRoundedCorners) {
3205 clipState.emplace(aBuilder);
3206 clipState->ClipContentDescendants(clip.mBGClipArea, clip.mRadii);
3210 nsDisplayBackgroundColor* bgItem = CreateBackgroundColor(
3211 aBuilder, aFrame, aSecondaryReferenceFrame, bgColorRect, bgSC,
3212 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0));
3214 if (bgItem) {
3215 bgItemList.AppendToTop(bgItem);
3219 if (isThemed) {
3220 nsDisplayThemedBackground* bgItem = CreateThemedBackground(
3221 aBuilder, aFrame, aSecondaryReferenceFrame, aBackgroundRect);
3223 if (bgItem) {
3224 bgItem->Init(aBuilder);
3225 bgItemList.AppendToTop(bgItem);
3228 if (!bgItemList.IsEmpty()) {
3229 aList->AppendToTop(&bgItemList);
3230 return AppendedBackgroundType::ThemedBackground;
3233 return AppendedBackgroundType::None;
3236 if (!drawBackgroundImage) {
3237 if (!bgItemList.IsEmpty()) {
3238 aList->AppendToTop(&bgItemList);
3239 return AppendedBackgroundType::Background;
3242 return AppendedBackgroundType::None;
3245 const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
3247 bool needBlendContainer = false;
3248 const nsRect& bgOriginRect =
3249 aBackgroundOriginRect.IsEmpty() ? aBackgroundRect : aBackgroundOriginRect;
3251 // Passing bg == nullptr in this macro will result in one iteration with
3252 // i = 0.
3253 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
3254 if (bg->mImage.mLayers[i].mImage.IsNone()) {
3255 continue;
3258 EnsureBuildingDisplayList();
3260 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3261 needBlendContainer = true;
3264 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3265 if (!aBuilder->IsForEventDelivery()) {
3266 const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
3267 SetBackgroundClipRegion(clipState, aFrame, layer, aBackgroundRect,
3268 willPaintBorder);
3271 nsDisplayList thisItemList(aBuilder);
3272 nsDisplayBackgroundImage::InitData bgData =
3273 nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect,
3274 bgSC);
3276 if (bgData.shouldFixToViewport) {
3277 auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
3278 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3279 aBuilder, aFrame, aBuilder->GetVisibleRect(),
3280 aBuilder->GetDirtyRect());
3282 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
3283 aBuilder);
3284 if (displayData) {
3285 asrSetter.SetCurrentActiveScrolledRoot(
3286 displayData->mContainingBlockActiveScrolledRoot);
3287 asrSetter.SetCurrentScrollParentId(displayData->mScrollParentId);
3288 if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
3289 // Override the dirty rect on the builder to be the dirty rect of
3290 // the viewport.
3291 // displayData->mDirtyRect is relative to the presshell's viewport
3292 // frame (the root frame), and we need it to be relative to aFrame.
3293 nsIFrame* rootFrame =
3294 aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
3295 // There cannot be any transforms between aFrame and rootFrame
3296 // because then bgData.shouldFixToViewport would have been false.
3297 nsRect visibleRect =
3298 displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame);
3299 aBuilder->SetVisibleRect(visibleRect);
3300 nsRect dirtyRect =
3301 displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
3302 aBuilder->SetDirtyRect(dirtyRect);
3306 nsDisplayBackgroundImage* bgItem = nullptr;
3308 // The clip is captured by the nsDisplayFixedPosition, so clear the
3309 // clip for the nsDisplayBackgroundImage inside.
3310 DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
3311 bgImageClip.Clear();
3312 bgItem = CreateBackgroundImage(aBuilder, aFrame,
3313 aSecondaryReferenceFrame, bgData);
3315 if (bgItem) {
3316 thisItemList.AppendToTop(
3317 nsDisplayFixedPosition::CreateForFixedBackground(
3318 aBuilder, aFrame, aSecondaryReferenceFrame, bgItem, i, asr));
3320 } else { // bgData.shouldFixToViewport == false
3321 nsDisplayBackgroundImage* bgItem = CreateBackgroundImage(
3322 aBuilder, aFrame, aSecondaryReferenceFrame, bgData);
3323 if (bgItem) {
3324 thisItemList.AppendToTop(bgItem);
3328 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3329 // asr is scrolled. Even if we wrap a fixed background layer, that's
3330 // fine, because the item will have a scrolled clip that limits the
3331 // item with respect to asr.
3332 if (aSecondaryReferenceFrame) {
3333 const auto tableType = GetTableTypeFromFrame(aFrame);
3334 const uint16_t index = CalculateTablePerFrameKey(i + 1, tableType);
3336 thisItemList.AppendNewToTopWithIndex<nsDisplayTableBlendMode>(
3337 aBuilder, aSecondaryReferenceFrame, index, &thisItemList,
3338 bg->mImage.mLayers[i].mBlendMode, asr, aFrame, true);
3339 } else {
3340 thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
3341 aBuilder, aFrame, i + 1, &thisItemList,
3342 bg->mImage.mLayers[i].mBlendMode, asr, true);
3345 bgItemList.AppendToTop(&thisItemList);
3348 if (needBlendContainer) {
3349 bgItemList.AppendToTop(
3350 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3351 aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr));
3354 if (!bgItemList.IsEmpty()) {
3355 aList->AppendToTop(&bgItemList);
3356 return AppendedBackgroundType::Background;
3359 return AppendedBackgroundType::None;
3362 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3363 // intersects aRect. Assumes that the unrounded border has already
3364 // been checked for intersection.
3365 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame,
3366 const nsPoint& aFrameToReferenceFrame,
3367 const nsRect& aTestRect) {
3368 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize())
3369 .Intersects(aTestRect)) {
3370 return false;
3373 nscoord radii[8];
3374 return !aFrame->GetBorderRadii(radii) ||
3375 nsLayoutUtils::RoundedRectIntersectsRect(
3376 nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii,
3377 aTestRect);
3380 // Returns TRUE if aContainedRect is guaranteed to be contained in
3381 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3382 // handled conservatively by returning FALSE in some situations where
3383 // a more thorough analysis could return TRUE.
3385 // See also RoundedRectIntersectsRect.
3386 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
3387 const nscoord aRadii[8],
3388 const nsRect& aContainedRect) {
3389 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii,
3390 aContainedRect);
3391 return rgn.Contains(aContainedRect);
3394 bool nsDisplayBackgroundImage::CanApplyOpacity(
3395 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3396 return CanBuildWebRenderDisplayItems(aManager, aBuilder);
3399 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3400 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3401 return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip !=
3402 StyleGeometryBox::Text &&
3403 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3404 aManager, *StyleFrame()->PresContext(), StyleFrame(),
3405 mBackgroundStyle->StyleBackground(), mLayer,
3406 aBuilder->GetBackgroundPaintFlags());
3409 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3410 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3411 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3412 nsDisplayListBuilder* aDisplayListBuilder) {
3413 if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(),
3414 aDisplayListBuilder)) {
3415 return false;
3418 uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
3419 bool dummy;
3420 nsCSSRendering::PaintBGParams params =
3421 nsCSSRendering::PaintBGParams::ForSingleLayer(
3422 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy),
3423 mBackgroundRect, StyleFrame(), paintFlags, mLayer,
3424 CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity());
3425 params.bgClipRect = &mBounds;
3426 ImgDrawResult result =
3427 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3428 params, aBuilder, aResources, aSc, aManager, this);
3429 if (result == ImgDrawResult::NOT_SUPPORTED) {
3430 return false;
3432 return true;
3435 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
3436 const nsRect& aRect,
3437 HitTestState* aState,
3438 nsTArray<nsIFrame*>* aOutFrames) {
3439 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3440 aOutFrames->AppendElement(mFrame);
3444 static nsRect GetInsideClipRect(const nsDisplayItem* aItem,
3445 StyleGeometryBox aClip, const nsRect& aRect,
3446 const nsRect& aBackgroundRect) {
3447 if (aRect.IsEmpty()) {
3448 return {};
3451 nsIFrame* frame = aItem->Frame();
3453 nsRect clipRect = aBackgroundRect;
3454 if (frame->IsCanvasFrame()) {
3455 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3456 clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
3457 } else if (aClip == StyleGeometryBox::PaddingBox ||
3458 aClip == StyleGeometryBox::ContentBox) {
3459 nsMargin border = frame->GetUsedBorder();
3460 if (aClip == StyleGeometryBox::ContentBox) {
3461 border += frame->GetUsedPadding();
3463 border.ApplySkipSides(frame->GetSkipSides());
3464 clipRect.Deflate(border);
3467 return clipRect.Intersect(aRect);
3470 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
3471 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3472 nsRegion result;
3473 *aSnap = false;
3475 if (!mBackgroundStyle) {
3476 return result;
3479 *aSnap = true;
3481 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3482 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3483 // which expects frames to be sent to it in content order, not reverse
3484 // content order which we'll produce here.
3485 // Of course, if there's only one frame in the flow, it doesn't matter.
3486 if (mFrame->StyleBorder()->mBoxDecorationBreak ==
3487 StyleBoxDecorationBreak::Clone ||
3488 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
3489 const nsStyleImageLayers::Layer& layer =
3490 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3491 if (layer.mImage.IsOpaque() && layer.mBlendMode == StyleBlend::Normal &&
3492 layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
3493 layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
3494 layer.mClip != StyleGeometryBox::Text) {
3495 result = GetInsideClipRect(this, layer.mClip, mBounds, mBackgroundRect);
3499 return result;
3502 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform(
3503 nsDisplayListBuilder* aBuilder) const {
3504 if (!mBackgroundStyle) {
3505 return Some(NS_RGBA(0, 0, 0, 0));
3507 return Nothing();
3510 nsRect nsDisplayBackgroundImage::GetPositioningArea() const {
3511 if (!mBackgroundStyle) {
3512 return nsRect();
3514 nsIFrame* attachedToFrame;
3515 bool transformedFixed;
3516 return nsCSSRendering::ComputeImageLayerPositioningArea(
3517 mFrame->PresContext(), mFrame, mBackgroundRect,
3518 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer],
3519 &attachedToFrame, &transformedFixed) +
3520 ToReferenceFrame();
3523 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3524 const {
3525 if (!mBackgroundStyle) {
3526 return false;
3529 nscoord radii[8];
3530 if (mFrame->GetBorderRadii(radii)) {
3531 // A change in the size of the positioning area might change the position
3532 // of the rounded corners.
3533 return true;
3536 const nsStyleImageLayers::Layer& layer =
3537 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3538 return layer.RenderingMightDependOnPositioningAreaSizeChange();
3541 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
3542 gfxContext* aCtx) {
3543 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &mBounds);
3546 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
3547 gfxContext* aCtx,
3548 const nsRect& aBounds,
3549 nsRect* aClipRect) {
3550 gfxContext* ctx = aCtx;
3551 StyleGeometryBox clip =
3552 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip;
3554 if (clip == StyleGeometryBox::Text) {
3555 if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect,
3556 aBuilder)) {
3557 return;
3561 nsCSSRendering::PaintBGParams params =
3562 nsCSSRendering::PaintBGParams::ForSingleLayer(
3563 *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
3564 aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER,
3565 1.0f);
3566 params.bgClipRect = aClipRect;
3567 Unused << nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
3569 if (clip == StyleGeometryBox::Text) {
3570 ctx->PopGroupAndBlend();
3574 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3575 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3576 nsRegion* aInvalidRegion) const {
3577 if (!mBackgroundStyle) {
3578 return;
3581 const auto* geometry =
3582 static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
3584 bool snap;
3585 nsRect bounds = GetBounds(aBuilder, &snap);
3586 nsRect positioningArea = GetPositioningArea();
3587 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
3588 (positioningArea.Size() != geometry->mPositioningArea.Size() &&
3589 RenderingMightDependOnPositioningAreaSizeChange())) {
3590 // Positioning area changed in a way that could cause everything to change,
3591 // so invalidate everything (both old and new painting areas).
3592 aInvalidRegion->Or(bounds, geometry->mBounds);
3593 return;
3595 if (!mDestRect.IsEqualInterior(geometry->mDestRect)) {
3596 // Dest area changed in a way that could cause everything to change,
3597 // so invalidate everything (both old and new painting areas).
3598 aInvalidRegion->Or(bounds, geometry->mBounds);
3599 return;
3601 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3602 // Positioning area is unchanged, so invalidate just the change in the
3603 // painting area.
3604 aInvalidRegion->Xor(bounds, geometry->mBounds);
3608 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder,
3609 bool* aSnap) const {
3610 *aSnap = true;
3611 return mBounds;
3614 nsRect nsDisplayBackgroundImage::GetBoundsInternal(
3615 nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) {
3616 // This allows nsDisplayTableBackgroundImage to change the frame used for
3617 // bounds calculation.
3618 nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame;
3620 nsPresContext* presContext = frame->PresContext();
3622 if (!mBackgroundStyle) {
3623 return nsRect();
3626 nsRect clipRect = mBackgroundRect;
3627 if (frame->IsCanvasFrame()) {
3628 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3629 clipRect = canvasFrame->CanvasArea() + ToReferenceFrame();
3631 const nsStyleImageLayers::Layer& layer =
3632 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3633 return nsCSSRendering::GetBackgroundLayerRect(
3634 presContext, frame, mBackgroundRect, clipRect, layer,
3635 aBuilder->GetBackgroundPaintFlags());
3638 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3639 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aData,
3640 nsIFrame* aCellFrame)
3641 : nsDisplayBackgroundImage(aBuilder, aFrame, aData, aCellFrame),
3642 mStyleFrame(aCellFrame) {
3643 if (aBuilder->IsRetainingDisplayList()) {
3644 mStyleFrame->AddDisplayItem(this);
3648 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3649 if (mStyleFrame) {
3650 mStyleFrame->RemoveDisplayItem(this);
3654 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const {
3655 bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
3656 aRect += ToReferenceFrame();
3657 return result;
3660 nsDisplayThemedBackground::nsDisplayThemedBackground(
3661 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3662 const nsRect& aBackgroundRect)
3663 : nsPaintedDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) {
3664 MOZ_COUNT_CTOR(nsDisplayThemedBackground);
3667 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) {
3668 const nsStyleDisplay* disp = StyleFrame()->StyleDisplay();
3669 mAppearance = disp->EffectiveAppearance();
3670 StyleFrame()->IsThemed(disp, &mThemeTransparency);
3672 // Perform necessary RegisterThemeGeometry
3673 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3674 nsITheme::ThemeGeometryType type =
3675 theme->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance);
3676 if (type != nsITheme::eThemeGeometryTypeUnknown) {
3677 RegisterThemeGeometry(aBuilder, this, StyleFrame(), type);
3680 mBounds = GetBoundsInternal();
3683 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) {
3684 aStream << " (themed, appearance:" << (int)mAppearance << ")";
3687 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
3688 const nsRect& aRect,
3689 HitTestState* aState,
3690 nsTArray<nsIFrame*>* aOutFrames) {
3691 // Assume that any point in our background rect is a hit.
3692 if (mBackgroundRect.Intersects(aRect)) {
3693 aOutFrames->AppendElement(mFrame);
3697 nsRegion nsDisplayThemedBackground::GetOpaqueRegion(
3698 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3699 nsRegion result;
3700 *aSnap = false;
3702 if (mThemeTransparency == nsITheme::eOpaque) {
3703 *aSnap = true;
3704 result = mBackgroundRect;
3706 return result;
3709 Maybe<nscolor> nsDisplayThemedBackground::IsUniform(
3710 nsDisplayListBuilder* aBuilder) const {
3711 return Nothing();
3714 nsRect nsDisplayThemedBackground::GetPositioningArea() const {
3715 return mBackgroundRect;
3718 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
3719 gfxContext* aCtx) {
3720 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), nullptr);
3723 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
3724 gfxContext* aCtx,
3725 const nsRect& aBounds,
3726 nsRect* aClipRect) {
3727 // XXXzw this ignores aClipRect.
3728 nsPresContext* presContext = StyleFrame()->PresContext();
3729 nsITheme* theme = presContext->Theme();
3730 nsRect drawing(mBackgroundRect);
3731 theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(),
3732 mAppearance, &drawing);
3733 drawing.IntersectRect(drawing, aBounds);
3734 theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect,
3735 drawing);
3738 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3739 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3740 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3741 nsDisplayListBuilder* aDisplayListBuilder) {
3742 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3743 return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc,
3744 aManager, StyleFrame(),
3745 mAppearance, mBackgroundRect);
3748 bool nsDisplayThemedBackground::IsWindowActive() const {
3749 return !mFrame->PresContext()->Document()->IsTopLevelWindowInactive();
3752 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3753 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3754 nsRegion* aInvalidRegion) const {
3755 const auto* geometry =
3756 static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
3758 bool snap;
3759 nsRect bounds = GetBounds(aBuilder, &snap);
3760 nsRect positioningArea = GetPositioningArea();
3761 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
3762 // Invalidate everything (both old and new painting areas).
3763 aInvalidRegion->Or(bounds, geometry->mBounds);
3764 return;
3766 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3767 // Positioning area is unchanged, so invalidate just the change in the
3768 // painting area.
3769 aInvalidRegion->Xor(bounds, geometry->mBounds);
3771 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3772 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
3773 IsWindowActive() != geometry->mWindowIsActive) {
3774 aInvalidRegion->Or(*aInvalidRegion, bounds);
3778 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder,
3779 bool* aSnap) const {
3780 *aSnap = true;
3781 return mBounds;
3784 nsRect nsDisplayThemedBackground::GetBoundsInternal() {
3785 nsPresContext* presContext = mFrame->PresContext();
3787 nsRect r = mBackgroundRect - ToReferenceFrame();
3788 presContext->Theme()->GetWidgetOverflow(
3789 presContext->DeviceContext(), mFrame,
3790 mFrame->StyleDisplay()->EffectiveAppearance(), &r);
3791 return r + ToReferenceFrame();
3794 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
3795 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder,
3796 gfxContext* aCtx) {
3797 mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(),
3798 mFrame, ToReferenceFrame(), mColor);
3800 #endif
3802 bool nsDisplayBackgroundColor::CanApplyOpacity(
3803 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3804 // Don't apply opacity if the background color is animated since the color is
3805 // going to be changed on the compositor.
3806 return !EffectCompositor::HasAnimationsForCompositor(
3807 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR);
3810 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3811 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3812 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3813 nsDisplayListBuilder* aDisplayListBuilder) {
3814 gfx::sRGBColor color = mColor;
3815 color.a *= aBuilder.GetInheritedOpacity();
3817 if (color == sRGBColor() &&
3818 !EffectCompositor::HasAnimationsForCompositor(
3819 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3820 return true;
3823 if (HasBackgroundClipText()) {
3824 return false;
3827 uint64_t animationsId = 0;
3828 // We don't support background-color animations on table elements yet.
3829 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
3830 animationsId =
3831 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
3834 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
3835 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3836 wr::LayoutRect r = wr::ToLayoutRect(bounds);
3838 if (animationsId) {
3839 wr::WrAnimationProperty prop{
3840 wr::WrAnimationType::BackgroundColor,
3841 animationsId,
3843 aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(),
3844 wr::ToColorF(ToDeviceColor(color)), &prop);
3845 } else {
3846 aBuilder.StartGroup(this);
3847 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
3848 wr::ToColorF(ToDeviceColor(color)));
3849 aBuilder.FinishGroup();
3852 return true;
3855 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder,
3856 gfxContext* aCtx,
3857 const DisplayItemClip& aClip) {
3858 MOZ_ASSERT(!HasBackgroundClipText());
3860 if (mColor == sRGBColor()) {
3861 return;
3864 nsRect fillRect = mBackgroundRect;
3865 if (aClip.HasClip()) {
3866 fillRect.IntersectRect(fillRect, aClip.GetClipRect());
3869 DrawTarget* dt = aCtx->GetDrawTarget();
3870 int32_t A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
3871 Rect bounds = ToRect(nsLayoutUtils::RectToGfxRect(fillRect, A2D));
3872 MaybeSnapToDevicePixels(bounds, *dt);
3873 ColorPattern fill(ToDeviceColor(mColor));
3875 if (aClip.GetRoundedRectCount()) {
3876 MOZ_ASSERT(aClip.GetRoundedRectCount() == 1);
3878 AutoTArray<DisplayItemClip::RoundedRect, 1> roundedRect;
3879 aClip.AppendRoundedRects(&roundedRect);
3881 bool pushedClip = false;
3882 if (!fillRect.Contains(roundedRect[0].mRect)) {
3883 dt->PushClipRect(bounds);
3884 pushedClip = true;
3887 RectCornerRadii pixelRadii;
3888 nsCSSRendering::ComputePixelRadii(roundedRect[0].mRadii, A2D, &pixelRadii);
3889 dt->FillRoundedRect(
3890 RoundedRect(NSRectToSnappedRect(roundedRect[0].mRect, A2D, *dt),
3891 pixelRadii),
3892 fill);
3893 if (pushedClip) {
3894 dt->PopClip();
3896 } else {
3897 dt->FillRect(bounds, fill);
3901 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
3902 gfxContext* aCtx) {
3903 if (mColor == sRGBColor()) {
3904 return;
3907 #if 0
3908 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3909 // results in a precision induced rounding issue that makes the rect one
3910 // pixel shorter in rare cases. Disabled in favor of the old code for now.
3911 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3912 // reproduce the bug.
3914 // TODO:
3915 // This new path does not include support for background-clip:text; need to
3916 // be fixed if/when we switch to this new code path.
3918 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
3920 Rect rect = NSRectToSnappedRect(mBackgroundRect,
3921 mFrame->PresContext()->AppUnitsPerDevPixel(),
3922 aDrawTarget);
3923 ColorPattern color(ToDeviceColor(mColor));
3924 aDrawTarget.FillRect(rect, color);
3925 #else
3926 gfxContext* ctx = aCtx;
3927 gfxRect bounds = nsLayoutUtils::RectToGfxRect(
3928 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3930 if (HasBackgroundClipText()) {
3931 if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
3932 return;
3935 ctx->SetColor(mColor);
3936 ctx->NewPath();
3937 ctx->SnappedRectangle(bounds);
3938 ctx->Fill();
3939 ctx->PopGroupAndBlend();
3940 return;
3943 ctx->SetColor(mColor);
3944 ctx->NewPath();
3945 ctx->SnappedRectangle(bounds);
3946 ctx->Fill();
3947 #endif
3950 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion(
3951 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3952 *aSnap = false;
3954 if (mColor.a != 1 ||
3955 // Even if the current alpha channel is 1, we treat this item as if it's
3956 // non-opaque if there is a background-color animation since the animation
3957 // might change the alpha channel.
3958 EffectCompositor::HasAnimationsForCompositor(
3959 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3960 return nsRegion();
3963 if (!mHasStyle || HasBackgroundClipText()) {
3964 return nsRegion();
3967 *aSnap = true;
3968 return GetInsideClipRect(this, mBottomLayerClip, mBackgroundRect,
3969 mBackgroundRect);
3972 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform(
3973 nsDisplayListBuilder* aBuilder) const {
3974 return Some(mColor.ToABGR());
3977 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
3978 const nsRect& aRect,
3979 HitTestState* aState,
3980 nsTArray<nsIFrame*>* aOutFrames) {
3981 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3982 // aRect doesn't intersect our border-radius curve.
3983 return;
3986 aOutFrames->AppendElement(mFrame);
3989 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) {
3990 aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << ","
3991 << mColor.a << ")";
3992 aStream << " backgroundRect" << mBackgroundRect;
3995 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
3996 bool* aSnap) const {
3997 *aSnap = false;
3998 return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
4001 nsRect nsDisplayOutline::GetInnerRect() const {
4002 if (nsRect* savedOutlineInnerRect =
4003 mFrame->GetProperty(nsIFrame::OutlineInnerRectProperty())) {
4004 return *savedOutlineInnerRect;
4006 return mFrame->GetRectRelativeToSelf();
4009 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4010 // TODO join outlines together
4011 MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(),
4012 "Should have not created a nsDisplayOutline!");
4014 nsRect rect = GetInnerRect() + ToReferenceFrame();
4015 nsPresContext* pc = mFrame->PresContext();
4016 if (IsThemedOutline()) {
4017 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
4018 pc->Theme()->DrawWidgetBackground(aCtx, mFrame,
4019 StyleAppearance::FocusOutline, rect,
4020 GetPaintRect(aBuilder, aCtx));
4021 return;
4024 nsCSSRendering::PaintNonThemedOutline(
4025 pc, *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), rect, mFrame->Style());
4028 bool nsDisplayOutline::IsThemedOutline() const {
4029 #ifdef DEBUG
4030 nsPresContext* pc = mFrame->PresContext();
4031 MOZ_ASSERT(
4032 pc->Theme()->ThemeSupportsWidget(pc, mFrame,
4033 StyleAppearance::FocusOutline),
4034 "All of our supported platforms have support for themed focus-outlines");
4035 #endif
4036 return mFrame->StyleOutline()->mOutlineStyle.IsAuto();
4039 bool nsDisplayOutline::CreateWebRenderCommands(
4040 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4041 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4042 nsDisplayListBuilder* aDisplayListBuilder) {
4043 nsPresContext* pc = mFrame->PresContext();
4044 nsRect rect = GetInnerRect() + ToReferenceFrame();
4045 if (IsThemedOutline()) {
4046 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
4047 return pc->Theme()->CreateWebRenderCommandsForWidget(
4048 aBuilder, aResources, aSc, aManager, mFrame,
4049 StyleAppearance::FocusOutline, rect);
4052 bool dummy;
4053 Maybe<nsCSSBorderRenderer> borderRenderer =
4054 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4055 pc, /* aDrawTarget = */ nullptr, mFrame,
4056 GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style());
4058 if (!borderRenderer) {
4059 // No border renderer means "there is no outline".
4060 // Paint nothing and return success.
4061 return true;
4064 borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
4065 return true;
4068 bool nsDisplayOutline::HasRadius() const {
4069 const auto& radius = mFrame->StyleBorder()->mBorderRadius;
4070 return !nsLayoutUtils::HasNonZeroCorner(radius);
4073 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const {
4074 const nsStyleOutline* outline = mFrame->StyleOutline();
4075 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
4076 if (borderBox.Contains(aRect) && !HasRadius() &&
4077 outline->mOutlineOffset.ToCSSPixels() >= 0.0f) {
4078 // aRect is entirely inside the border-rect, and the outline isn't rendered
4079 // inside the border-rect, so the outline is not visible.
4080 return true;
4082 return false;
4085 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
4086 const nsRect& aRect, HitTestState* aState,
4087 nsTArray<nsIFrame*>* aOutFrames) {
4088 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4089 // aRect doesn't intersect our border-radius curve.
4090 return;
4093 aOutFrames->AppendElement(mFrame);
4096 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4097 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4098 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4099 nsDisplayListBuilder* aDisplayListBuilder) {
4100 return true;
4103 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4104 return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
4107 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) {
4108 mOverrideZIndex = Some(aZIndex);
4111 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
4112 nsIFrame* aCaretFrame)
4113 : nsPaintedDisplayItem(aBuilder, aCaretFrame),
4114 mCaret(aBuilder->GetCaret()),
4115 mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) {
4116 MOZ_COUNT_CTOR(nsDisplayCaret);
4117 // The presence of a caret doesn't change the overflow rect
4118 // of the owning frame, so the normal building rect might not
4119 // include the caret at all. We use MarkFrameForDisplay to ensure
4120 // we build this item, and here we override the building rect
4121 // to cover the pixels we're going to draw.
4122 SetBuildingRect(mBounds);
4125 #ifdef NS_BUILD_REFCNT_LOGGING
4126 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret); }
4127 #endif
4129 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder,
4130 bool* aSnap) const {
4131 *aSnap = true;
4132 // The caret returns a rect in the coordinates of mFrame.
4133 return mBounds;
4136 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4137 // Note: Because we exist, we know that the caret is visible, so we don't
4138 // need to check for the caret's visibility.
4139 mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
4142 bool nsDisplayCaret::CreateWebRenderCommands(
4143 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4144 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4145 nsDisplayListBuilder* aDisplayListBuilder) {
4146 using namespace layers;
4147 nsRect caretRect;
4148 nsRect hookRect;
4149 nscolor caretColor;
4150 nsIFrame* frame =
4151 mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor);
4152 MOZ_ASSERT(frame == mFrame, "We're referring different frame");
4153 if (!frame) {
4154 return true;
4157 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
4158 gfx::DeviceColor color = ToDeviceColor(caretColor);
4159 LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
4160 caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
4161 LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
4162 hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
4164 wr::LayoutRect caret = wr::ToLayoutRect(devCaretRect);
4165 wr::LayoutRect hook = wr::ToLayoutRect(devHookRect);
4167 // Note, WR will pixel snap anything that is layout aligned.
4168 aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), false, false,
4169 wr::ToColorF(color));
4171 if (!devHookRect.IsEmpty()) {
4172 aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), false, false,
4173 wr::ToColorF(color));
4175 return true;
4178 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder,
4179 nsIFrame* aFrame)
4180 : nsPaintedDisplayItem(aBuilder, aFrame) {
4181 MOZ_COUNT_CTOR(nsDisplayBorder);
4183 mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
4186 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const {
4187 nsRect paddingRect = GetPaddingRect();
4188 const nsStyleBorder* styleBorder;
4189 if (paddingRect.Contains(aRect) &&
4190 !(styleBorder = mFrame->StyleBorder())->IsBorderImageSizeAvailable() &&
4191 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
4192 // aRect is entirely inside the content rect, and no part
4193 // of the border is rendered inside the content rect, so we are not
4194 // visible
4195 // Skip this if there's a border-image (which draws a background
4196 // too) or if there is a border-radius (which makes the border draw
4197 // further in).
4198 return true;
4201 return false;
4204 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry(
4205 nsDisplayListBuilder* aBuilder) {
4206 return new nsDisplayBorderGeometry(this, aBuilder);
4209 void nsDisplayBorder::ComputeInvalidationRegion(
4210 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4211 nsRegion* aInvalidRegion) const {
4212 const auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
4213 bool snap;
4215 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
4216 // We can probably get away with only invalidating the difference
4217 // between the border and padding rects, but the XUL ui at least
4218 // is apparently painting a background with this?
4219 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4223 bool nsDisplayBorder::CreateWebRenderCommands(
4224 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4225 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4226 nsDisplayListBuilder* aDisplayListBuilder) {
4227 nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
4229 ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder(
4230 this, mFrame, rect, aBuilder, aResources, aSc, aManager,
4231 aDisplayListBuilder);
4233 if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
4234 return false;
4236 return true;
4239 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4240 nsPoint offset = ToReferenceFrame();
4242 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
4243 ? PaintBorderFlags::SyncDecodeImages
4244 : PaintBorderFlags();
4246 Unused << nsCSSRendering::PaintBorder(
4247 mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
4248 nsRect(offset, mFrame->GetSize()), mFrame->Style(), flags,
4249 mFrame->GetSkipSides());
4252 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder,
4253 bool* aSnap) const {
4254 *aSnap = true;
4255 return mBounds;
4258 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
4259 gfxContext* aCtx) {
4260 nsPoint offset = ToReferenceFrame();
4261 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4262 nsPresContext* presContext = mFrame->PresContext();
4264 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
4266 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
4267 GetPaintRect(aBuilder, aCtx), 1.0f);
4270 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
4271 bool* aSnap) const {
4272 *aSnap = false;
4273 return mBounds;
4276 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() {
4277 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
4278 ToReferenceFrame();
4281 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const {
4282 nsPoint origin = ToReferenceFrame();
4283 nsRect frameRect(origin, mFrame->GetSize());
4284 if (!frameRect.Contains(aRect)) {
4285 return false;
4288 // the visible region is entirely inside the border-rect, and box shadows
4289 // never render within the border-rect (unless there's a border radius).
4290 nscoord twipsRadii[8];
4291 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
4292 if (!hasBorderRadii) {
4293 return true;
4296 return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
4299 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4300 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4301 if (shadows.IsEmpty()) {
4302 return false;
4305 bool hasBorderRadius;
4306 // We don't support native themed things yet like box shadows around
4307 // input buttons.
4309 // TODO(emilio): The non-native theme could provide the right rect+radius
4310 // instead relatively painlessly, if we find this causes performance issues or
4311 // what not.
4312 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4315 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4316 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4317 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4318 nsDisplayListBuilder* aDisplayListBuilder) {
4319 if (!CanBuildWebRenderDisplayItems()) {
4320 return false;
4323 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4324 nsPoint offset = ToReferenceFrame();
4325 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4326 bool snap;
4327 nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
4329 bool hasBorderRadius;
4330 bool nativeTheme =
4331 nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4333 // Don't need the full size of the shadow rect like we do in
4334 // nsCSSRendering since WR takes care of calculations for blur
4335 // and spread radius.
4336 nsRect frameRect =
4337 nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
4339 RectCornerRadii borderRadii;
4340 if (hasBorderRadius) {
4341 hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
4342 mFrame, borderRadii);
4345 // Everything here is in app units, change to device units.
4346 LayoutDeviceRect clipRect =
4347 LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel);
4348 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4349 MOZ_ASSERT(!shadows.IsEmpty());
4351 for (const auto& shadow : Reversed(shadows)) {
4352 if (shadow.inset) {
4353 continue;
4356 float blurRadius =
4357 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4358 gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor(
4359 shadow.base, mFrame, aBuilder.GetInheritedOpacity());
4361 // We don't move the shadow rect here since WR does it for us
4362 // Now translate everything to device pixels.
4363 const nsRect& shadowRect = frameRect;
4364 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4365 nsPoint(shadow.base.horizontal.ToAppUnits(),
4366 shadow.base.vertical.ToAppUnits()),
4367 appUnitsPerDevPixel);
4369 LayoutDeviceRect deviceBox =
4370 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4371 wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
4372 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4374 LayoutDeviceSize zeroSize;
4375 wr::BorderRadius borderRadius =
4376 wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
4377 if (hasBorderRadius) {
4378 borderRadius = wr::ToBorderRadius(
4379 LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
4380 LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
4381 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
4382 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
4385 float spreadRadius =
4386 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4388 aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
4389 deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
4390 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius,
4391 spreadRadius, borderRadius,
4392 wr::BoxShadowClipMode::Outset);
4395 return true;
4398 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4399 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4400 nsRegion* aInvalidRegion) const {
4401 const auto* geometry =
4402 static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
4403 bool snap;
4404 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
4405 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
4406 nsRegion oldShadow, newShadow;
4407 nscoord dontCare[8];
4408 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
4409 if (hasBorderRadius) {
4410 // If we have rounded corners then we need to invalidate the frame area
4411 // too since we paint into it.
4412 oldShadow = geometry->mBounds;
4413 newShadow = GetBounds(aBuilder, &snap);
4414 } else {
4415 oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
4416 newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
4418 aInvalidRegion->Or(oldShadow, newShadow);
4422 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
4423 gfxContext* aCtx) {
4424 nsPoint offset = ToReferenceFrame();
4425 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4426 nsPresContext* presContext = mFrame->PresContext();
4428 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS);
4430 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
4433 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4434 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4435 const nsPoint& aReferenceOffset) {
4436 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4437 if (shadows.IsEmpty()) {
4438 // Means we don't have to paint anything
4439 return true;
4442 bool hasBorderRadius;
4443 bool nativeTheme =
4444 nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
4446 // We don't support native themed things yet like box shadows around
4447 // input buttons.
4448 return !nativeTheme;
4451 /* static */
4452 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4453 wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
4454 nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect) {
4455 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) {
4456 return;
4459 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
4461 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4463 LayoutDeviceRect clipRect =
4464 LayoutDeviceRect::FromAppUnits(aVisibleRect, appUnitsPerDevPixel);
4466 for (const auto& shadow : Reversed(shadows)) {
4467 if (!shadow.inset) {
4468 continue;
4471 nsRect shadowRect =
4472 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
4473 RectCornerRadii innerRadii;
4474 nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
4476 // Now translate everything to device pixels.
4477 LayoutDeviceRect deviceBoxRect =
4478 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4479 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4480 sRGBColor shadowColor =
4481 nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0);
4483 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4484 nsPoint(shadow.base.horizontal.ToAppUnits(),
4485 shadow.base.vertical.ToAppUnits()),
4486 appUnitsPerDevPixel);
4488 float blurRadius =
4489 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4491 wr::BorderRadius borderRadius = wr::ToBorderRadius(
4492 LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
4493 LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
4494 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
4495 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
4496 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4497 float spreadRadius =
4498 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4500 aBuilder.PushBoxShadow(
4501 wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
4502 !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
4503 wr::ToLayoutVector2D(shadowOffset),
4504 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, spreadRadius,
4505 borderRadius, wr::BoxShadowClipMode::Inset);
4509 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4510 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4511 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4512 nsDisplayListBuilder* aDisplayListBuilder) {
4513 if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame,
4514 ToReferenceFrame())) {
4515 return false;
4518 bool snap;
4519 nsRect visible = GetBounds(aDisplayListBuilder, &snap);
4520 nsPoint offset = ToReferenceFrame();
4521 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4522 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4523 aBuilder, aSc, visible, mFrame, borderRect);
4525 return true;
4528 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4529 nsIFrame* aFrame, nsDisplayList* aList)
4530 : nsDisplayWrapList(aBuilder, aFrame, aList,
4531 aBuilder->CurrentActiveScrolledRoot(), false) {}
4533 nsDisplayWrapList::nsDisplayWrapList(
4534 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4535 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
4536 : nsPaintedDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
4537 mList(aBuilder),
4538 mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
4539 mOverrideZIndex(0),
4540 mHasZIndexOverride(false),
4541 mClearingClipChain(aClearClipChain) {
4542 MOZ_COUNT_CTOR(nsDisplayWrapList);
4544 mBaseBuildingRect = GetBuildingRect();
4546 mListPtr = &mList;
4547 mListPtr->AppendToTop(aList);
4548 mOriginalClipChain = mClipChain;
4549 nsDisplayWrapList::UpdateBounds(aBuilder);
4552 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4553 nsIFrame* aFrame, nsDisplayItem* aItem)
4554 : nsPaintedDisplayItem(aBuilder, aFrame,
4555 aBuilder->CurrentActiveScrolledRoot()),
4556 mList(aBuilder),
4557 mOverrideZIndex(0),
4558 mHasZIndexOverride(false) {
4559 MOZ_COUNT_CTOR(nsDisplayWrapList);
4561 mBaseBuildingRect = GetBuildingRect();
4563 mListPtr = &mList;
4564 mListPtr->AppendToTop(aItem);
4565 mOriginalClipChain = mClipChain;
4566 nsDisplayWrapList::UpdateBounds(aBuilder);
4568 if (!aFrame || !aFrame->IsTransformed()) {
4569 return;
4572 // See the previous nsDisplayWrapList constructor
4573 if (aItem->Frame() == aFrame) {
4574 mToReferenceFrame = aItem->ToReferenceFrame();
4577 nsRect visible = aBuilder->GetVisibleRect() +
4578 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
4580 SetBuildingRect(visible);
4583 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList); }
4585 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder,
4586 const nsRect& aRect, HitTestState* aState,
4587 nsTArray<nsIFrame*>* aOutFrames) {
4588 mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames);
4591 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder,
4592 bool* aSnap) const {
4593 *aSnap = false;
4594 return mBounds;
4597 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4598 bool* aSnap) const {
4599 *aSnap = false;
4600 bool snap;
4601 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
4602 GetBounds(aBuilder, &snap));
4605 Maybe<nscolor> nsDisplayWrapList::IsUniform(
4606 nsDisplayListBuilder* aBuilder) const {
4607 // We could try to do something but let's conservatively just return Nothing.
4608 return Nothing();
4611 void nsDisplayWrapper::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4612 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4615 nsRect nsDisplayWrapList::GetComponentAlphaBounds(
4616 nsDisplayListBuilder* aBuilder) const {
4617 return mListPtr->GetComponentAlphaBounds(aBuilder);
4620 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4621 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4622 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4623 nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList) {
4624 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4625 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
4626 aNewClipList);
4627 return true;
4630 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder,
4631 nsIFrame* aFrame, nsDisplayList* aList,
4632 nsDisplayItemWrapper* aWrapper) {
4633 if (!aList->GetTop()) {
4634 return NS_OK;
4636 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
4637 if (!item) {
4638 return NS_ERROR_OUT_OF_MEMORY;
4640 // aList was emptied
4641 aList->AppendToTop(item);
4642 return NS_OK;
4645 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
4646 nsDisplayList* aList,
4647 nsDisplayItemWrapper* aWrapper) {
4648 for (nsDisplayItem* item : aList->TakeItems()) {
4649 item = aWrapper->WrapItem(aBuilder, item);
4650 if (!item) {
4651 return NS_ERROR_OUT_OF_MEMORY;
4653 aList->AppendToTop(item);
4655 // aList was emptied
4656 return NS_OK;
4659 nsresult nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
4660 nsIFrame* aFrame,
4661 const nsDisplayListSet& aIn,
4662 const nsDisplayListSet& aOut) {
4663 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
4664 NS_ENSURE_SUCCESS(rv, rv);
4666 if (&aOut == &aIn) {
4667 return NS_OK;
4669 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
4670 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
4671 aOut.Floats()->AppendToTop(aIn.Floats());
4672 aOut.Content()->AppendToTop(aIn.Content());
4673 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
4674 aOut.Outlines()->AppendToTop(aIn.Outlines());
4675 return NS_OK;
4678 nsresult nsDisplayItemWrapper::WrapListsInPlace(
4679 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4680 const nsDisplayListSet& aLists) {
4681 nsresult rv;
4682 if (WrapBorderBackground()) {
4683 // Our border-backgrounds are in-flow
4684 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
4685 NS_ENSURE_SUCCESS(rv, rv);
4687 // Our block border-backgrounds are in-flow
4688 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
4689 NS_ENSURE_SUCCESS(rv, rv);
4690 // The floats are not in flow
4691 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
4692 NS_ENSURE_SUCCESS(rv, rv);
4693 // Our child content is in flow
4694 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
4695 NS_ENSURE_SUCCESS(rv, rv);
4696 // The positioned descendants may not be in-flow
4697 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
4698 NS_ENSURE_SUCCESS(rv, rv);
4699 // The outlines may not be in-flow
4700 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
4703 nsDisplayOpacity::nsDisplayOpacity(
4704 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4705 const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsOnly,
4706 bool aNeedsActiveLayer, bool aWrapsBackdropFilter)
4707 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4708 mOpacity(aFrame->StyleEffects()->mOpacity),
4709 mForEventsOnly(aForEventsOnly),
4710 mNeedsActiveLayer(aNeedsActiveLayer),
4711 mChildOpacityState(ChildOpacityState::Unknown),
4712 mWrapsBackdropFilter(aWrapsBackdropFilter) {
4713 MOZ_COUNT_CTOR(nsDisplayOpacity);
4716 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder,
4717 const nsRect& aRect,
4718 nsDisplayItem::HitTestState* aState,
4719 nsTArray<nsIFrame*>* aOutFrames) {
4720 AutoRestore<float> opacity(aState->mCurrentOpacity);
4721 aState->mCurrentOpacity *= mOpacity;
4723 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4724 // only consider fully opaque items? Or make this configurable somehow?
4725 if (aBuilder->HitTestIsForVisibility() && mOpacity == 0.0f) {
4726 return;
4728 nsDisplayWrapList::HitTest(aBuilder, aRect, aState, aOutFrames);
4731 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4732 bool* aSnap) const {
4733 *aSnap = false;
4734 // The only time where mOpacity == 1.0 should be when we have will-change.
4735 // We could report this as opaque then but when the will-change value starts
4736 // animating the element would become non opaque and could cause repaints.
4737 return nsRegion();
4740 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4741 if (GetOpacity() == 0.0f) {
4742 return;
4745 if (GetOpacity() == 1.0f) {
4746 GetChildren()->Paint(aBuilder, aCtx,
4747 mFrame->PresContext()->AppUnitsPerDevPixel());
4748 return;
4751 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4752 // allocation.
4753 aCtx->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4754 GetChildren()->Paint(aBuilder, aCtx,
4755 mFrame->PresContext()->AppUnitsPerDevPixel());
4756 aCtx->GetDrawTarget()->PopLayer();
4759 /* static */
4760 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
4761 nsIFrame* aFrame) {
4762 return EffectCompositor::HasAnimationsForCompositor(
4763 aFrame, DisplayItemType::TYPE_OPACITY) ||
4764 (ActiveLayerTracker::IsStyleAnimated(
4765 aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()));
4768 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager,
4769 nsDisplayListBuilder* aBuilder) const {
4770 return !EffectCompositor::HasAnimationsForCompositor(
4771 mFrame, DisplayItemType::TYPE_OPACITY);
4774 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4775 // children that don't overlap and can all apply the opacity to themselves.
4776 static const size_t kOpacityMaxChildCount = 3;
4778 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4779 // are likely have more child items than |kOpacityMaxChildCount|.
4780 static const size_t kOpacityMaxListSize = kOpacityMaxChildCount * 2;
4783 * Recursively iterates through |aList| and collects at most
4784 * |kOpacityMaxChildCount| display item pointers to items that return true for
4785 * CanApplyOpacity(). The item pointers are added to |aArray|.
4787 * LayerEventRegions and WrapList items are ignored.
4789 * We need to do this recursively, because the child display items might contain
4790 * nested nsDisplayWrapLists.
4792 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4793 * item that returns false for CanApplyOpacity() is encountered.
4794 * Otherwise returns true.
4796 static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager,
4797 nsDisplayListBuilder* aBuilder,
4798 nsDisplayList* aList,
4799 nsTArray<nsPaintedDisplayItem*>& aArray) {
4800 if (aList->Length() > kOpacityMaxListSize) {
4801 // Exit early, since |aList| will likely contain more than
4802 // |kOpacityMaxChildCount| items.
4803 return false;
4806 for (nsDisplayItem* i : *aList) {
4807 const DisplayItemType type = i->GetType();
4809 if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
4810 continue;
4813 // Descend only into wraplists.
4814 if (type == DisplayItemType::TYPE_WRAP_LIST ||
4815 type == DisplayItemType::TYPE_CONTAINER) {
4816 // The current display item has children, process them first.
4817 if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(),
4818 aArray)) {
4819 return false;
4822 continue;
4825 if (aArray.Length() == kOpacityMaxChildCount) {
4826 return false;
4829 auto* item = i->AsPaintedDisplayItem();
4830 if (!item || !item->CanApplyOpacity(aManager, aBuilder)) {
4831 return false;
4834 aArray.AppendElement(item);
4837 return true;
4840 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager,
4841 nsDisplayListBuilder* aBuilder) {
4842 if (mChildOpacityState == ChildOpacityState::Deferred) {
4843 return false;
4846 // Iterate through the child display list and copy at most
4847 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4848 AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items;
4849 if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) {
4850 mChildOpacityState = ChildOpacityState::Deferred;
4851 return false;
4854 struct {
4855 nsPaintedDisplayItem* item{};
4856 nsRect bounds;
4857 } children[kOpacityMaxChildCount];
4859 bool snap;
4860 size_t childCount = 0;
4861 for (nsPaintedDisplayItem* item : items) {
4862 children[childCount].item = item;
4863 children[childCount].bounds = item->GetBounds(aBuilder, &snap);
4864 childCount++;
4867 for (size_t i = 0; i < childCount; i++) {
4868 for (size_t j = i + 1; j < childCount; j++) {
4869 if (children[i].bounds.Intersects(children[j].bounds)) {
4870 mChildOpacityState = ChildOpacityState::Deferred;
4871 return false;
4876 mChildOpacityState = ChildOpacityState::Applied;
4877 return true;
4881 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4882 * that has the same frame as the opacity item, and that supports painting with
4883 * opacity. In this case the opacity item can be optimized away.
4885 bool nsDisplayOpacity::ApplyToMask() {
4886 if (mList.Length() != 1) {
4887 return false;
4890 nsDisplayItem* item = mList.GetBottom();
4891 if (item->Frame() != mFrame) {
4892 // The effect item needs to have the same frame as the opacity item.
4893 return false;
4896 const DisplayItemType type = item->GetType();
4897 if (type == DisplayItemType::TYPE_MASK) {
4898 return true;
4901 return false;
4904 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4905 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder,
4906 float aInheritedOpacity) {
4907 if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
4908 mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
4909 // If we've been split, then we might need to merge, so
4910 // don't flatten us away.
4911 return false;
4914 if (mNeedsActiveLayer || mOpacity == 0.0) {
4915 // If our opacity is zero then we'll discard all descendant display items
4916 // except for layer event regions, so there's no point in doing this
4917 // optimization (and if we do do it, then invalidations of those descendants
4918 // might trigger repainting).
4919 return false;
4922 if (mList.IsEmpty()) {
4923 return false;
4926 // We can only flatten opacity items into a mask if we haven't
4927 // already flattened an earlier ancestor, since the SVG code pulls the opacity
4928 // from style directly, and won't know about the outer opacity value.
4929 if (aInheritedOpacity == 1.0f && ApplyToMask()) {
4930 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame));
4931 mChildOpacityState = ChildOpacityState::Applied;
4932 return true;
4935 // Return true if we successfully applied opacity to child items.
4936 return CanApplyToChildren(aManager, aBuilder);
4939 void nsDisplayOpacity::ComputeInvalidationRegion(
4940 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4941 nsRegion* aInvalidRegion) const {
4942 const auto* geometry =
4943 static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
4945 bool snap;
4946 if (mOpacity != geometry->mOpacity) {
4947 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4951 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) {
4952 aStream << " (opacity " << mOpacity << ", mChildOpacityState: ";
4953 switch (mChildOpacityState) {
4954 case ChildOpacityState::Unknown:
4955 aStream << "Unknown";
4956 break;
4957 case ChildOpacityState::Applied:
4958 aStream << "Applied";
4959 break;
4960 case ChildOpacityState::Deferred:
4961 aStream << "Deferred";
4962 break;
4963 default:
4964 break;
4967 aStream << ")";
4970 bool nsDisplayOpacity::CreateWebRenderCommands(
4971 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4972 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4973 nsDisplayListBuilder* aDisplayListBuilder) {
4974 MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied);
4975 float oldOpacity = aBuilder.GetInheritedOpacity();
4976 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
4977 aBuilder.SetInheritedOpacity(1.0f);
4978 aBuilder.SetInheritedClipChain(nullptr);
4979 float opacity = mOpacity * oldOpacity;
4980 float* opacityForSC = &opacity;
4982 uint64_t animationsId =
4983 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
4984 wr::WrAnimationProperty prop{
4985 wr::WrAnimationType::Opacity,
4986 animationsId,
4989 wr::StackingContextParams params;
4990 params.animation = animationsId ? &prop : nullptr;
4991 params.opacity = opacityForSC;
4992 params.clip =
4993 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
4994 if (mWrapsBackdropFilter) {
4995 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
4997 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
4998 params);
5000 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
5001 &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
5002 aBuilder.SetInheritedOpacity(oldOpacity);
5003 aBuilder.SetInheritedClipChain(oldClipChain);
5004 return true;
5007 nsDisplayBlendMode::nsDisplayBlendMode(
5008 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5009 StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
5010 const bool aIsForBackground)
5011 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5012 mBlendMode(aBlendMode),
5013 mIsForBackground(aIsForBackground) {
5014 MOZ_COUNT_CTOR(nsDisplayBlendMode);
5017 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5018 bool* aSnap) const {
5019 *aSnap = false;
5020 // We are never considered opaque
5021 return nsRegion();
5024 bool nsDisplayBlendMode::CreateWebRenderCommands(
5025 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5026 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5027 nsDisplayListBuilder* aDisplayListBuilder) {
5028 wr::StackingContextParams params;
5029 params.mix_blend_mode =
5030 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
5031 params.clip =
5032 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5033 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5034 params);
5036 return nsDisplayWrapList::CreateWebRenderCommands(
5037 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5040 void nsDisplayBlendMode::Paint(nsDisplayListBuilder* aBuilder,
5041 gfxContext* aCtx) {
5042 // This should be switched to use PushLayerWithBlend, once it's
5043 // been implemented for all DrawTarget backends.
5044 DrawTarget* dt = aCtx->GetDrawTarget();
5045 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5046 Rect rect = NSRectToRect(GetPaintRect(aBuilder, aCtx), appUnitsPerDevPixel);
5047 rect.RoundOut();
5049 // Create a temporary DrawTarget that is clipped to the area that
5050 // we're going to draw to. This will include the same transform as
5051 // is currently on |dt|.
5052 RefPtr<DrawTarget> temp =
5053 dt->CreateClippedDrawTarget(rect, SurfaceFormat::B8G8R8A8);
5054 if (!temp) {
5055 return;
5058 gfxContext ctx(temp, /* aPreserveTransform */ true);
5060 GetChildren()->Paint(aBuilder, &ctx,
5061 mFrame->PresContext()->AppUnitsPerDevPixel());
5063 // Draw the temporary DT to the real destination, applying the blend mode, but
5064 // no transform.
5065 temp->Flush();
5066 RefPtr<SourceSurface> surface = temp->Snapshot();
5067 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
5068 dt->SetTransform(Matrix());
5069 dt->DrawSurface(
5070 surface, Rect(surface->GetRect()), Rect(surface->GetRect()),
5071 DrawSurfaceOptions(),
5072 DrawOptions(1.0f, nsCSSRendering::GetGFXBlendMode(mBlendMode)));
5075 gfx::CompositionOp nsDisplayBlendMode::BlendMode() {
5076 return nsCSSRendering::GetGFXBlendMode(mBlendMode);
5079 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const {
5080 // Items for the same content element should be merged into a single
5081 // compositing group.
5082 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
5083 !HasSameContent(aItem)) {
5084 return false;
5087 const auto* item = static_cast<const nsDisplayBlendMode*>(aItem);
5088 if (mIsForBackground || item->mIsForBackground) {
5089 // Don't merge background-blend-mode items
5090 return false;
5093 return true;
5096 /* static */
5097 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForMixBlendMode(
5098 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5099 const ActiveScrolledRoot* aActiveScrolledRoot) {
5100 return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
5101 aActiveScrolledRoot, false);
5104 /* static */
5105 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5106 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5107 nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot) {
5108 if (aSecondaryFrame) {
5109 auto type = GetTableTypeFromFrame(aFrame);
5110 auto index = static_cast<uint16_t>(type);
5112 return MakeDisplayItemWithIndex<nsDisplayTableBlendContainer>(
5113 aBuilder, aSecondaryFrame, index, aList, aActiveScrolledRoot, true,
5114 aFrame);
5117 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>(
5118 aBuilder, aFrame, 1, aList, aActiveScrolledRoot, true);
5121 nsDisplayBlendContainer::nsDisplayBlendContainer(
5122 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5123 const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground)
5124 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5125 mIsForBackground(aIsForBackground) {
5126 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
5129 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder* aBuilder,
5130 gfxContext* aCtx) {
5131 aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5132 GetChildren()->Paint(aBuilder, aCtx,
5133 mFrame->PresContext()->AppUnitsPerDevPixel());
5134 aCtx->GetDrawTarget()->PopLayer();
5137 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5138 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5139 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5140 nsDisplayListBuilder* aDisplayListBuilder) {
5141 wr::StackingContextParams params;
5142 params.flags |= wr::StackingContextFlags::IS_BLEND_CONTAINER;
5143 params.clip =
5144 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5145 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5146 params);
5148 return nsDisplayWrapList::CreateWebRenderCommands(
5149 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5152 nsDisplayOwnLayer::nsDisplayOwnLayer(
5153 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5154 const ActiveScrolledRoot* aActiveScrolledRoot,
5155 nsDisplayOwnLayerFlags aFlags, const ScrollbarData& aScrollbarData,
5156 bool aForceActive, bool aClearClipChain)
5157 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
5158 aClearClipChain),
5159 mFlags(aFlags),
5160 mScrollbarData(aScrollbarData),
5161 mForceActive(aForceActive),
5162 mWrAnimationId(0) {
5163 MOZ_COUNT_CTOR(nsDisplayOwnLayer);
5166 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5167 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb;
5170 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5171 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container;
5174 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5175 return IsScrollbarContainer() && IsScrollbarLayerForRoot();
5178 bool nsDisplayOwnLayer::IsScrollbarLayerForRoot() const {
5179 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5180 mScrollbarData.mTargetViewId ==
5181 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5184 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5185 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM;
5188 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5189 return GetType() == DisplayItemType::TYPE_FIXED_POSITION;
5192 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5193 return GetType() == DisplayItemType::TYPE_STICKY_POSITION;
5196 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5197 if (!mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
5198 return false;
5200 return mFrame->PresContext()->HasDynamicToolbar() ||
5201 // For tests on Android, this pref is set to simulate the dynamic
5202 // toolbar
5203 StaticPrefs::apz_fixed_margin_override_enabled();
5206 bool nsDisplayOwnLayer::ShouldFixedAndStickyContentGetAnimationIds() const {
5207 #if defined(MOZ_WIDGET_ANDROID)
5208 return mFrame->PresContext()->IsRootContentDocumentCrossProcess();
5209 #else
5210 return false;
5211 #endif
5214 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5215 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5216 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5217 nsDisplayListBuilder* aDisplayListBuilder) {
5218 Maybe<wr::WrAnimationProperty> prop;
5219 bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() &&
5220 (IsScrollThumbLayer() || IsZoomingLayer() ||
5221 (IsFixedPositionLayer() &&
5222 ShouldFixedAndStickyContentGetAnimationIds()) ||
5223 (IsStickyPositionLayer() &&
5224 ShouldFixedAndStickyContentGetAnimationIds()) ||
5225 (IsRootScrollbarContainer() && HasDynamicToolbar()));
5227 if (needsProp) {
5228 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5229 // to create and set an animation id. That way APZ can adjust the position/
5230 // zoom of this content asynchronously as needed.
5231 RefPtr<WebRenderAPZAnimationData> animationData =
5232 aManager->CommandBuilder()
5233 .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(this);
5234 mWrAnimationId = animationData->GetAnimationId();
5236 prop.emplace();
5237 prop->id = mWrAnimationId;
5238 prop->key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5239 wr::SpatialKeyKind::APZ);
5240 prop->effect_type = wr::WrAnimationType::Transform;
5243 wr::StackingContextParams params;
5244 params.animation = prop.ptrOr(nullptr);
5245 params.clip =
5246 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5247 if (IsScrollbarContainer() && IsRootScrollbarContainer()) {
5248 params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER;
5250 if (IsZoomingLayer() ||
5251 ((IsFixedPositionLayer() &&
5252 ShouldFixedAndStickyContentGetAnimationIds()) ||
5253 (IsStickyPositionLayer() &&
5254 ShouldFixedAndStickyContentGetAnimationIds()) ||
5255 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5256 params.is_2d_scale_translation = true;
5257 params.should_snap = true;
5260 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5261 params);
5263 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
5264 aDisplayListBuilder);
5265 return true;
5268 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData,
5269 WebRenderLayerScrollData* aLayerData) {
5270 bool isRelevantToApz =
5271 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5272 (IsFixedPositionLayer() &&
5273 ShouldFixedAndStickyContentGetAnimationIds()) ||
5274 (IsStickyPositionLayer() &&
5275 ShouldFixedAndStickyContentGetAnimationIds()));
5277 if (!isRelevantToApz) {
5278 return false;
5281 if (!aLayerData) {
5282 return true;
5285 if (IsZoomingLayer()) {
5286 aLayerData->SetZoomAnimationId(mWrAnimationId);
5287 return true;
5290 if (IsFixedPositionLayer() && ShouldFixedAndStickyContentGetAnimationIds()) {
5291 aLayerData->SetFixedPositionAnimationId(mWrAnimationId);
5292 return true;
5295 if (IsStickyPositionLayer() && ShouldFixedAndStickyContentGetAnimationIds()) {
5296 aLayerData->SetStickyPositionAnimationId(mWrAnimationId);
5297 return true;
5300 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5302 aLayerData->SetScrollbarData(mScrollbarData);
5304 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5305 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5306 return true;
5309 if (IsScrollThumbLayer()) {
5310 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5311 LayoutDeviceRect bounds = LayoutDeviceIntRect::FromAppUnits(
5312 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
5313 // Subframe scrollbars are subject to the pinch-zoom scale,
5314 // but root scrollbars are not because they are outside of the
5315 // region that is zoomed.
5316 const float resolution =
5317 IsScrollbarLayerForRoot()
5318 ? 1.0f
5319 : mFrame->PresContext()->PresShell()->GetCumulativeResolution();
5320 LayerIntRect layerBounds =
5321 RoundedOut(bounds * LayoutDeviceToLayerScale(resolution));
5322 aLayerData->SetVisibleRegion(LayerIntRegion(layerBounds));
5324 return true;
5327 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) {
5328 aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
5329 (int)mFlags, mScrollbarData.mTargetViewId)
5330 .get();
5333 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
5334 nsIFrame* aFrame,
5335 nsSubDocumentFrame* aSubDocFrame,
5336 nsDisplayList* aList,
5337 nsDisplayOwnLayerFlags aFlags)
5338 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5339 aBuilder->CurrentActiveScrolledRoot(), aFlags),
5340 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5341 mShouldFlatten(false),
5342 mSubDocFrame(aSubDocFrame) {
5343 MOZ_COUNT_CTOR(nsDisplaySubDocument);
5345 if (mSubDocFrame && mSubDocFrame != mFrame) {
5346 mSubDocFrame->AddDisplayItem(this);
5350 nsDisplaySubDocument::~nsDisplaySubDocument() {
5351 MOZ_COUNT_DTOR(nsDisplaySubDocument);
5352 if (mSubDocFrame) {
5353 mSubDocFrame->RemoveDisplayItem(this);
5357 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const {
5358 return mSubDocFrame ? mSubDocFrame : mFrame;
5361 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) {
5362 if (aFrame == mSubDocFrame) {
5363 mSubDocFrame = nullptr;
5364 SetDeletedFrame();
5366 nsDisplayOwnLayer::RemoveFrame(aFrame);
5369 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder,
5370 nsIFrame* aFrame) {
5371 return aBuilder->IsPaintingToWindow() &&
5372 DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext());
5375 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder,
5376 bool* aSnap) const {
5377 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5379 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5380 usingDisplayPort) {
5381 *aSnap = false;
5382 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
5385 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
5388 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5389 bool* aSnap) const {
5390 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5392 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5393 usingDisplayPort) {
5394 *aSnap = false;
5395 return nsRegion();
5398 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
5401 /* static */
5402 nsDisplayFixedPosition* nsDisplayFixedPosition::CreateForFixedBackground(
5403 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5404 nsDisplayBackgroundImage* aImage, const uint16_t aIndex,
5405 const ActiveScrolledRoot* aScrollTargetASR) {
5406 nsDisplayList temp(aBuilder);
5407 temp.AppendToTop(aImage);
5409 if (aSecondaryFrame) {
5410 auto tableType = GetTableTypeFromFrame(aFrame);
5411 const uint16_t index = CalculateTablePerFrameKey(aIndex + 1, tableType);
5412 return MakeDisplayItemWithIndex<nsDisplayTableFixedPosition>(
5413 aBuilder, aSecondaryFrame, index, &temp, aFrame, aScrollTargetASR);
5416 return MakeDisplayItemWithIndex<nsDisplayFixedPosition>(
5417 aBuilder, aFrame, aIndex + 1, &temp, aScrollTargetASR);
5420 nsDisplayFixedPosition::nsDisplayFixedPosition(
5421 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5422 const ActiveScrolledRoot* aActiveScrolledRoot,
5423 const ActiveScrolledRoot* aScrollTargetASR)
5424 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5425 mScrollTargetASR(aScrollTargetASR),
5426 mIsFixedBackground(false) {
5427 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5430 nsDisplayFixedPosition::nsDisplayFixedPosition(
5431 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5432 const ActiveScrolledRoot* aScrollTargetASR)
5433 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5434 aBuilder->CurrentActiveScrolledRoot()),
5435 // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5436 mScrollTargetASR(aScrollTargetASR),
5437 mIsFixedBackground(true) {
5438 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5441 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() {
5442 if (mScrollTargetASR &&
5443 (mIsFixedBackground || !nsLayoutUtils::IsReallyFixedPos(mFrame))) {
5444 return mScrollTargetASR->GetViewId();
5446 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5449 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5450 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5451 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5452 nsDisplayListBuilder* aDisplayListBuilder) {
5453 SideBits sides = SideBits::eNone;
5454 if (!mIsFixedBackground) {
5455 sides = nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5458 // We install this RAII scrolltarget tracker so that any
5459 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5460 // share the same ASR as this item) use the correct scroll target. That way
5461 // attempts to scroll on those items will scroll the root scroll frame.
5462 wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker(
5463 aBuilder, GetActiveScrolledRoot(), GetScrollTargetId(), sides);
5464 return nsDisplayOwnLayer::CreateWebRenderCommands(
5465 aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
5468 bool nsDisplayFixedPosition::UpdateScrollData(
5469 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5470 if (aLayerData) {
5471 if (!mIsFixedBackground) {
5472 aLayerData->SetFixedPositionSides(
5473 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame));
5475 aLayerData->SetFixedPositionScrollContainerId(GetScrollTargetId());
5477 nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5478 return true;
5481 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream& aStream) {
5482 aStream << nsPrintfCString(
5483 " (containerASR %s) (scrolltarget %" PRIu64 ")",
5484 ActiveScrolledRoot::ToString(mScrollTargetASR).get(),
5485 GetScrollTargetId())
5486 .get();
5489 TableType GetTableTypeFromFrame(nsIFrame* aFrame) {
5490 if (aFrame->IsTableFrame()) {
5491 return TableType::Table;
5494 if (aFrame->IsTableColFrame()) {
5495 return TableType::TableCol;
5498 if (aFrame->IsTableColGroupFrame()) {
5499 return TableType::TableColGroup;
5502 if (aFrame->IsTableRowFrame()) {
5503 return TableType::TableRow;
5506 if (aFrame->IsTableRowGroupFrame()) {
5507 return TableType::TableRowGroup;
5510 if (aFrame->IsTableCellFrame()) {
5511 return TableType::TableCell;
5514 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5515 return TableType::Table;
5518 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5519 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5520 nsIFrame* aAncestorFrame, const ActiveScrolledRoot* aScrollTargetASR)
5521 : nsDisplayFixedPosition(aBuilder, aFrame, aList, aScrollTargetASR),
5522 mAncestorFrame(aAncestorFrame) {
5523 if (aBuilder->IsRetainingDisplayList()) {
5524 mAncestorFrame->AddDisplayItem(this);
5528 nsDisplayStickyPosition::nsDisplayStickyPosition(
5529 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5530 const ActiveScrolledRoot* aActiveScrolledRoot,
5531 const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
5532 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5533 mContainerASR(aContainerASR),
5534 mClippedToDisplayPort(aClippedToDisplayPort),
5535 mShouldFlatten(false) {
5536 MOZ_COUNT_CTOR(nsDisplayStickyPosition);
5539 // Returns the smallest distance from "0" to the range [min, max] where
5540 // min <= max. Despite the name, the return value is actually a 1-D vector,
5541 // and so may be negative if max < 0.
5542 static nscoord DistanceToRange(nscoord min, nscoord max) {
5543 MOZ_ASSERT(min <= max);
5544 if (max < 0) {
5545 return max;
5547 if (min > 0) {
5548 return min;
5550 MOZ_ASSERT(min <= 0 && max >= 0);
5551 return 0;
5554 // Returns the magnitude of the part of the range [min, max] that is greater
5555 // than zero. The return value is always non-negative.
5556 static nscoord PositivePart(nscoord min, nscoord max) {
5557 MOZ_ASSERT(min <= max);
5558 if (min >= 0) {
5559 return max - min;
5561 if (max > 0) {
5562 return max;
5564 return 0;
5567 // Returns the magnitude of the part of the range [min, max] that is less
5568 // than zero. The return value is always non-negative.
5569 static nscoord NegativePart(nscoord min, nscoord max) {
5570 MOZ_ASSERT(min <= max);
5571 if (max <= 0) {
5572 return max - min;
5574 if (min < 0) {
5575 return 0 - min;
5577 return 0;
5580 StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() {
5581 StickyScrollContainer* stickyScrollContainer =
5582 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
5583 if (stickyScrollContainer) {
5584 // If there's no ASR for the scrollframe that this sticky item is attached
5585 // to, then don't create a WR sticky item for it either. Trying to do so
5586 // will end in sadness because WR will interpret some coordinates as
5587 // relative to the nearest enclosing scrollframe, which will correspond
5588 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5589 // same as the scrollframe this sticky item is actually supposed to be
5590 // attached to, thus the sadness.
5591 // Not sending WR the sticky item is ok, because the enclosing scrollframe
5592 // will never be asynchronously scrolled. Instead we will always position
5593 // the sticky items correctly on the gecko side and WR will never need to
5594 // adjust their position itself.
5595 MOZ_ASSERT(
5596 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5597 if (!stickyScrollContainer->ScrollFrame()
5598 ->IsMaybeAsynchronouslyScrolled()) {
5599 stickyScrollContainer = nullptr;
5602 return stickyScrollContainer;
5605 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5606 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5607 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5608 nsDisplayListBuilder* aDisplayListBuilder) {
5609 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5611 Maybe<wr::SpaceAndClipChainHelper> saccHelper;
5613 if (stickyScrollContainer) {
5614 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5616 bool snap;
5617 nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
5619 Maybe<float> topMargin;
5620 Maybe<float> rightMargin;
5621 Maybe<float> bottomMargin;
5622 Maybe<float> leftMargin;
5623 wr::StickyOffsetBounds vBounds = {0.0, 0.0};
5624 wr::StickyOffsetBounds hBounds = {0.0, 0.0};
5625 nsPoint appliedOffset;
5627 nsRectAbsolute outer;
5628 nsRectAbsolute inner;
5629 stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5631 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
5632 nsPoint offset =
5633 scrollFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5635 // Adjust the scrollPort coordinates to be relative to the reference frame,
5636 // so that it is in the same space as everything else.
5637 nsRect scrollPort =
5638 stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
5639 scrollPort += offset;
5641 // The following computations make more sense upon understanding the
5642 // semantics of "inner" and "outer", which is explained in the comment on
5643 // SetStickyPositionData in Layers.h.
5645 if (outer.YMost() != inner.YMost()) {
5646 // Question: How far will itemBounds.y be from the top of the scrollport
5647 // when we have scrolled from the current scroll position of "0" to
5648 // reach the range [inner.YMost(), outer.YMost()] where the item gets
5649 // stuck?
5650 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5651 // needs to be adjusted by the distance to the range, less any other
5652 // sticky ranges that fall between 0 and the range. If the distance is
5653 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5654 // scrolling upwards (decreasing scroll offset) to reach that range,
5655 // which would increase itemBounds.y and make it farther away from the
5656 // top of the scrollport. So in that case the adjustment is -distance.
5657 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5658 // we would be scrolling downwards, itemBounds.y would decrease, and we
5659 // again need to adjust by -distance. If we are already in the range
5660 // then no adjustment is needed and distance is 0 so again using
5661 // -distance works. If the distance is positive, and the item has both
5662 // top and bottom sticky ranges, then the bottom sticky range may fall
5663 // (entirely[1] or partly[2]) between the current scroll position.
5664 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5665 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5666 // In these cases, the item doesn't actually move for that part of the
5667 // distance, so we need to subtract out that bit, which can be computed
5668 // as the positive portion of the range [outer.Y(), inner.Y()].
5669 nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
5670 if (distance > 0) {
5671 distance -= PositivePart(outer.Y(), inner.Y());
5673 topMargin = Some(NSAppUnitsToFloatPixels(
5674 itemBounds.y - scrollPort.y - distance, auPerDevPixel));
5675 // Question: What is the maximum positive ("downward") offset that WR
5676 // will have to apply to this item in order to prevent the item from
5677 // visually moving?
5678 // Answer: Since the item is "sticky" in the range [inner.YMost(),
5679 // outer.YMost()], the maximum offset will be the size of the range, which
5680 // is outer.YMost() - inner.YMost().
5681 vBounds.max =
5682 NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
5683 // Question: how much of an offset has layout already applied to the item?
5684 // Answer: if we are
5685 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5686 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5687 // then layout has already applied some offset to the position of the
5688 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5689 // and |outer.YMost() - inner.YMost()| in case (b).
5690 if (inner.YMost() < 0) {
5691 appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
5692 MOZ_ASSERT(appliedOffset.y > 0);
5695 if (outer.Y() != inner.Y()) {
5696 // Similar logic as in the previous section, but this time we care about
5697 // the distance from itemBounds.YMost() to scrollPort.YMost().
5698 nscoord distance = DistanceToRange(outer.Y(), inner.Y());
5699 if (distance < 0) {
5700 distance += NegativePart(inner.YMost(), outer.YMost());
5702 bottomMargin = Some(NSAppUnitsToFloatPixels(
5703 scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
5704 // And here WR will be moving the item upwards rather than downwards so
5705 // again things are inverted from the previous block.
5706 vBounds.min =
5707 NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
5708 // We can't have appliedOffset be both positive and negative, and the top
5709 // adjustment takes priority. So here we only update appliedOffset.y if
5710 // it wasn't set by the top-sticky case above.
5711 if (appliedOffset.y == 0 && inner.Y() > 0) {
5712 appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
5713 MOZ_ASSERT(appliedOffset.y < 0);
5716 // Same as above, but for the x-axis
5717 if (outer.XMost() != inner.XMost()) {
5718 nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
5719 if (distance > 0) {
5720 distance -= PositivePart(outer.X(), inner.X());
5722 leftMargin = Some(NSAppUnitsToFloatPixels(
5723 itemBounds.x - scrollPort.x - distance, auPerDevPixel));
5724 hBounds.max =
5725 NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
5726 if (inner.XMost() < 0) {
5727 appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
5728 MOZ_ASSERT(appliedOffset.x > 0);
5731 if (outer.X() != inner.X()) {
5732 nscoord distance = DistanceToRange(outer.X(), inner.X());
5733 if (distance < 0) {
5734 distance += NegativePart(inner.XMost(), outer.XMost());
5736 rightMargin = Some(NSAppUnitsToFloatPixels(
5737 scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
5738 hBounds.min =
5739 NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
5740 if (appliedOffset.x == 0 && inner.X() > 0) {
5741 appliedOffset.x = std::max(0, outer.X()) - inner.X();
5742 MOZ_ASSERT(appliedOffset.x < 0);
5746 LayoutDeviceRect bounds =
5747 LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
5748 wr::LayoutVector2D applied = {
5749 NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
5750 NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
5751 wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame(
5752 wr::ToLayoutRect(bounds), topMargin.ptrOr(nullptr),
5753 rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
5754 leftMargin.ptrOr(nullptr), vBounds, hBounds, applied,
5755 wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5756 wr::SpatialKeyKind::Sticky));
5758 saccHelper.emplace(aBuilder, spatialId);
5759 aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId);
5763 wr::StackingContextParams params;
5764 params.clip =
5765 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5766 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
5767 aBuilder, params);
5768 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc,
5769 aManager, aDisplayListBuilder);
5772 if (stickyScrollContainer) {
5773 aManager->CommandBuilder().PopOverrideForASR(mContainerASR);
5776 return true;
5779 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5780 StickyScrollContainer* aStickyScrollContainer, float aAppUnitsPerDevPixel,
5781 float aScaleX, float aScaleY, LayerRectAbsolute& aStickyOuter,
5782 LayerRectAbsolute& aStickyInner) {
5783 nsRectAbsolute outer;
5784 nsRectAbsolute inner;
5785 aStickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5786 aStickyOuter.SetBox(
5787 NSAppUnitsToFloatPixels(outer.X(), aAppUnitsPerDevPixel) * aScaleX,
5788 NSAppUnitsToFloatPixels(outer.Y(), aAppUnitsPerDevPixel) * aScaleY,
5789 NSAppUnitsToFloatPixels(outer.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5790 NSAppUnitsToFloatPixels(outer.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5791 aStickyInner.SetBox(
5792 NSAppUnitsToFloatPixels(inner.X(), aAppUnitsPerDevPixel) * aScaleX,
5793 NSAppUnitsToFloatPixels(inner.Y(), aAppUnitsPerDevPixel) * aScaleY,
5794 NSAppUnitsToFloatPixels(inner.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5795 NSAppUnitsToFloatPixels(inner.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5798 bool nsDisplayStickyPosition::UpdateScrollData(
5799 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5800 bool hasDynamicToolbar = HasDynamicToolbar();
5801 if (aLayerData && hasDynamicToolbar) {
5802 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5803 if (stickyScrollContainer) {
5804 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5805 float cumulativeResolution =
5806 mFrame->PresShell()->GetCumulativeResolution();
5807 LayerRectAbsolute stickyOuter;
5808 LayerRectAbsolute stickyInner;
5809 CalculateLayerScrollRanges(stickyScrollContainer, auPerDevPixel,
5810 cumulativeResolution, cumulativeResolution,
5811 stickyOuter, stickyInner);
5812 aLayerData->SetStickyScrollRangeOuter(stickyOuter);
5813 aLayerData->SetStickyScrollRangeInner(stickyInner);
5815 SideBits sides =
5816 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5817 aLayerData->SetFixedPositionSides(sides);
5819 ScrollableLayerGuid::ViewID scrollId =
5820 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5821 ->GetScrolledFrame()
5822 ->GetContent());
5823 aLayerData->SetStickyPositionScrollContainerId(scrollId);
5826 // Return true if either there is a dynamic toolbar affecting this sticky
5827 // item or the OwnLayer base implementation returns true for some other
5828 // reason.
5829 bool ret = hasDynamicToolbar;
5830 ret |= nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5831 return ret;
5834 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5835 nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
5836 nsIFrame* aScrollFrame, const CompositorHitTestInfo& aHitInfo,
5837 const nsRect& aHitArea)
5838 : nsDisplayWrapList(aBuilder, aScrollFrame),
5839 mScrollFrame(aScrollFrame),
5840 mScrolledFrame(aScrolledFrame),
5841 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5842 mHitInfo(aHitInfo),
5843 mHitArea(aHitArea) {
5844 #ifdef NS_BUILD_REFCNT_LOGGING
5845 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
5846 #endif
5849 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5850 nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) {
5851 ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
5852 mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(),
5853 ToReferenceFrame(), aLayerManager, mScrollParentId,
5854 mScrollFrame->GetSize(), false);
5855 metadata.GetMetrics().SetIsScrollInfoLayer(true);
5856 nsIScrollableFrame* scrollableFrame = mScrollFrame->GetScrollTargetFrame();
5857 if (scrollableFrame) {
5858 aBuilder->AddScrollFrameToNotify(scrollableFrame);
5861 return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
5864 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5865 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5866 if (aLayerData) {
5867 UniquePtr<ScrollMetadata> metadata =
5868 ComputeScrollMetadata(aData->GetBuilder(), aData->GetManager());
5869 MOZ_ASSERT(aData);
5870 MOZ_ASSERT(metadata);
5871 aLayerData->AppendScrollMetadata(*aData, *metadata);
5873 return true;
5876 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5877 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5878 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5879 nsDisplayListBuilder* aDisplayListBuilder) {
5880 ScrollableLayerGuid::ViewID scrollId =
5881 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame->GetContent());
5883 const LayoutDeviceRect devRect = LayoutDeviceRect::FromAppUnits(
5884 mHitArea, mScrollFrame->PresContext()->AppUnitsPerDevPixel());
5886 const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
5888 aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, mHitInfo,
5889 SideBits::eNone);
5891 return true;
5894 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) {
5895 aStream << " (scrollframe " << mScrollFrame << " scrolledFrame "
5896 << mScrolledFrame << ")";
5899 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5900 nsSubDocumentFrame* aSubDocFrame,
5901 nsDisplayList* aList, int32_t aAPD,
5902 int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags)
5903 : nsDisplaySubDocument(aBuilder, aFrame, aSubDocFrame, aList, aFlags),
5904 mAPD(aAPD),
5905 mParentAPD(aParentAPD) {
5906 MOZ_COUNT_CTOR(nsDisplayZoom);
5909 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder,
5910 bool* aSnap) const {
5911 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
5912 *aSnap = false;
5913 return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD);
5916 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5917 HitTestState* aState,
5918 nsTArray<nsIFrame*>* aOutFrames) {
5919 nsRect rect;
5920 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5921 // rect as well instead of possibly rounding the width or height to zero.
5922 if (aRect.width == 1 && aRect.height == 1) {
5923 rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD));
5924 rect.width = rect.height = 1;
5925 } else {
5926 rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
5928 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5931 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5932 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5933 const ActiveScrolledRoot* aActiveScrolledRoot, FrameMetrics::ViewID aViewID)
5934 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5935 mViewID(aViewID) {
5936 MOZ_COUNT_CTOR(nsDisplayAsyncZoom);
5939 #ifdef NS_BUILD_REFCNT_LOGGING
5940 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5941 MOZ_COUNT_DTOR(nsDisplayAsyncZoom);
5943 #endif
5945 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder* aBuilder,
5946 const nsRect& aRect, HitTestState* aState,
5947 nsTArray<nsIFrame*>* aOutFrames) {
5948 #ifdef DEBUG
5949 nsIScrollableFrame* scrollFrame = do_QueryFrame(mFrame);
5950 MOZ_ASSERT(scrollFrame && ViewportUtils::IsZoomedContentRoot(
5951 scrollFrame->GetScrolledFrame()));
5952 #endif
5953 nsRect rect = ViewportUtils::VisualToLayout(aRect, mFrame->PresShell());
5954 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5957 bool nsDisplayAsyncZoom::UpdateScrollData(
5958 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5959 bool ret = nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5960 MOZ_ASSERT(ret);
5961 if (aLayerData) {
5962 aLayerData->SetAsyncZoomContainerId(mViewID);
5964 return ret;
5967 ///////////////////////////////////////////////////
5968 // nsDisplayTransform Implementation
5971 #ifndef DEBUG
5972 static_assert(sizeof(nsDisplayTransform) <= 512,
5973 "nsDisplayTransform has grown");
5974 #endif
5976 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5977 nsIFrame* aFrame, nsDisplayList* aList,
5978 const nsRect& aChildrenBuildingRect)
5979 : nsPaintedDisplayItem(aBuilder, aFrame),
5980 mChildren(aBuilder),
5981 mTransform(Some(Matrix4x4())),
5982 mChildrenBuildingRect(aChildrenBuildingRect),
5983 mPrerenderDecision(PrerenderDecision::No),
5984 mIsTransformSeparator(true),
5985 mHasTransformGetter(false),
5986 mHasAssociatedPerspective(false) {
5987 MOZ_COUNT_CTOR(nsDisplayTransform);
5988 MOZ_ASSERT(aFrame, "Must have a frame!");
5989 Init(aBuilder, aList);
5992 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5993 nsIFrame* aFrame, nsDisplayList* aList,
5994 const nsRect& aChildrenBuildingRect,
5995 PrerenderDecision aPrerenderDecision)
5996 : nsPaintedDisplayItem(aBuilder, aFrame),
5997 mChildren(aBuilder),
5998 mChildrenBuildingRect(aChildrenBuildingRect),
5999 mPrerenderDecision(aPrerenderDecision),
6000 mIsTransformSeparator(false),
6001 mHasTransformGetter(false),
6002 mHasAssociatedPerspective(false) {
6003 MOZ_COUNT_CTOR(nsDisplayTransform);
6004 MOZ_ASSERT(aFrame, "Must have a frame!");
6005 SetReferenceFrameToAncestor(aBuilder);
6006 Init(aBuilder, aList);
6009 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
6010 nsIFrame* aFrame, nsDisplayList* aList,
6011 const nsRect& aChildrenBuildingRect,
6012 decltype(WithTransformGetter))
6013 : nsPaintedDisplayItem(aBuilder, aFrame),
6014 mChildren(aBuilder),
6015 mChildrenBuildingRect(aChildrenBuildingRect),
6016 mPrerenderDecision(PrerenderDecision::No),
6017 mIsTransformSeparator(false),
6018 mHasTransformGetter(true),
6019 mHasAssociatedPerspective(false) {
6020 MOZ_COUNT_CTOR(nsDisplayTransform);
6021 MOZ_ASSERT(aFrame, "Must have a frame!");
6022 MOZ_ASSERT(aFrame->GetTransformGetter());
6023 Init(aBuilder, aList);
6026 void nsDisplayTransform::SetReferenceFrameToAncestor(
6027 nsDisplayListBuilder* aBuilder) {
6028 if (mFrame == aBuilder->RootReferenceFrame()) {
6029 return;
6031 // We manually recompute mToReferenceFrame without going through the
6032 // builder, since this won't apply the 'additional offset'. Our
6033 // children will already be painting with that applied, and we don't
6034 // want to include it a second time in our transform. We don't recompute
6035 // our visible/building rects, since those should still include the additional
6036 // offset.
6037 // TODO: Are there are things computed using our ToReferenceFrame that should
6038 // have the additional offset applied? Should we instead just manually remove
6039 // the offset from our transform instead of this more general value?
6040 // Can we instead apply the additional offset to us and not our children, like
6041 // we do for all other offsets (and how reference frames are supposed to
6042 // work)?
6043 nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame);
6044 const nsIFrame* referenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
6045 mToReferenceFrame = mFrame->GetOffsetToCrossDoc(referenceFrame);
6048 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
6049 nsDisplayList* aChildren) {
6050 mChildren.AppendToTop(aChildren);
6051 UpdateBounds(aBuilder);
6054 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
6055 return false;
6058 /* Returns the delta specified by the transform-origin property.
6059 * This is a positive delta, meaning that it indicates the direction to move
6060 * to get from (0, 0) of the frame to the transform origin. This function is
6061 * called off the main thread.
6063 /* static */
6064 Point3D nsDisplayTransform::GetDeltaToTransformOrigin(
6065 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6066 float aAppUnitsPerPixel) {
6067 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6068 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6069 aFrame->Combines3DTransformWithAncestors(),
6070 "Shouldn't get a delta for an untransformed frame!");
6072 if (!aFrame->IsTransformed()) {
6073 return Point3D();
6076 /* For both of the coordinates, if the value of transform is a
6077 * percentage, it's relative to the size of the frame. Otherwise, if it's
6078 * a distance, it's already computed for us!
6080 const nsStyleDisplay* display = aFrame->StyleDisplay();
6082 const StyleTransformOrigin& transformOrigin = display->mTransformOrigin;
6083 CSSPoint origin = nsStyleTransformMatrix::Convert2DPosition(
6084 transformOrigin.horizontal, transformOrigin.vertical, aRefBox);
6086 // Note:
6087 // 1. SVG frames have a reference box that can be (and typically is) offset
6088 // from the TopLeft() of the frame. We need to account for that here.
6089 // 2. If we are using transform-box:content-box in CSS layout, we have the
6090 // offset from TopLeft() of the frame as well.
6091 origin.x += CSSPixel::FromAppUnits(aRefBox.X());
6092 origin.y += CSSPixel::FromAppUnits(aRefBox.Y());
6094 float scale = AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel);
6095 float z = transformOrigin.depth._0;
6096 return Point3D(origin.x * scale, origin.y * scale, z * scale);
6099 /* static */
6100 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
6101 float aAppUnitsPerPixel,
6102 Matrix4x4& aOutMatrix) {
6103 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6104 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6105 aFrame->Combines3DTransformWithAncestors(),
6106 "Shouldn't get a delta for an untransformed frame!");
6107 MOZ_ASSERT(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
6109 if (!aFrame->IsTransformed()) {
6110 return false;
6113 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6114 // correctly yet (similar to the aBoundsOverride case for
6115 // GetResultingTransformMatrix)?
6116 nsIFrame* perspectiveFrame =
6117 aFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6118 if (!perspectiveFrame) {
6119 return false;
6122 /* Grab the values for perspective and perspective-origin (if present) */
6123 const nsStyleDisplay* perspectiveDisplay = perspectiveFrame->StyleDisplay();
6124 if (perspectiveDisplay->mChildPerspective.IsNone()) {
6125 return false;
6128 MOZ_ASSERT(perspectiveDisplay->mChildPerspective.IsLength());
6129 float perspective =
6130 perspectiveDisplay->mChildPerspective.AsLength().ToCSSPixels();
6131 perspective = std::max(1.0f, perspective);
6132 if (perspective < std::numeric_limits<Float>::epsilon()) {
6133 return true;
6136 TransformReferenceBox refBox(perspectiveFrame);
6138 Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition(
6139 perspectiveDisplay->mPerspectiveOrigin.horizontal,
6140 perspectiveDisplay->mPerspectiveOrigin.vertical, refBox,
6141 aAppUnitsPerPixel);
6143 /* GetOffsetTo computes the offset required to move from 0,0 in
6144 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6145 * this, it's faster to compute this way.
6147 nsPoint frameToPerspectiveOffset = -aFrame->GetOffsetTo(perspectiveFrame);
6148 Point frameToPerspectiveGfxOffset(
6149 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.x, aAppUnitsPerPixel),
6150 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.y, aAppUnitsPerPixel));
6152 /* Move the perspective origin to be relative to aFrame, instead of relative
6153 * to the containing block which is how it was specified in the style system.
6155 perspectiveOrigin += frameToPerspectiveGfxOffset;
6157 aOutMatrix._34 =
6158 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective),
6159 aAppUnitsPerPixel);
6161 aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
6162 return true;
6165 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6166 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6167 float aAppUnitsPerPixel)
6168 : mFrame(aFrame),
6169 mTranslate(aFrame->StyleDisplay()->mTranslate),
6170 mRotate(aFrame->StyleDisplay()->mRotate),
6171 mScale(aFrame->StyleDisplay()->mScale),
6172 mTransform(aFrame->StyleDisplay()->mTransform),
6173 mMotion(MotionPathUtils::ResolveMotionPath(aFrame, aRefBox)),
6174 mToTransformOrigin(
6175 GetDeltaToTransformOrigin(aFrame, aRefBox, aAppUnitsPerPixel)) {}
6177 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6178 * translates from local coordinate space to transform coordinate space, then
6179 * hands it back.
6181 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6182 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6183 float aAppUnitsPerPixel) {
6184 return GetResultingTransformMatrixInternal(aProperties, aRefBox, nsPoint(),
6185 aAppUnitsPerPixel, 0);
6188 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6189 const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel,
6190 uint32_t aFlags) {
6191 TransformReferenceBox refBox(aFrame);
6192 FrameTransformProperties props(aFrame, refBox, aAppUnitsPerPixel);
6193 return GetResultingTransformMatrixInternal(props, refBox, aOrigin,
6194 aAppUnitsPerPixel, aFlags);
6197 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal(
6198 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6199 const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags) {
6200 const nsIFrame* frame = aProperties.mFrame;
6201 NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE),
6202 "Must have a frame to compute perspective!");
6204 // Get the underlying transform matrix:
6206 /* Get the matrix, then change its basis to factor in the origin. */
6207 Matrix4x4 result;
6208 // Call IsSVGTransformed() regardless of the value of
6209 // aProperties.HasTransform(), since we still need any
6210 // potential parentsChildrenOnlyTransform.
6211 Matrix svgTransform, parentsChildrenOnlyTransform;
6212 const bool hasSVGTransforms =
6213 frame && frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
6214 frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform);
6215 bool shouldRound = nsLayoutUtils::ShouldSnapToGrid(frame);
6217 /* Transformed frames always have a transform, or are preserving 3d (and might
6218 * still have perspective!) */
6219 if (aProperties.HasTransform()) {
6220 result = nsStyleTransformMatrix::ReadTransforms(
6221 aProperties.mTranslate, aProperties.mRotate, aProperties.mScale,
6222 aProperties.mMotion.ptrOr(nullptr), aProperties.mTransform, aRefBox,
6223 aAppUnitsPerPixel);
6224 } else if (hasSVGTransforms) {
6225 // Correct the translation components for zoom:
6226 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6227 svgTransform._31 *= pixelsPerCSSPx;
6228 svgTransform._32 *= pixelsPerCSSPx;
6229 result = Matrix4x4::From2D(svgTransform);
6232 // Apply any translation due to 'transform-origin' and/or 'transform-box':
6233 result.ChangeBasis(aProperties.mToTransformOrigin);
6235 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
6236 // an explanation of what children-only transforms are.
6237 const bool parentHasChildrenOnlyTransform =
6238 hasSVGTransforms && !parentsChildrenOnlyTransform.IsIdentity();
6240 if (parentHasChildrenOnlyTransform) {
6241 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6242 parentsChildrenOnlyTransform._31 *= pixelsPerCSSPx;
6243 parentsChildrenOnlyTransform._32 *= pixelsPerCSSPx;
6245 Point3D frameOffset(
6246 NSAppUnitsToFloatPixels(-frame->GetPosition().x, aAppUnitsPerPixel),
6247 NSAppUnitsToFloatPixels(-frame->GetPosition().y, aAppUnitsPerPixel), 0);
6248 Matrix4x4 parentsChildrenOnlyTransform3D =
6249 Matrix4x4::From2D(parentsChildrenOnlyTransform)
6250 .ChangeBasis(frameOffset);
6252 result *= parentsChildrenOnlyTransform3D;
6255 Matrix4x4 perspectiveMatrix;
6256 bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE;
6257 if (hasPerspective) {
6258 if (ComputePerspectiveMatrix(frame, aAppUnitsPerPixel, perspectiveMatrix)) {
6259 result *= perspectiveMatrix;
6263 if ((aFlags & INCLUDE_PRESERVE3D_ANCESTORS) && frame &&
6264 frame->Combines3DTransformWithAncestors()) {
6265 // Include the transform set on our parent
6266 nsIFrame* parentFrame =
6267 frame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6268 NS_ASSERTION(parentFrame && parentFrame->IsTransformed() &&
6269 parentFrame->Extend3DContext(),
6270 "Preserve3D mismatch!");
6271 TransformReferenceBox refBox(parentFrame);
6272 FrameTransformProperties props(parentFrame, refBox, aAppUnitsPerPixel);
6274 uint32_t flags =
6275 aFlags & (INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE);
6277 // If this frame isn't transformed (but we exist for backface-visibility),
6278 // then we're not a reference frame so no offset to origin will be added.
6279 // Otherwise we need to manually translate into our parent's coordinate
6280 // space.
6281 if (frame->IsTransformed()) {
6282 nsLayoutUtils::PostTranslate(result, frame->GetPosition(),
6283 aAppUnitsPerPixel, shouldRound);
6285 Matrix4x4 parent = GetResultingTransformMatrixInternal(
6286 props, refBox, nsPoint(0, 0), aAppUnitsPerPixel, flags);
6287 result = result * parent;
6290 if (aFlags & OFFSET_BY_ORIGIN) {
6291 nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel,
6292 shouldRound);
6295 return result;
6298 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6299 static constexpr nsCSSPropertyIDSet opacitySet =
6300 nsCSSPropertyIDSet::OpacityProperties();
6301 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, opacitySet)) {
6302 return true;
6305 EffectCompositor::SetPerformanceWarning(
6306 mFrame, opacitySet,
6307 AnimationPerformanceWarning(
6308 AnimationPerformanceWarning::Type::OpacityFrameInactive));
6310 return false;
6313 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6314 return mPrerenderDecision != PrerenderDecision::No;
6317 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
6318 nsDisplayListBuilder* aBuilder) {
6319 return StaticPrefs::gfx_omta_background_color();
6322 static bool IsInStickyPositionedSubtree(const nsIFrame* aFrame) {
6323 for (const nsIFrame* frame = aFrame; frame;
6324 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame)) {
6325 if (frame->IsStickyPositioned()) {
6326 return true;
6329 return false;
6332 static bool ShouldUsePartialPrerender(const nsIFrame* aFrame) {
6333 return StaticPrefs::layout_animation_prerender_partial() &&
6334 // Bug 1642547: Support partial prerender for position:sticky elements.
6335 !IsInStickyPositionedSubtree(aFrame);
6338 /* static */
6339 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
6340 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect)
6341 -> PrerenderInfo {
6342 PrerenderInfo result;
6343 // If we are in a preserve-3d tree, and we've disallowed async animations, we
6344 // return No prerender decision directly.
6345 if ((aFrame->Extend3DContext() ||
6346 aFrame->Combines3DTransformWithAncestors()) &&
6347 !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
6348 return result;
6351 // Elements whose transform has been modified recently, or which
6352 // have a compositor-animated transform, can be prerendered. An element
6353 // might have only just had its transform animated in which case
6354 // the ActiveLayerManager may not have been notified yet.
6355 static constexpr nsCSSPropertyIDSet transformSet =
6356 nsCSSPropertyIDSet::TransformLikeProperties();
6357 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame) &&
6358 !EffectCompositor::HasAnimationsForCompositor(
6359 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
6360 EffectCompositor::SetPerformanceWarning(
6361 aFrame, transformSet,
6362 AnimationPerformanceWarning(
6363 AnimationPerformanceWarning::Type::TransformFrameInactive));
6365 // This case happens when we're sure that the frame is not animated and its
6366 // preserve-3d ancestors are not, either. So we don't need to pre-render.
6367 // However, this decision shouldn't affect the decisions for other frames in
6368 // the preserve-3d context. We need this flag to determine whether we should
6369 // block async animations on other frames in the current preserve-3d tree.
6370 result.mHasAnimations = false;
6371 return result;
6374 // We should not allow prerender if any ancestor container element has
6375 // mask/clip-path effects.
6377 // With prerender and async transform animation, we do not need to restyle an
6378 // animated element to respect position changes, since that transform is done
6379 // by layer animation. As a result, the container element is not aware of
6380 // position change of that containing element and loses the chance to update
6381 // the content of mask/clip-path.
6383 // Why do we need to update a mask? This is relative to how we generate a
6384 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
6385 // mask layer, to reduce memory usage, we did not choose the size of the
6386 // masked element as mask size. Instead, we read the union of bounds of all
6387 // children display items by nsDisplayWrapList::GetBounds, which is smaller
6388 // than or equal to the masked element's boundary, and use it as the position
6389 // size of the mask layer. That union bounds is actually affected by the
6390 // geometry of the animated element. To keep the content of mask up to date,
6391 // forbidding of prerender is required.
6392 for (nsIFrame* container =
6393 nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
6394 container;
6395 container = nsLayoutUtils::GetCrossDocParentFrameInProcess(container)) {
6396 const nsStyleSVGReset* svgReset = container->StyleSVGReset();
6397 if (svgReset->HasMask() || svgReset->HasClipPath()) {
6398 return result;
6402 // If the incoming dirty rect already contains the entire overflow area,
6403 // we are already rendering the entire content.
6404 nsRect overflow = aFrame->InkOverflowRectRelativeToSelf();
6405 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
6406 // in cases of non-invertible transforms, so we set `untransformedRect` to
6407 // `aDirtyRect` as an initial value for such cases.
6408 nsRect untransformedDirtyRect = *aDirtyRect;
6409 UntransformRect(*aDirtyRect, overflow, aFrame, &untransformedDirtyRect);
6410 if (untransformedDirtyRect.Contains(overflow)) {
6411 *aDirtyRect = untransformedDirtyRect;
6412 result.mDecision = PrerenderDecision::Full;
6413 return result;
6416 float viewportRatio =
6417 StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
6418 uint32_t absoluteLimitX =
6419 StaticPrefs::layout_animation_prerender_absolute_limit_x();
6420 uint32_t absoluteLimitY =
6421 StaticPrefs::layout_animation_prerender_absolute_limit_y();
6422 nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
6424 float resolution = aFrame->PresShell()->GetCumulativeResolution();
6425 if (resolution < 1.0f) {
6426 refSize.SizeTo(
6427 NSCoordSaturatingNonnegativeMultiply(refSize.width, 1.0f / resolution),
6428 NSCoordSaturatingNonnegativeMultiply(refSize.height,
6429 1.0f / resolution));
6432 // Only prerender if the transformed frame's size is <= a multiple of the
6433 // reference frame size (~viewport), and less than an absolute limit.
6434 // Both the ratio and the absolute limit are configurable.
6435 nscoord maxLength = std::max(nscoord(refSize.width * viewportRatio),
6436 nscoord(refSize.height * viewportRatio));
6437 nsSize relativeLimit(maxLength, maxLength);
6438 nsSize absoluteLimit(
6439 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
6440 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
6441 nsSize maxSize = Min(relativeLimit, absoluteLimit);
6443 const auto transform = nsLayoutUtils::GetTransformToAncestor(
6444 RelativeTo{aFrame},
6445 RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});
6446 const gfxRect transformedBounds = transform.TransformAndClipBounds(
6447 gfxRect(overflow.x, overflow.y, overflow.width, overflow.height),
6448 gfxRect::MaxIntRect());
6449 const nsSize frameSize =
6450 nsSize(transformedBounds.width, transformedBounds.height);
6452 uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
6453 uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
6454 if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
6455 *aDirtyRect = overflow;
6456 result.mDecision = PrerenderDecision::Full;
6457 return result;
6460 if (ShouldUsePartialPrerender(aFrame)) {
6461 *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(
6462 aFrame, untransformedDirtyRect, overflow, maxSize);
6463 result.mDecision = PrerenderDecision::Partial;
6464 return result;
6467 if (frameArea > maxLimitArea) {
6468 uint64_t appUnitsPerPixel = AppUnitsPerCSSPixel();
6469 EffectCompositor::SetPerformanceWarning(
6470 aFrame, transformSet,
6471 AnimationPerformanceWarning(
6472 AnimationPerformanceWarning::Type::ContentTooLargeArea,
6474 int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
6475 int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
6476 }));
6477 } else {
6478 EffectCompositor::SetPerformanceWarning(
6479 aFrame, transformSet,
6480 AnimationPerformanceWarning(
6481 AnimationPerformanceWarning::Type::ContentTooLarge,
6483 nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
6484 nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
6485 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
6486 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
6487 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
6488 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
6489 }));
6492 return result;
6495 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
6496 * visible or hit. */
6497 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix) {
6498 if (aMatrix.IsSingular()) {
6499 return false;
6501 if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) {
6502 return false;
6504 return true;
6507 const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const {
6508 if (mTransform) {
6509 return *mTransform;
6512 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6514 if (mHasTransformGetter) {
6515 mTransform.emplace((mFrame->GetTransformGetter())(mFrame, scale));
6516 Point3D newOrigin =
6517 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
6518 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f);
6519 mTransform->ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
6520 } else if (!mIsTransformSeparator) {
6521 DebugOnly<bool> isReference = mFrame->IsTransformed() ||
6522 mFrame->Combines3DTransformWithAncestors() ||
6523 mFrame->Extend3DContext();
6524 MOZ_ASSERT(isReference);
6525 mTransform.emplace(
6526 GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale,
6527 INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN));
6528 } else {
6529 // Use identity matrix
6530 mTransform.emplace();
6533 return *mTransform;
6536 const Matrix4x4Flagged& nsDisplayTransform::GetInverseTransform() const {
6537 if (mInverseTransform) {
6538 return *mInverseTransform;
6541 MOZ_ASSERT(!GetTransform().IsSingular());
6543 mInverseTransform.emplace(GetTransform().Inverse());
6545 return *mInverseTransform;
6548 Matrix4x4 nsDisplayTransform::GetTransformForRendering(
6549 LayoutDevicePoint* aOutOrigin) const {
6550 if (!mFrame->HasPerspective() || mHasTransformGetter ||
6551 mIsTransformSeparator) {
6552 if (!mHasTransformGetter && !mIsTransformSeparator && aOutOrigin) {
6553 // If aOutOrigin is provided, put the offset to origin into it, because
6554 // we need to keep it separate for webrender. The combination of
6555 // *aOutOrigin and the returned matrix here should always be equivalent
6556 // to what GetTransform() would have returned.
6557 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6558 *aOutOrigin = LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale);
6560 // The rounding behavior should also be the same as GetTransform().
6561 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6562 aOutOrigin->Round();
6564 return GetResultingTransformMatrix(mFrame, nsPoint(0, 0), scale,
6565 INCLUDE_PERSPECTIVE);
6567 return GetTransform().GetMatrix();
6569 MOZ_ASSERT(!mHasTransformGetter);
6571 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6572 // Don't include perspective transform, or the offset to origin, since
6573 // nsDisplayPerspective will handle both of those.
6574 return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
6577 const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
6578 nsDisplayListBuilder* aBuilder) {
6579 MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
6581 if (!IsLeafOf3DContext()) {
6582 return GetTransform().GetMatrix();
6585 if (!mTransformPreserves3D) {
6586 const nsIFrame* establisher; // Establisher of the 3D rendering context.
6587 for (establisher = mFrame;
6588 establisher && establisher->Combines3DTransformWithAncestors();
6589 establisher =
6590 establisher->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
6592 const nsIFrame* establisherReference = aBuilder->FindReferenceFrameFor(
6593 nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher));
6595 nsPoint offset = establisher->GetOffsetToCrossDoc(establisherReference);
6596 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6597 uint32_t flags =
6598 INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN;
6599 mTransformPreserves3D = MakeUnique<Matrix4x4>(
6600 GetResultingTransformMatrix(mFrame, offset, scale, flags));
6603 return *mTransformPreserves3D;
6606 bool nsDisplayTransform::CreateWebRenderCommands(
6607 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
6608 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
6609 nsDisplayListBuilder* aDisplayListBuilder) {
6610 // We want to make sure we don't pollute the transform property in the WR
6611 // stacking context by including the position of this frame (relative to the
6612 // parent reference frame). We need to keep those separate; the position of
6613 // this frame goes into the stacking context bounds while the transform goes
6614 // into the transform.
6615 LayoutDevicePoint position;
6616 Matrix4x4 newTransformMatrix = GetTransformForRendering(&position);
6618 gfx::Matrix4x4* transformForSC = &newTransformMatrix;
6619 if (newTransformMatrix.IsIdentity()) {
6620 // If the transform is an identity transform, strip it out so that WR
6621 // doesn't turn this stacking context into a reference frame, as it
6622 // affects positioning. Bug 1345577 tracks a better fix.
6623 transformForSC = nullptr;
6625 // In ChooseScaleAndSetTransform, we round the offset from the reference
6626 // frame used to adjust the transform, if there is no transform, or it
6627 // is just a translation. We need to do the same here.
6628 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6629 position.Round();
6633 auto key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
6634 wr::SpatialKeyKind::Transform);
6636 // We don't send animations for transform separator display items.
6637 uint64_t animationsId =
6638 mIsTransformSeparator
6640 : AddAnimationsForWebRender(
6641 this, aManager, aDisplayListBuilder,
6642 IsPartialPrerender() ? Some(position) : Nothing());
6643 wr::WrAnimationProperty prop{wr::WrAnimationType::Transform, animationsId,
6644 key};
6646 nsDisplayTransform* deferredTransformItem = nullptr;
6647 if (!mFrame->ChildrenHavePerspective()) {
6648 // If it has perspective, we create a new scroll data via the
6649 // UpdateScrollData call because that scenario is more complex. Otherwise
6650 // we can just stash the transform on the StackingContextHelper and
6651 // apply it to any scroll data that are created inside this
6652 // nsDisplayTransform.
6653 deferredTransformItem = this;
6656 // Determine if we're possibly animated (= would need an active layer in FLB).
6657 bool animated = !mIsTransformSeparator &&
6658 ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
6660 wr::StackingContextParams params;
6661 params.mBoundTransform = &newTransformMatrix;
6662 params.animation = animationsId ? &prop : nullptr;
6664 wr::WrTransformInfo transform_info;
6665 if (transformForSC) {
6666 transform_info.transform = wr::ToLayoutTransform(newTransformMatrix);
6667 transform_info.key = key;
6668 params.mTransformPtr = &transform_info;
6669 } else {
6670 params.mTransformPtr = nullptr;
6673 params.prim_flags = !BackfaceIsHidden()
6674 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
6675 : wr::PrimitiveFlags{0};
6676 params.paired_with_perspective = mHasAssociatedPerspective;
6677 params.mDeferredTransformItem = deferredTransformItem;
6678 params.mAnimated = animated;
6679 // Determine if we would have to rasterize any items in local raster space
6680 // (i.e. disable subpixel AA). We don't always need to rasterize locally even
6681 // if the stacking context is possibly animated (at the cost of potentially
6682 // some false negatives with respect to will-change handling), so we pass in
6683 // this determination separately to accurately match with when FLB would
6684 // normally disable subpixel AA.
6685 params.mRasterizeLocally = animated && Frame()->HasAnimationOfTransform();
6686 params.SetPreserve3D(mFrame->Extend3DContext() && !mIsTransformSeparator);
6687 params.clip =
6688 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
6690 LayoutDeviceSize boundsSize = LayoutDeviceSize::FromAppUnits(
6691 mChildBounds.Size(), mFrame->PresContext()->AppUnitsPerDevPixel());
6693 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
6694 params, LayoutDeviceRect(position, boundsSize));
6696 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6697 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
6698 return true;
6701 bool nsDisplayTransform::UpdateScrollData(
6702 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
6703 if (!mFrame->ChildrenHavePerspective()) {
6704 // This case is handled in CreateWebRenderCommands by stashing the transform
6705 // on the stacking context.
6706 return false;
6708 if (aLayerData) {
6709 aLayerData->SetTransform(GetTransform().GetMatrix());
6710 aLayerData->SetTransformIsPerspective(true);
6712 return true;
6715 bool nsDisplayTransform::ShouldSkipTransform(
6716 nsDisplayListBuilder* aBuilder) const {
6717 return (aBuilder->RootReferenceFrame() == mFrame) &&
6718 aBuilder->IsForGenerateGlyphMask();
6721 void nsDisplayTransform::Collect3DTransformLeaves(
6722 nsDisplayListBuilder* aBuilder, nsTArray<nsDisplayTransform*>& aLeaves) {
6723 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6724 aLeaves.AppendElement(this);
6725 return;
6728 FlattenedDisplayListIterator iter(aBuilder, &mChildren);
6729 while (iter.HasNext()) {
6730 nsDisplayItem* item = iter.GetNextItem();
6731 if (item->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
6732 auto* perspective = static_cast<nsDisplayPerspective*>(item);
6733 if (!perspective->GetChildren()->GetTop()) {
6734 continue;
6736 item = perspective->GetChildren()->GetTop();
6738 if (item->GetType() != DisplayItemType::TYPE_TRANSFORM) {
6739 gfxCriticalError() << "Invalid child item within 3D transform of type: "
6740 << item->Name();
6741 continue;
6743 static_cast<nsDisplayTransform*>(item)->Collect3DTransformLeaves(aBuilder,
6744 aLeaves);
6748 static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
6749 const gfx::Polygon& aPolygon) {
6750 MOZ_ASSERT(!aPolygon.IsEmpty());
6752 RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
6753 const nsTArray<Point4D>& points = aPolygon.GetPoints();
6755 pathBuilder->MoveTo(points[0].As2DPoint());
6757 for (size_t i = 1; i < points.Length(); ++i) {
6758 pathBuilder->LineTo(points[i].As2DPoint());
6761 pathBuilder->Close();
6762 return pathBuilder->Finish();
6765 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6766 nsDisplayListBuilder* aBuilder, nsTArray<TransformPolygon>& aLeaves) {
6767 std::list<TransformPolygon> inputLayers;
6769 nsTArray<nsDisplayTransform*> leaves;
6770 Collect3DTransformLeaves(aBuilder, leaves);
6771 for (nsDisplayTransform* item : leaves) {
6772 auto bounds = LayoutDeviceRect::FromAppUnits(
6773 item->mChildBounds, item->mFrame->PresContext()->AppUnitsPerDevPixel());
6774 Matrix4x4 transform = item->GetAccumulatedPreserved3DTransform(aBuilder);
6776 if (!IsFrameVisible(item->mFrame, transform)) {
6777 continue;
6779 gfx::Polygon polygon =
6780 gfx::Polygon::FromRect(gfx::Rect(bounds.ToUnknownRect()));
6782 polygon.TransformToScreenSpace(transform);
6784 if (polygon.GetPoints().Length() >= 3) {
6785 inputLayers.push_back(TransformPolygon(item, std::move(polygon)));
6789 if (inputLayers.empty()) {
6790 return;
6793 BSPTree<nsDisplayTransform> tree(inputLayers);
6794 nsTArray<TransformPolygon> orderedLayers(tree.GetDrawOrder());
6796 for (TransformPolygon& polygon : orderedLayers) {
6797 Matrix4x4 inverse =
6798 polygon.data->GetAccumulatedPreserved3DTransform(aBuilder).Inverse();
6800 MOZ_ASSERT(polygon.geometry);
6801 polygon.geometry->TransformToLayerSpace(inverse);
6804 aLeaves = std::move(orderedLayers);
6807 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder,
6808 gfxContext* aCtx) {
6809 Paint(aBuilder, aCtx, Nothing());
6812 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
6813 const Maybe<gfx::Polygon>& aPolygon) {
6814 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6815 MOZ_ASSERT(!aPolygon);
6816 nsTArray<TransformPolygon> leaves;
6817 CollectSorted3DTransformLeaves(aBuilder, leaves);
6818 for (TransformPolygon& item : leaves) {
6819 item.data->Paint(aBuilder, aCtx, item.geometry);
6821 return;
6824 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
6825 Matrix4x4 trans = ShouldSkipTransform(aBuilder)
6826 ? Matrix4x4()
6827 : GetAccumulatedPreserved3DTransform(aBuilder);
6828 if (!IsFrameVisible(mFrame, trans)) {
6829 return;
6832 Matrix trans2d;
6833 if (trans.CanDraw2D(&trans2d)) {
6834 aCtx->Multiply(ThebesMatrix(trans2d));
6836 if (aPolygon) {
6837 RefPtr<gfx::Path> path =
6838 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6839 aCtx->GetDrawTarget()->PushClip(path);
6842 GetChildren()->Paint(aBuilder, aCtx,
6843 mFrame->PresContext()->AppUnitsPerDevPixel());
6845 if (aPolygon) {
6846 aCtx->GetDrawTarget()->PopClip();
6848 return;
6851 // TODO: Implement 3d transform handling, including plane splitting and
6852 // sorting. See BasicCompositor.
6853 auto pixelBounds = LayoutDeviceRect::FromAppUnitsToOutside(
6854 mChildBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
6855 RefPtr<DrawTarget> untransformedDT =
6856 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6857 IntSize(pixelBounds.Width(), pixelBounds.Height()),
6858 SurfaceFormat::B8G8R8A8, true);
6859 if (!untransformedDT || !untransformedDT->IsValid()) {
6860 return;
6862 untransformedDT->SetTransform(
6863 Matrix::Translation(-Point(pixelBounds.X(), pixelBounds.Y())));
6865 gfxContext groupTarget(untransformedDT, /* aPreserveTransform */ true);
6867 if (aPolygon) {
6868 RefPtr<gfx::Path> path =
6869 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6870 aCtx->GetDrawTarget()->PushClip(path);
6873 GetChildren()->Paint(aBuilder, &groupTarget,
6874 mFrame->PresContext()->AppUnitsPerDevPixel());
6876 if (aPolygon) {
6877 aCtx->GetDrawTarget()->PopClip();
6880 RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
6882 trans.PreTranslate(pixelBounds.X(), pixelBounds.Y(), 0);
6883 aCtx->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf, trans);
6886 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
6887 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6888 // completely bypass the main thread for this animation, so it is always
6889 // worthwhile.
6890 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6891 // already involved so there is less to be gained.
6892 // Therefore we check that the *post-transform* bounds of this item are
6893 // big enough to justify an active layer.
6894 return EffectCompositor::HasAnimationsForCompositor(
6895 mFrame, DisplayItemType::TYPE_TRANSFORM) ||
6896 (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame));
6899 nsRect nsDisplayTransform::TransformUntransformedBounds(
6900 nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
6901 bool snap;
6902 const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
6903 // GetTransform always operates in dev pixels.
6904 const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
6905 return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
6906 factor);
6910 * Returns the bounds for this transform. The bounds are calculated during
6911 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6913 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
6914 bool* aSnap) const {
6915 *aSnap = false;
6916 return mBounds;
6919 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
6920 MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
6922 /* Some transforms can get empty bounds in 2D, but might get transformed again
6923 * and get non-empty bounds. A simple example of this would be a 180 degree
6924 * rotation getting applied twice.
6925 * We should not depend on transforming bounds level by level.
6927 * This function collects the bounds of this transform and stores it in
6928 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6929 * down and include the bounds of the child transforms.
6930 * The bounds are transformed with the accumulated transformation matrix up to
6931 * the 3D context root coordinate space.
6933 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6934 accTransform.Accumulate(GetTransform().GetMatrix());
6936 // Do not dive into another 3D context.
6937 if (!IsLeafOf3DContext()) {
6938 for (nsDisplayItem* i : *GetChildren()) {
6939 i->DoUpdateBoundsPreserves3D(aBuilder);
6943 /* The child transforms that extend 3D context further will have empty bounds,
6944 * so the untransformed bounds here is the bounds of all the non-preserve-3d
6945 * content under this transform.
6947 const nsRect rect = TransformUntransformedBounds(
6948 aBuilder, accTransform.GetCurrentTransform());
6949 aBuilder->AccumulateRect(rect);
6952 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6953 nsDisplayListBuilder* aBuilder) {
6954 MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
6955 IsTransformSeparator());
6956 // Updating is not going through to child 3D context.
6957 ComputeBounds(aBuilder);
6960 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
6961 UpdateUntransformedBounds(aBuilder);
6963 if (IsTransformSeparator()) {
6964 MOZ_ASSERT(GetTransform().IsIdentity());
6965 mBounds = mChildBounds;
6966 return;
6969 if (mFrame->Extend3DContext()) {
6970 if (!Combines3DTransformWithAncestors()) {
6971 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6972 // collect the bounds from the child transforms.
6973 UpdateBoundsFor3D(aBuilder);
6974 } else {
6975 // With nested 3D transforms, the 2D bounds might not be useful.
6976 mBounds = nsRect();
6979 return;
6982 MOZ_ASSERT(!mFrame->Extend3DContext());
6984 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
6985 // but mix-blend-mode relies on having bounds set. See bug 1556956.
6987 // A stand-alone transform.
6988 mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
6991 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
6992 MOZ_ASSERT(mFrame->Extend3DContext() &&
6993 !mFrame->Combines3DTransformWithAncestors() &&
6994 !IsTransformSeparator());
6996 // Always start updating from an establisher of a 3D rendering context.
6997 nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
6998 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6999 accTransform.StartRoot();
7000 ComputeBounds(aBuilder);
7001 mBounds = aBuilder->GetAccumulatedRect();
7004 void nsDisplayTransform::UpdateUntransformedBounds(
7005 nsDisplayListBuilder* aBuilder) {
7006 mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
7007 aBuilder, mActiveScrolledRoot);
7010 #ifdef DEBUG_HIT
7011 # include <time.h>
7012 #endif
7014 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
7015 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
7016 const nsRect& aRect, HitTestState* aState,
7017 nsTArray<nsIFrame*>* aOutFrames) {
7018 if (aState->mInPreserves3D) {
7019 GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
7020 return;
7023 /* Here's how this works:
7024 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
7025 * anything).
7026 * 2. Invert the matrix.
7027 * 3. Use it to transform the rect into the correct space.
7028 * 4. Pass that rect down through to the list's version of HitTest.
7030 // GetTransform always operates in dev pixels.
7031 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7032 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7034 if (!IsFrameVisible(mFrame, matrix)) {
7035 return;
7038 const bool oldHitOccludingItem = aState->mHitOccludingItem;
7040 /* We want to go from transformed-space to regular space.
7041 * Thus we have to invert the matrix, which normally does
7042 * the reverse operation (e.g. regular->transformed)
7045 /* Now, apply the transform and pass it down the channel. */
7046 matrix.Invert();
7047 nsRect resultingRect;
7048 // Magic width/height indicating we're hit testing a point, not a rect
7049 const bool testingPoint = aRect.width == 1 && aRect.height == 1;
7050 if (testingPoint) {
7051 Point4D point =
7052 matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
7053 NSAppUnitsToFloatPixels(aRect.y, factor)));
7054 if (!point.HasPositiveWCoord()) {
7055 return;
7058 Point point2d = point.As2DPoint();
7060 resultingRect =
7061 nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
7062 NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1);
7064 } else {
7065 Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
7066 NSAppUnitsToFloatPixels(aRect.y, factor),
7067 NSAppUnitsToFloatPixels(aRect.width, factor),
7068 NSAppUnitsToFloatPixels(aRect.height, factor));
7070 Rect childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds.x, factor),
7071 NSAppUnitsToFloatPixels(mChildBounds.y, factor),
7072 NSAppUnitsToFloatPixels(mChildBounds.width, factor),
7073 NSAppUnitsToFloatPixels(mChildBounds.height, factor));
7075 Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
7077 resultingRect =
7078 nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
7079 NSFloatPixelsToAppUnits(float(rect.Y()), factor),
7080 NSFloatPixelsToAppUnits(float(rect.Width()), factor),
7081 NSFloatPixelsToAppUnits(float(rect.Height()), factor));
7084 if (resultingRect.IsEmpty()) {
7085 return;
7088 #ifdef DEBUG_HIT
7089 printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
7090 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(),
7091 resultingRect.Y());
7092 uint32_t originalFrameCount = aOutFrames.Length();
7093 #endif
7095 GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
7097 if (aState->mHitOccludingItem && !testingPoint &&
7098 !mChildBounds.Contains(aRect)) {
7099 MOZ_ASSERT(aBuilder->HitTestIsForVisibility());
7100 // We're hit-testing a rect that's bigger than our child bounds, but
7101 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so
7102 // we can't stop hit-testing altogether.
7104 // FIXME(emilio): I think this means that theoretically we might include
7105 // some frames fully behind other transformed-but-opaque frames? Then again
7106 // that's our pre-existing behavior for other untransformed content that
7107 // doesn't fill the whole rect. To be fully correct I think we'd need proper
7108 // "known occluded region" tracking, but that might be overkill for our
7109 // purposes here.
7110 aState->mHitOccludingItem = oldHitOccludingItem;
7113 #ifdef DEBUG_HIT
7114 if (originalFrameCount != aOutFrames.Length())
7115 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7116 dynamic_cast<void*>(aOutFrames.ElementAt(0)));
7117 printf("=== end of hit test ===\n");
7118 #endif
7121 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
7122 const nsPoint& aPoint) {
7123 // GetTransform always operates in dev pixels.
7124 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7125 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7127 NS_ASSERTION(IsFrameVisible(mFrame, matrix),
7128 "We can't have hit a frame that isn't visible!");
7130 Matrix4x4 inverse = matrix;
7131 inverse.Invert();
7132 Point4D point =
7133 inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
7134 NSAppUnitsToFloatPixels(aPoint.y, factor)));
7136 Point point2d = point.As2DPoint();
7138 Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
7139 return transformed.z;
7142 /* The transform is opaque iff the transform consists solely of scales and
7143 * translations and if the underlying content is opaque. Thus if the transform
7144 * is of the form
7146 * |a c e|
7147 * |b d f|
7148 * |0 0 1|
7150 * We need b and c to be zero.
7152 * We also need to check whether the underlying opaque content completely fills
7153 * our visible rect. We use UntransformRect which expands to the axis-aligned
7154 * bounding rect, but that's OK since if
7155 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7156 * certainly contains the actual (non-axis-aligned) untransformed rect.
7158 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7159 bool* aSnap) const {
7160 *aSnap = false;
7162 nsRect untransformedVisible;
7163 if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
7164 return nsRegion();
7167 const Matrix4x4Flagged& matrix = GetTransform();
7168 Matrix matrix2d;
7169 if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
7170 return nsRegion();
7173 nsRegion result;
7175 bool tmpSnap;
7176 const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
7177 const nsRegion opaque =
7178 ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
7180 if (opaque.Contains(untransformedVisible)) {
7181 result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
7183 return result;
7186 nsRect nsDisplayTransform::GetComponentAlphaBounds(
7187 nsDisplayListBuilder* aBuilder) const {
7188 if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
7189 return nsRect();
7192 bool snap;
7193 return GetBounds(aBuilder, &snap);
7196 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7197 * the smallest rectangle (in app space) containing the transformed image of
7198 * that rectangle. That is, it takes the four corners of the rectangle,
7199 * transforms them according to the matrix associated with the specified frame,
7200 * then returns the smallest rectangle containing the four transformed points.
7202 * @param aUntransformedBounds The rectangle (in app units) to transform.
7203 * @param aFrame The frame whose transformation should be applied.
7204 * @param aOrigin The delta from the frame origin to the coordinate space origin
7205 * @return The smallest rectangle containing the image of the transformed
7206 * rectangle.
7208 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds,
7209 const nsIFrame* aFrame,
7210 TransformReferenceBox& aRefBox) {
7211 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7213 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7215 FrameTransformProperties props(aFrame, aRefBox, factor);
7216 return nsLayoutUtils::MatrixTransformRect(
7217 aUntransformedBounds,
7218 GetResultingTransformMatrixInternal(props, aRefBox, nsPoint(), factor,
7219 kTransformRectFlags),
7220 factor);
7223 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7224 const nsRect& aChildBounds,
7225 const nsIFrame* aFrame,
7226 nsRect* aOutRect) {
7227 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7229 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7230 Matrix4x4 transform = GetResultingTransformMatrix(aFrame, nsPoint(), factor,
7231 kTransformRectFlags);
7232 return UntransformRect(aTransformedBounds, aChildBounds, transform, factor,
7233 aOutRect);
7236 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7237 const nsRect& aChildBounds,
7238 const Matrix4x4& aMatrix,
7239 float aAppUnitsPerPixel,
7240 nsRect* aOutRect) {
7241 if (aMatrix.IsSingular()) {
7242 return false;
7245 RectDouble result(
7246 NSAppUnitsToFloatPixels(aTransformedBounds.x, aAppUnitsPerPixel),
7247 NSAppUnitsToFloatPixels(aTransformedBounds.y, aAppUnitsPerPixel),
7248 NSAppUnitsToFloatPixels(aTransformedBounds.width, aAppUnitsPerPixel),
7249 NSAppUnitsToFloatPixels(aTransformedBounds.height, aAppUnitsPerPixel));
7251 RectDouble childGfxBounds(
7252 NSAppUnitsToFloatPixels(aChildBounds.x, aAppUnitsPerPixel),
7253 NSAppUnitsToFloatPixels(aChildBounds.y, aAppUnitsPerPixel),
7254 NSAppUnitsToFloatPixels(aChildBounds.width, aAppUnitsPerPixel),
7255 NSAppUnitsToFloatPixels(aChildBounds.height, aAppUnitsPerPixel));
7257 result = aMatrix.Inverse().ProjectRectBounds(result, childGfxBounds);
7258 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result),
7259 aAppUnitsPerPixel);
7260 return true;
7263 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder,
7264 const nsRect& aRect,
7265 nsRect* aOutRect) const {
7266 if (GetTransform().IsSingular()) {
7267 return false;
7270 // GetTransform always operates in dev pixels.
7271 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7272 RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor),
7273 NSAppUnitsToFloatPixels(aRect.y, factor),
7274 NSAppUnitsToFloatPixels(aRect.width, factor),
7275 NSAppUnitsToFloatPixels(aRect.height, factor));
7277 bool snap;
7278 nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
7279 RectDouble childGfxBounds(
7280 NSAppUnitsToFloatPixels(childBounds.x, factor),
7281 NSAppUnitsToFloatPixels(childBounds.y, factor),
7282 NSAppUnitsToFloatPixels(childBounds.width, factor),
7283 NSAppUnitsToFloatPixels(childBounds.height, factor));
7285 /* We want to untransform the matrix, so invert the transformation first! */
7286 result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds);
7288 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
7290 return true;
7293 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) {
7294 aStream << GetTransform().GetMatrix();
7295 if (IsTransformSeparator()) {
7296 aStream << " transform-separator";
7298 if (IsLeafOf3DContext()) {
7299 aStream << " 3d-context-leaf";
7301 if (mFrame->Extend3DContext()) {
7302 aStream << " extends-3d-context";
7304 if (mFrame->Combines3DTransformWithAncestors()) {
7305 aStream << " combines-3d-with-ancestors";
7308 aStream << " prerender(";
7309 switch (mPrerenderDecision) {
7310 case PrerenderDecision::No:
7311 aStream << "no";
7312 break;
7313 case PrerenderDecision::Partial:
7314 aStream << "partial";
7315 break;
7316 case PrerenderDecision::Full:
7317 aStream << "full";
7318 break;
7320 aStream << ")";
7321 aStream << " childrenBuildingRect" << mChildrenBuildingRect;
7324 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
7325 nsIFrame* aFrame,
7326 nsDisplayList* aList)
7327 : nsPaintedDisplayItem(aBuilder, aFrame), mList(aBuilder) {
7328 mList.AppendToTop(aList);
7329 MOZ_ASSERT(mList.Length() == 1);
7330 MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
7333 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder,
7334 gfxContext* aCtx) {
7335 // Just directly recurse into children, since we'll include the persepctive
7336 // value in any nsDisplayTransform children.
7337 GetChildren()->Paint(aBuilder, aCtx,
7338 mFrame->PresContext()->AppUnitsPerDevPixel());
7341 nsRegion nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7342 bool* aSnap) const {
7343 if (!GetChildren()->GetTop()) {
7344 *aSnap = false;
7345 return nsRegion();
7348 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder, aSnap);
7351 bool nsDisplayPerspective::CreateWebRenderCommands(
7352 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7353 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7354 nsDisplayListBuilder* aDisplayListBuilder) {
7355 float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
7356 Matrix4x4 perspectiveMatrix;
7357 DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
7358 mFrame, appUnitsPerPixel, perspectiveMatrix);
7359 MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
7362 * ClipListToRange can remove our child after we were created.
7364 if (!GetChildren()->GetTop()) {
7365 return false;
7369 * The resulting matrix is still in the coordinate space of the transformed
7370 * frame. Append a translation to the reference frame coordinates.
7372 nsDisplayTransform* transform =
7373 static_cast<nsDisplayTransform*>(GetChildren()->GetTop());
7375 Point3D newOrigin =
7376 Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
7377 appUnitsPerPixel),
7378 NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
7379 appUnitsPerPixel),
7380 0.0f);
7381 Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
7383 perspectiveMatrix.PostTranslate(roundedOrigin);
7385 nsIFrame* perspectiveFrame =
7386 mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
7388 // Passing true here is always correct, since perspective always combines
7389 // transforms with the descendants. However that'd make WR do a lot of work
7390 // that it doesn't really need to do if there aren't other transforms forming
7391 // part of the 3D context.
7393 // WR knows how to treat perspective in that case, so the only thing we need
7394 // to do is to ensure we pass true when we're involved in a 3d context in any
7395 // other way via the transform-style property on either the transformed frame
7396 // or the perspective frame in order to not confuse WR's preserve-3d code in
7397 // very awful ways.
7398 bool preserve3D =
7399 mFrame->Extend3DContext() || perspectiveFrame->Extend3DContext();
7401 wr::StackingContextParams params;
7403 wr::WrTransformInfo transform_info;
7404 transform_info.transform = wr::ToLayoutTransform(perspectiveMatrix);
7405 transform_info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
7406 wr::SpatialKeyKind::Perspective);
7407 params.mTransformPtr = &transform_info;
7409 params.reference_frame_kind = wr::WrReferenceFrameKind::Perspective;
7410 params.prim_flags = !BackfaceIsHidden()
7411 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7412 : wr::PrimitiveFlags{0};
7413 params.SetPreserve3D(preserve3D);
7414 params.clip =
7415 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
7417 Maybe<uint64_t> scrollingRelativeTo;
7418 for (const auto* asr = GetActiveScrolledRoot(); asr; asr = asr->mParent) {
7419 // In OOP documents, the root scrollable frame of the in-process root
7420 // document is always active, so using IsAncestorFrameCrossDocInProcess
7421 // should be fine here.
7422 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7423 asr->mScrollableFrame->GetScrolledFrame(), perspectiveFrame)) {
7424 scrollingRelativeTo.emplace(asr->GetViewId());
7425 break;
7429 // We put the perspective reference frame wrapping the transformed frame,
7430 // even though there may be arbitrarily nested scroll frames in between.
7432 // We need to know how many ancestor scroll-frames are we nested in, in order
7433 // for the async scrolling code in WebRender to calculate the right
7434 // transformation for the perspective contents.
7435 params.scrolling_relative_to = scrollingRelativeTo.ptrOr(nullptr);
7437 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
7438 params);
7440 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7441 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
7443 return true;
7446 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
7447 nsTextFrame* aFrame)
7448 : nsPaintedDisplayItem(aBuilder, aFrame),
7449 mVisIStartEdge(0),
7450 mVisIEndEdge(0) {
7451 MOZ_COUNT_CTOR(nsDisplayText);
7452 mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7453 // Bug 748228
7454 mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
7455 mVisibleRect = aBuilder->GetVisibleRect() +
7456 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
7459 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager,
7460 nsDisplayListBuilder* aBuilder) const {
7461 auto* f = static_cast<nsTextFrame*>(mFrame);
7463 if (f->IsSelected()) {
7464 return false;
7467 const nsStyleText* textStyle = f->StyleText();
7468 if (textStyle->HasTextShadow()) {
7469 return false;
7472 nsTextFrame::TextDecorations decorations;
7473 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7474 decorations);
7475 return !decorations.HasDecorationLines();
7478 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
7479 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
7480 // We don't pass mVisibleRect here, since this can be called from within
7481 // the WebRender fallback painting path, and we don't want to issue
7482 // recorded commands that are dependent on the visible/building rect.
7483 RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
7486 bool nsDisplayText::CreateWebRenderCommands(
7487 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7488 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7489 nsDisplayListBuilder* aDisplayListBuilder) {
7490 auto* f = static_cast<nsTextFrame*>(mFrame);
7491 auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
7493 nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
7494 // Bug 748228
7495 bounds.Inflate(appUnitsPerDevPixel);
7497 if (bounds.IsEmpty()) {
7498 return true;
7501 // For large font sizes, punt to a blob image, to avoid the blurry rendering
7502 // that results from WR clamping the glyph size used for rasterization.
7504 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7506 // This is not strictly accurate, as final used font sizes might not be the
7507 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7508 // altering the used size of the font actually used).
7509 // It also fails to consider how transforms might affect the device-font-size
7510 // that webrender uses (and clamps).
7511 // But it should be near enough for practical purposes; the limitations just
7512 // mean we might sometimes end up with webrender still applying some bitmap
7513 // scaling, or bail out when we didn't really need to.
7514 constexpr float kWebRenderFontSizeLimit = 320.0;
7515 f->EnsureTextRun(nsTextFrame::eInflated);
7516 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7517 if (textRun &&
7518 textRun->GetFontGroup()->GetStyle()->size > kWebRenderFontSizeLimit) {
7519 return false;
7522 gfx::Point deviceOffset =
7523 LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
7524 .ToUnknownPoint();
7526 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7527 // frames) lets us early reject a bunch of things.
7528 nsRect visible = mVisibleRect;
7530 // Add the "source rect" area from which the given shadows could intersect
7531 // with mVisibleRect, and which therefore needs to included in the paint
7532 // operation, to the `visible` rect that we will use to limit the bounds of
7533 // what we send to the renderer.
7534 auto addShadowSourceToVisible = [&](Span<const StyleSimpleShadow> aShadows) {
7535 for (const auto& shadow : aShadows) {
7536 nsRect sourceRect = mVisibleRect;
7537 // Negate the offsets, because we're looking for the "source" rect that
7538 // could cast a shadow into the visible rect, rather than a "target" area
7539 // onto which the visible rect would cast a shadow.
7540 sourceRect.MoveBy(-shadow.horizontal.ToAppUnits(),
7541 -shadow.vertical.ToAppUnits());
7542 // Inflate to account for the shadow blur.
7543 sourceRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
7544 shadow.blur.ToAppUnits(), appUnitsPerDevPixel));
7545 visible.OrWith(sourceRect);
7549 // Shadows can translate things back into view, so we enlarge the notional
7550 // "visible" rect to ensure we don't skip painting relevant parts that might
7551 // cast a shadow within the visible area.
7552 addShadowSourceToVisible(f->StyleText()->mTextShadow.AsSpan());
7554 // Similarly for shadows that may be cast by ::selection.
7555 if (f->IsSelected()) {
7556 nsTextPaintStyle textPaint(f);
7557 Span<const StyleSimpleShadow> shadows;
7558 f->GetSelectionTextShadow(SelectionType::eNormal, textPaint, &shadows);
7559 addShadowSourceToVisible(shadows);
7562 // Inflate a little extra to allow for potential antialiasing "blur".
7563 visible.Inflate(3 * appUnitsPerDevPixel);
7564 bounds = bounds.Intersect(visible);
7566 gfxContext* textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager,
7567 this, bounds, deviceOffset);
7569 aBuilder.StartGroup(this);
7571 RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
7572 aBuilder.GetInheritedOpacity(), true);
7573 const bool result = textDrawer->GetTextDrawer()->Finish();
7575 if (result) {
7576 aBuilder.FinishGroup();
7577 } else {
7578 aBuilder.CancelGroup(true);
7581 return result;
7584 void nsDisplayText::RenderToContext(gfxContext* aCtx,
7585 nsDisplayListBuilder* aBuilder,
7586 const nsRect& aVisibleRect, float aOpacity,
7587 bool aIsRecording) {
7588 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7590 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7591 // antialiased pixels beyond the measured text extents.
7592 // This is temporary until we do this in the actual calculation of text
7593 // extents.
7594 auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
7595 LayoutDeviceRect extraVisible =
7596 LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D);
7597 extraVisible.Inflate(1);
7599 gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width,
7600 extraVisible.height);
7601 pixelVisible.Inflate(2);
7602 pixelVisible.RoundOut();
7604 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7605 if (!aBuilder->IsForGenerateGlyphMask() && !aIsRecording) {
7606 autoSaveClip.Clip(pixelVisible);
7609 NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
7610 NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
7612 gfxContextMatrixAutoSaveRestore matrixSR;
7614 nsPoint framePt = ToReferenceFrame();
7615 if (f->Style()->IsTextCombined()) {
7616 float scaleFactor = nsTextFrame::GetTextCombineScaleFactor(f);
7617 if (scaleFactor != 1.0f) {
7618 if (auto* textDrawer = aCtx->GetTextDrawer()) {
7619 // WebRender doesn't support scaling text like this yet
7620 textDrawer->FoundUnsupportedFeature();
7621 return;
7623 matrixSR.SetContext(aCtx);
7624 // Setup matrix to compress text for text-combine-upright if
7625 // necessary. This is done here because we want selection be
7626 // compressed at the same time as text.
7627 gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
7628 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7629 if (textRun && textRun->IsRightToLeft()) {
7630 pt.x += gfxFloat(f->GetSize().width) / A2D;
7632 gfxMatrix mat = aCtx->CurrentMatrixDouble()
7633 .PreTranslate(pt)
7634 .PreScale(scaleFactor, 1.0)
7635 .PreTranslate(-pt);
7636 aCtx->SetMatrixDouble(mat);
7639 nsTextFrame::PaintTextParams params(aCtx);
7640 params.framePt = gfx::Point(framePt.x, framePt.y);
7641 params.dirtyRect = extraVisible;
7643 if (aBuilder->IsForGenerateGlyphMask()) {
7644 params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
7645 } else {
7646 params.state = nsTextFrame::PaintTextParams::PaintText;
7649 f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
7650 f->IsSelected(), aOpacity);
7653 // This could go to nsDisplayListInvalidation.h, but
7654 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7655 // would produce circular dependencies.
7656 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
7657 public:
7658 nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
7659 : nsDisplayItemGenericGeometry(aItem, aBuilder),
7660 mVisIStartEdge(aItem->VisIStartEdge()),
7661 mVisIEndEdge(aItem->VisIEndEdge()) {
7662 nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
7663 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7664 mDecorations);
7668 * We store the computed text decorations here since they are
7669 * computed using style data from parent frames. Any changes to these
7670 * styles will only invalidate the parent frame and not this frame.
7672 nsTextFrame::TextDecorations mDecorations;
7673 nscoord mVisIStartEdge;
7674 nscoord mVisIEndEdge;
7677 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry(
7678 nsDisplayListBuilder* aBuilder) {
7679 return new nsDisplayTextGeometry(this, aBuilder);
7682 void nsDisplayText::ComputeInvalidationRegion(
7683 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7684 nsRegion* aInvalidRegion) const {
7685 const nsDisplayTextGeometry* geometry =
7686 static_cast<const nsDisplayTextGeometry*>(aGeometry);
7687 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7689 nsTextFrame::TextDecorations decorations;
7690 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7691 decorations);
7693 bool snap;
7694 const nsRect& newRect = geometry->mBounds;
7695 nsRect oldRect = GetBounds(aBuilder, &snap);
7696 if (decorations != geometry->mDecorations ||
7697 mVisIStartEdge != geometry->mVisIStartEdge ||
7698 mVisIEndEdge != geometry->mVisIEndEdge ||
7699 !oldRect.IsEqualInterior(newRect) ||
7700 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
7701 aInvalidRegion->Or(oldRect, newRect);
7705 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) {
7706 #ifdef DEBUG
7707 aStream << " (\"";
7709 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7710 nsCString buf;
7711 f->ToCString(buf);
7713 aStream << buf.get() << "\")";
7714 #endif
7717 nsDisplayEffectsBase::nsDisplayEffectsBase(
7718 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7719 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
7720 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
7721 aClearClipChain) {
7722 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7725 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
7726 nsIFrame* aFrame,
7727 nsDisplayList* aList)
7728 : nsDisplayWrapList(aBuilder, aFrame, aList) {
7729 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7732 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7733 bool* aSnap) const {
7734 *aSnap = false;
7735 return nsRegion();
7738 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder* aBuilder,
7739 const nsRect& aRect, HitTestState* aState,
7740 nsTArray<nsIFrame*>* aOutFrames) {
7741 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
7742 if (SVGIntegrationUtils::HitTestFrameForEffects(
7743 mFrame, rectCenter - ToReferenceFrame())) {
7744 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
7748 gfxRect nsDisplayEffectsBase::BBoxInUserSpace() const {
7749 return SVGUtils::GetBBox(mFrame);
7752 gfxPoint nsDisplayEffectsBase::UserSpaceOffset() const {
7753 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
7756 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7757 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7758 nsRegion* aInvalidRegion) const {
7759 const auto* geometry =
7760 static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
7761 bool snap;
7762 nsRect bounds = GetBounds(aBuilder, &snap);
7763 if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
7764 geometry->mUserSpaceOffset != UserSpaceOffset() ||
7765 !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
7766 // Filter and mask output can depend on the location of the frame's user
7767 // space and on the frame's BBox. We need to invalidate if either of these
7768 // change relative to the reference frame.
7769 // Invalidations from our inactive layer manager are not enough to catch
7770 // some of these cases because filters can produce output even if there's
7771 // nothing in the filter input.
7772 aInvalidRegion->Or(bounds, geometry->mBounds);
7776 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7777 if (mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
7778 ISVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame);
7779 if (!svgFrame) {
7780 return false;
7782 if (auto* svgElement = SVGElement::FromNode(mFrame->GetContent())) {
7783 // The SVG spec says only to draw filters if the element
7784 // has valid dimensions.
7785 return svgElement->HasValidDimensions();
7787 return false;
7790 return true;
7793 using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams;
7795 static void ComputeMaskGeometry(PaintFramesParams& aParams) {
7796 // Properties are added lazily and may have been removed by a restyle, so
7797 // make sure all applicable ones are set again.
7798 nsIFrame* firstFrame =
7799 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
7801 const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
7803 nsTArray<SVGMaskFrame*> maskFrames;
7804 // XXX check return value?
7805 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
7807 if (maskFrames.Length() == 0) {
7808 return;
7811 gfxContext& ctx = aParams.ctx;
7812 nsIFrame* frame = aParams.frame;
7814 nsPoint offsetToUserSpace =
7815 nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame);
7817 auto cssToDevScale = frame->PresContext()->CSSToDevPixelScale();
7818 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
7820 gfxPoint devPixelOffsetToUserSpace =
7821 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, appUnitsPerDevPixel);
7823 gfxContextMatrixAutoSaveRestore matSR(&ctx);
7824 ctx.SetMatrixDouble(
7825 ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace));
7827 // Convert boaderArea and dirtyRect to user space.
7828 nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
7829 nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
7831 // Union all mask layer rectangles in user space.
7832 LayoutDeviceRect maskInUserSpace;
7833 for (size_t i = 0; i < maskFrames.Length(); i++) {
7834 SVGMaskFrame* maskFrame = maskFrames[i];
7835 LayoutDeviceRect currentMaskSurfaceRect;
7837 if (maskFrame) {
7838 auto rect = maskFrame->GetMaskArea(aParams.frame);
7839 currentMaskSurfaceRect =
7840 CSSRect::FromUnknownRect(ToRect(rect)) * cssToDevScale;
7841 } else {
7842 nsCSSRendering::ImageLayerClipState clipState;
7843 nsCSSRendering::GetImageLayerClip(
7844 svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(),
7845 userSpaceBorderArea, userSpaceDirtyRect,
7846 /* aWillPaintBorder = */ false, appUnitsPerDevPixel, &clipState);
7847 currentMaskSurfaceRect = LayoutDeviceRect::FromUnknownRect(
7848 ToRect(clipState.mDirtyRectInDevPx));
7851 maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
7854 if (!maskInUserSpace.IsEmpty()) {
7855 aParams.maskRect = Some(maskInUserSpace);
7856 } else {
7857 aParams.maskRect = Nothing();
7861 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7862 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7863 const ActiveScrolledRoot* aActiveScrolledRoot, bool aWrapsBackdropFilter)
7864 : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
7865 mWrapsBackdropFilter(aWrapsBackdropFilter) {
7866 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
7868 nsPresContext* presContext = mFrame->PresContext();
7869 uint32_t flags =
7870 aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
7871 const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
7872 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7873 const auto& layer = svgReset->mMask.mLayers[i];
7874 if (!layer.mImage.IsResolved()) {
7875 continue;
7877 const nsRect& borderArea = mFrame->GetRectRelativeToSelf();
7878 // NOTE(emilio): We only care about the dest rect so we don't bother
7879 // computing a clip.
7880 bool isTransformedFixed = false;
7881 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
7882 presContext, aFrame, flags, borderArea, borderArea, layer,
7883 &isTransformedFixed);
7884 mDestRects.AppendElement(state.mDestArea);
7888 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) {
7889 // Do not merge items for box-decoration-break:clone elements,
7890 // since each box should have its own mask in that case.
7891 if (aFrame->StyleBorder()->mBoxDecorationBreak ==
7892 StyleBoxDecorationBreak::Clone) {
7893 return false;
7896 // Do not merge if either frame has a mask. Continuation frames should apply
7897 // the mask independently (just like nsDisplayBackgroundImage).
7898 if (aFrame->StyleSVGReset()->HasMask()) {
7899 return false;
7902 return true;
7905 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem* aItem) const {
7906 // Items for the same content element should be merged into a single
7907 // compositing group.
7908 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
7909 !HasSameContent(aItem)) {
7910 return false;
7913 return CanMergeDisplayMaskFrame(mFrame) &&
7914 CanMergeDisplayMaskFrame(aItem->Frame());
7917 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7918 if (!ValidateSVGFrame()) {
7919 return false;
7922 nsIFrame* firstFrame =
7923 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
7925 return !(SVGObserverUtils::GetAndObserveClipPath(firstFrame, nullptr) ==
7926 SVGObserverUtils::eHasRefsSomeInvalid ||
7927 SVGObserverUtils::GetAndObserveMasks(firstFrame, nullptr) ==
7928 SVGObserverUtils::eHasRefsSomeInvalid);
7931 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder,
7932 gfxContext* aMaskContext,
7933 bool aHandleOpacity,
7934 bool* aMaskPainted) {
7935 MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
7937 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7938 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7939 PaintFramesParams params(*aMaskContext, mFrame, mBounds, borderArea, aBuilder,
7940 aHandleOpacity, imgParams);
7941 ComputeMaskGeometry(params);
7942 bool maskIsComplete = false;
7943 bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete);
7944 if (aMaskPainted) {
7945 *aMaskPainted = painted;
7948 return maskIsComplete &&
7949 (imgParams.result == ImgDrawResult::SUCCESS ||
7950 imgParams.result == ImgDrawResult::SUCCESS_NOT_COMPLETE ||
7951 imgParams.result == ImgDrawResult::WRONG_SIZE);
7954 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7955 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7956 nsRegion* aInvalidRegion) const {
7957 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
7958 aInvalidRegion);
7960 const auto* geometry =
7961 static_cast<const nsDisplayMasksAndClipPathsGeometry*>(aGeometry);
7962 bool snap;
7963 nsRect bounds = GetBounds(aBuilder, &snap);
7965 if (mDestRects.Length() != geometry->mDestRects.Length()) {
7966 aInvalidRegion->Or(bounds, geometry->mBounds);
7967 } else {
7968 for (size_t i = 0; i < mDestRects.Length(); i++) {
7969 if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) {
7970 aInvalidRegion->Or(bounds, geometry->mBounds);
7971 break;
7977 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7978 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
7979 const std::function<void()>& aPaintChildren) {
7980 // Clip the drawing target by mVisibleRect, which contains the visible
7981 // region of the target frame and its out-of-flow and inflow descendants.
7982 Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx),
7983 mFrame->PresContext()->AppUnitsPerDevPixel());
7984 bounds.RoundOut();
7985 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7986 autoSaveClip.Clip(bounds);
7988 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7989 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7990 PaintFramesParams params(*aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
7991 borderArea, aBuilder, false, imgParams);
7993 ComputeMaskGeometry(params);
7995 SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren);
7998 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder* aBuilder,
7999 gfxContext* aCtx) {
8000 if (!IsValidMask()) {
8001 return;
8003 PaintWithContentsPaintCallback(aBuilder, aCtx, [&] {
8004 GetChildren()->Paint(aBuilder, aCtx,
8005 mFrame->PresContext()->AppUnitsPerDevPixel());
8009 static Maybe<wr::WrClipChainId> CreateSimpleClipRegion(
8010 const nsDisplayMasksAndClipPaths& aDisplayItem,
8011 wr::DisplayListBuilder& aBuilder) {
8012 nsIFrame* frame = aDisplayItem.Frame();
8013 const auto* style = frame->StyleSVGReset();
8014 MOZ_ASSERT(style->HasClipPath() || style->HasMask());
8015 if (!SVGIntegrationUtils::UsingSimpleClipPathForFrame(frame)) {
8016 return Nothing();
8019 const auto& clipPath = style->mClipPath;
8020 const auto& shape = *clipPath.AsShape()._0;
8022 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8023 const nsRect refBox =
8024 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8026 wr::WrClipId clipId{};
8028 switch (shape.tag) {
8029 case StyleBasicShape::Tag::Rect: {
8030 const nsRect rect =
8031 ShapeUtils::ComputeInsetRect(shape.AsRect().rect, refBox) +
8032 aDisplayItem.ToReferenceFrame();
8034 nscoord radii[8] = {0};
8035 if (ShapeUtils::ComputeRectRadii(shape.AsRect().round, refBox, rect,
8036 radii)) {
8037 clipId = aBuilder.DefineRoundedRectClip(
8038 Nothing(),
8039 wr::ToComplexClipRegion(rect, radii, appUnitsPerDevPixel));
8040 } else {
8041 clipId = aBuilder.DefineRectClip(
8042 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8043 rect, appUnitsPerDevPixel)));
8046 break;
8048 case StyleBasicShape::Tag::Ellipse:
8049 case StyleBasicShape::Tag::Circle: {
8050 nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox);
8052 nsSize radii;
8053 if (shape.IsEllipse()) {
8054 radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox);
8055 } else {
8056 nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox);
8057 radii = {radius, radius};
8060 nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center -
8061 nsPoint(radii.width, radii.height),
8062 radii * 2);
8064 nscoord ellipseRadii[8];
8065 for (const auto corner : AllPhysicalHalfCorners()) {
8066 ellipseRadii[corner] =
8067 HalfCornerIsX(corner) ? radii.width : radii.height;
8070 clipId = aBuilder.DefineRoundedRectClip(
8071 Nothing(), wr::ToComplexClipRegion(ellipseRect, ellipseRadii,
8072 appUnitsPerDevPixel));
8074 break;
8076 default:
8077 // Please don't add more exceptions, try to find a way to define the clip
8078 // without using a mask image.
8080 // And if you _really really_ need to add an exception, add it to
8081 // SVGIntegrationUtils::UsingSimpleClipPathForFrame
8082 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8083 return Nothing();
8086 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8088 return Some(clipChainId);
8091 static void FillPolygonDataForDisplayItem(
8092 const nsDisplayMasksAndClipPaths& aDisplayItem,
8093 nsTArray<wr::LayoutPoint>& aPoints, wr::FillRule& aFillRule) {
8094 nsIFrame* frame = aDisplayItem.Frame();
8095 const auto* style = frame->StyleSVGReset();
8096 bool isPolygon = style->HasClipPath() && style->mClipPath.IsShape() &&
8097 style->mClipPath.AsShape()._0->IsPolygon();
8098 if (!isPolygon) {
8099 return;
8102 const auto& clipPath = style->mClipPath;
8103 const auto& shape = *clipPath.AsShape()._0;
8104 const nsRect refBox =
8105 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8107 // We only fill polygon data for polygons that are below a complexity
8108 // limit.
8109 nsTArray<nsPoint> vertices =
8110 ShapeUtils::ComputePolygonVertices(shape, refBox);
8111 if (vertices.Length() > wr::POLYGON_CLIP_VERTEX_MAX) {
8112 return;
8115 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8117 for (size_t i = 0; i < vertices.Length(); ++i) {
8118 wr::LayoutPoint point = wr::ToLayoutPoint(
8119 LayoutDevicePoint::FromAppUnits(vertices[i], appUnitsPerDevPixel));
8120 aPoints.AppendElement(point);
8123 aFillRule = (shape.AsPolygon().fill == StyleFillRule::Nonzero)
8124 ? wr::FillRule::Nonzero
8125 : wr::FillRule::Evenodd;
8128 static Maybe<wr::WrClipChainId> CreateWRClipPathAndMasks(
8129 nsDisplayMasksAndClipPaths* aDisplayItem, const LayoutDeviceRect& aBounds,
8130 wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder,
8131 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8132 nsDisplayListBuilder* aDisplayListBuilder) {
8133 if (auto clip = CreateSimpleClipRegion(*aDisplayItem, aBuilder)) {
8134 return clip;
8137 Maybe<wr::ImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(
8138 aDisplayItem, aBuilder, aResources, aSc, aDisplayListBuilder, aBounds);
8139 if (!mask) {
8140 return Nothing();
8143 // We couldn't create a simple clip region, but before we create an image
8144 // mask clip, see if we can get a polygon clip to add to it.
8145 nsTArray<wr::LayoutPoint> points;
8146 wr::FillRule fillRule = wr::FillRule::Nonzero;
8147 FillPolygonDataForDisplayItem(*aDisplayItem, points, fillRule);
8149 wr::WrClipId clipId =
8150 aBuilder.DefineImageMaskClip(mask.ref(), points, fillRule);
8152 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8154 return Some(clipChainId);
8157 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8158 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8159 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8160 nsDisplayListBuilder* aDisplayListBuilder) {
8161 bool snap;
8162 auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8163 nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
8164 LayoutDeviceRect bounds =
8165 LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel);
8167 Maybe<wr::WrClipChainId> clip = CreateWRClipPathAndMasks(
8168 this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder);
8170 float oldOpacity = aBuilder.GetInheritedOpacity();
8172 Maybe<StackingContextHelper> layer;
8173 const StackingContextHelper* sc = &aSc;
8174 if (clip) {
8175 // Create a new stacking context to attach the mask to, ensuring the mask is
8176 // applied to the aggregate, and not the individual elements.
8178 // The stacking context shouldn't have any offset.
8179 bounds.MoveTo(0, 0);
8181 Maybe<float> opacity =
8182 (SVGIntegrationUtils::UsingSimpleClipPathForFrame(mFrame) &&
8183 aBuilder.GetInheritedOpacity() != 1.0f)
8184 ? Some(aBuilder.GetInheritedOpacity())
8185 : Nothing();
8187 wr::StackingContextParams params;
8188 params.clip = wr::WrStackingContextClip::ClipChain(clip->id);
8189 params.opacity = opacity.ptrOr(nullptr);
8190 if (mWrapsBackdropFilter) {
8191 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8193 layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params,
8194 bounds);
8195 sc = layer.ptr();
8198 aBuilder.SetInheritedOpacity(1.0f);
8199 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8200 aBuilder.SetInheritedClipChain(nullptr);
8201 CreateWebRenderCommandsNewClipListOption(aBuilder, aResources, *sc, aManager,
8202 aDisplayListBuilder, layer.isSome());
8203 aBuilder.SetInheritedOpacity(oldOpacity);
8204 aBuilder.SetInheritedClipChain(oldClipChain);
8206 return true;
8209 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8210 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
8211 if (const DisplayItemClip* clip =
8212 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
8213 return Some(clip->GetClipRect());
8215 // This item does not have a clip with respect to |aASR|. However, we
8216 // might still have finite bounds with respect to |aASR|. Check our
8217 // children.
8218 nsDisplayList* childList = GetSameCoordinateSystemChildren();
8219 if (childList) {
8220 return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR));
8222 #ifdef DEBUG
8223 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
8224 #endif
8225 return Nothing();
8228 #ifdef MOZ_DUMP_PAINTING
8229 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) {
8230 nsIFrame* firstFrame =
8231 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8232 bool first = true;
8233 aTo += " effects=(";
8234 SVGClipPathFrame* clipPathFrame;
8235 // XXX Check return value?
8236 SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
8237 if (clipPathFrame) {
8238 if (!first) {
8239 aTo += ", ";
8241 aTo += nsPrintfCString(
8242 "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
8243 first = false;
8244 } else if (mFrame->StyleSVGReset()->HasClipPath()) {
8245 if (!first) {
8246 aTo += ", ";
8248 aTo += "clip(basic-shape)";
8249 first = false;
8252 nsTArray<SVGMaskFrame*> masks;
8253 // XXX check return value?
8254 SVGObserverUtils::GetAndObserveMasks(firstFrame, &masks);
8255 if (!masks.IsEmpty() && masks[0]) {
8256 if (!first) {
8257 aTo += ", ";
8259 aTo += "mask";
8261 aTo += ")";
8263 #endif
8265 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8266 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8267 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8268 nsDisplayListBuilder* aDisplayListBuilder) {
8269 WrFiltersHolder wrFilters;
8270 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8271 auto filterChain = style.StyleEffects()->mBackdropFilters.AsSpan();
8272 bool initialized = true;
8273 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8274 wrFilters) &&
8275 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame, filterChain,
8276 wrFilters, initialized)) {
8277 // TODO: If painting backdrop-filters on the content side is implemented,
8278 // consider returning false to fall back to that.
8279 wrFilters = {};
8282 if (!initialized) {
8283 wrFilters = {};
8286 nsCSSRendering::ImageLayerClipState clip;
8287 nsCSSRendering::GetImageLayerClip(
8288 style.StyleBackground()->BottomLayer(), mFrame, *style.StyleBorder(),
8289 mBackdropRect, mBackdropRect, false,
8290 mFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
8292 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
8293 mBackdropRect, mFrame->PresContext()->AppUnitsPerDevPixel());
8295 wr::ComplexClipRegion region =
8296 wr::ToComplexClipRegion(clip.mBGClipArea, clip.mRadii,
8297 mFrame->PresContext()->AppUnitsPerDevPixel());
8299 aBuilder.PushBackdropFilter(wr::ToLayoutRect(bounds), region,
8300 wrFilters.filters, wrFilters.filter_datas,
8301 !BackfaceIsHidden());
8303 wr::StackingContextParams params;
8304 params.clip =
8305 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8306 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8307 params);
8309 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8310 aDisplayListBuilder);
8311 return true;
8314 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder* aBuilder,
8315 gfxContext* aCtx) {
8316 // TODO: Implement backdrop filters
8317 GetChildren()->Paint(aBuilder, aCtx,
8318 mFrame->PresContext()->AppUnitsPerDevPixel());
8321 nsRect nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder* aBuilder,
8322 bool* aSnap) const {
8323 nsRect childBounds = nsDisplayWrapList::GetBounds(aBuilder, aSnap);
8325 *aSnap = false;
8327 return mBackdropRect.Union(childBounds);
8330 /* static */
8331 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder,
8332 nsIFrame* aFrame, nsDisplayList* aList,
8333 nsIFrame* aStyleFrame,
8334 bool aWrapsBackdropFilter)
8335 : nsDisplayEffectsBase(aBuilder, aFrame, aList),
8336 mStyle(aFrame == aStyleFrame ? nullptr : aStyleFrame->Style()),
8337 mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()),
8338 mWrapsBackdropFilter(aWrapsBackdropFilter) {
8339 MOZ_COUNT_CTOR(nsDisplayFilters);
8340 mVisibleRect = aBuilder->GetVisibleRect() +
8341 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
8344 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8345 PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) {
8346 GetChildren()->Paint(aBuilder, aContext,
8347 mFrame->PresContext()->AppUnitsPerDevPixel());
8351 void nsDisplayFilters::PaintWithContentsPaintCallback(
8352 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
8353 const std::function<void(gfxContext* aContext)>& aPaintChildren) {
8354 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
8355 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8356 PaintFramesParams params(*aCtx, mFrame, mVisibleRect, borderArea, aBuilder,
8357 false, imgParams);
8359 gfxPoint userSpaceToFrameSpaceOffset =
8360 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params);
8362 auto filterChain = mStyle ? mStyle->StyleEffects()->mFilters.AsSpan()
8363 : mFrame->StyleEffects()->mFilters.AsSpan();
8364 SVGIntegrationUtils::PaintFilter(
8365 params, filterChain,
8366 [&](gfxContext& aContext, imgDrawingParams&, const gfxMatrix*,
8367 const nsIntRect*) {
8368 gfxContextMatrixAutoSaveRestore autoSR(&aContext);
8369 aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(
8370 -userSpaceToFrameSpaceOffset));
8371 aPaintChildren(&aContext);
8375 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8376 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame);
8379 bool nsDisplayFilters::CreateWebRenderCommands(
8380 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8381 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8382 nsDisplayListBuilder* aDisplayListBuilder) {
8383 WrFiltersHolder wrFilters;
8384 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8385 auto filterChain = style.StyleEffects()->mFilters.AsSpan();
8386 bool initialized = true;
8387 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8388 wrFilters) &&
8389 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame, filterChain,
8390 wrFilters, initialized)) {
8391 if (mStyle) {
8392 // TODO(bug 1769223): Support fallback filters in the root code-path,
8393 // perhaps. For now treat it the same way as invalid filters.
8394 wrFilters = {};
8395 } else {
8396 // Draw using fallback.
8397 return false;
8401 if (!initialized) {
8402 // https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
8404 // If the filter references a non-existent object or the referenced object
8405 // is not a filter element, then the whole filter chain is ignored. No
8406 // filter is applied to the object.
8408 // Note that other engines have a weird discrepancy between SVG and HTML
8409 // content here, but the spec is clear.
8410 wrFilters = {};
8413 uint64_t clipChainId;
8414 if (wrFilters.post_filters_clip) {
8415 auto devPxRect = LayoutDeviceRect::FromAppUnits(
8416 wrFilters.post_filters_clip.value() + ToReferenceFrame(),
8417 mFrame->PresContext()->AppUnitsPerDevPixel());
8418 auto clipId =
8419 aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect));
8420 clipChainId = aBuilder.DefineClipChain({clipId}, true).id;
8421 } else {
8422 clipChainId = aBuilder.CurrentClipChainId();
8424 wr::WrStackingContextClip clip =
8425 wr::WrStackingContextClip::ClipChain(clipChainId);
8427 float opacity = aBuilder.GetInheritedOpacity();
8428 aBuilder.SetInheritedOpacity(1.0f);
8429 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8430 aBuilder.SetInheritedClipChain(nullptr);
8431 wr::StackingContextParams params;
8432 params.mFilters = std::move(wrFilters.filters);
8433 params.mFilterDatas = std::move(wrFilters.filter_datas);
8434 params.opacity = opacity != 1.0f ? &opacity : nullptr;
8435 params.clip = clip;
8436 if (mWrapsBackdropFilter) {
8437 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8439 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8440 params);
8442 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
8443 aManager, aDisplayListBuilder);
8444 aBuilder.SetInheritedOpacity(opacity);
8445 aBuilder.SetInheritedClipChain(oldClipChain);
8447 return true;
8450 #ifdef MOZ_DUMP_PAINTING
8451 void nsDisplayFilters::PrintEffects(nsACString& aTo) {
8452 nsIFrame* firstFrame =
8453 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8454 bool first = true;
8455 aTo += " effects=(";
8456 // We may exist for a mix of CSS filter functions and/or references to SVG
8457 // filters. If we have invalid references to SVG filters then we paint
8458 // nothing, but otherwise we will apply one or more filters.
8459 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) !=
8460 SVGObserverUtils::eHasRefsSomeInvalid) {
8461 if (!first) {
8462 aTo += ", ";
8464 aTo += "filter";
8466 aTo += ")";
8468 #endif
8470 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder,
8471 nsIFrame* aFrame, nsDisplayList* aList)
8472 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8473 MOZ_COUNT_CTOR(nsDisplaySVGWrapper);
8476 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8477 return !aBuilder->GetWidgetLayerManager();
8480 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8481 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8482 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8483 nsDisplayListBuilder* aDisplayListBuilder) {
8484 return CreateWebRenderCommandsNewClipListOption(
8485 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8488 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder* aBuilder,
8489 nsIFrame* aFrame,
8490 nsDisplayList* aList)
8491 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8492 MOZ_COUNT_CTOR(nsDisplayForeignObject);
8495 #ifdef NS_BUILD_REFCNT_LOGGING
8496 nsDisplayForeignObject::~nsDisplayForeignObject() {
8497 MOZ_COUNT_DTOR(nsDisplayForeignObject);
8499 #endif
8501 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8502 return !aBuilder->GetWidgetLayerManager();
8505 bool nsDisplayForeignObject::CreateWebRenderCommands(
8506 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8507 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8508 nsDisplayListBuilder* aDisplayListBuilder) {
8509 AutoRestore<bool> restoreDoGrouping(aManager->CommandBuilder().mDoGrouping);
8510 aManager->CommandBuilder().mDoGrouping = false;
8511 return CreateWebRenderCommandsNewClipListOption(
8512 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8515 void nsDisplayLink::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8516 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8517 aCtx->GetDrawTarget()->Link(
8518 mLinkSpec.get(), NSRectToRect(GetPaintRect(aBuilder, aCtx), appPerDev));
8521 void nsDisplayDestination::Paint(nsDisplayListBuilder* aBuilder,
8522 gfxContext* aCtx) {
8523 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8524 aCtx->GetDrawTarget()->Destination(
8525 mDestinationName.get(),
8526 NSPointToPoint(GetPaintRect(aBuilder, aCtx).TopLeft(), appPerDev));
8529 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8530 nsDisplayList* aOutResultList, nsIContent* aContent) {
8531 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
8532 // in content document order and SortByZOrder is a stable sort which
8533 // guarantees that boxes produced by the same element are placed together
8534 // in the sort. Consider a position:relative inline element that breaks
8535 // across lines and has absolutely positioned children; all the abs-pos
8536 // children should be z-ordered after all the boxes for the position:relative
8537 // element itself.
8538 PositionedDescendants()->SortByZOrder();
8540 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8541 // 1,2: backgrounds and borders
8542 aOutResultList->AppendToTop(BorderBackground());
8543 // 3: negative z-index children.
8544 for (auto* item : PositionedDescendants()->TakeItems()) {
8545 if (item->ZIndex() < 0) {
8546 aOutResultList->AppendToTop(item);
8547 } else {
8548 PositionedDescendants()->AppendToTop(item);
8552 // 4: block backgrounds
8553 aOutResultList->AppendToTop(BlockBorderBackgrounds());
8554 // 5: floats
8555 aOutResultList->AppendToTop(Floats());
8556 // 7: general content
8557 aOutResultList->AppendToTop(Content());
8558 // 7.5: outlines, in content tree order. We need to sort by content order
8559 // because an element with outline that breaks and has children with outline
8560 // might have placed child outline items between its own outline items.
8561 // The element's outline items need to all come before any child outline
8562 // items.
8563 if (aContent) {
8564 Outlines()->SortByContentOrder(aContent);
8566 aOutResultList->AppendToTop(Outlines());
8567 // 8, 9: non-negative z-index children
8568 aOutResultList->AppendToTop(PositionedDescendants());
8571 uint32_t PaintTelemetry::sPaintLevel = 0;
8573 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8574 // Don't record nested paints.
8575 if (sPaintLevel++ > 0) {
8576 return;
8579 mStart = TimeStamp::Now();
8582 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8583 MOZ_ASSERT(sPaintLevel != 0);
8584 if (--sPaintLevel > 0) {
8585 return;
8588 // If we're in multi-process mode, don't include paint times for the parent
8589 // process.
8590 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8591 return;
8594 double totalMs = (TimeStamp::Now() - mStart).ToMilliseconds();
8596 // Record the total time.
8597 Telemetry::Accumulate(Telemetry::CONTENT_PAINT_TIME,
8598 static_cast<uint32_t>(totalMs));
8601 static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
8602 if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
8603 return aFrame;
8606 if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
8607 !aFrame->GetPrevInFlow()) {
8608 return aFrame->GetPlaceholderFrame();
8611 return aFrame;
8614 static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
8615 nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
8616 MOZ_ASSERT(f);
8617 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
8620 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8621 nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
8622 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
8623 const bool aIsTransformed)
8624 : mBuilder(aBuilder),
8625 mPrevFrame(aBuilder->mCurrentFrame),
8626 mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
8627 mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
8628 mPrevAdditionalOffset(aBuilder->mAdditionalOffset),
8629 mPrevVisibleRect(aBuilder->mVisibleRect),
8630 mPrevDirtyRect(aBuilder->mDirtyRect),
8631 mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo),
8632 mPrevAncestorHasApzAwareEventHandler(
8633 aBuilder->mAncestorHasApzAwareEventHandler),
8634 mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
8635 mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
8636 if (aIsTransformed) {
8637 aBuilder->mCurrentOffsetToReferenceFrame =
8638 aBuilder->AdditionalOffset().refOr(nsPoint());
8639 aBuilder->mCurrentReferenceFrame = aForChild;
8640 } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
8641 aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
8642 } else {
8643 aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
8644 aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
8647 // If aForChild is being visited from a frame other than it's ancestor frame,
8648 // mInInvalidSubtree will need to be recalculated the slow way.
8649 if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) {
8650 aBuilder->mInInvalidSubtree =
8651 aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
8652 } else {
8653 aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
8656 aBuilder->mCurrentFrame = aForChild;
8657 aBuilder->mVisibleRect = aVisibleRect;
8658 aBuilder->mDirtyRect =
8659 aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
8662 } // namespace mozilla