no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / layout / painting / nsDisplayList.cpp
blob46f8b05f8227bace934ccf6fc467476d33f824b5
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/dom/PerformanceMainThread.h"
32 #include "mozilla/gfx/2D.h"
33 #include "mozilla/PresShell.h"
34 #include "mozilla/ShapeUtils.h"
35 #include "mozilla/StaticPrefs_apz.h"
36 #include "mozilla/StaticPrefs_gfx.h"
37 #include "mozilla/StaticPrefs_layers.h"
38 #include "mozilla/StaticPrefs_layout.h"
39 #include "mozilla/StaticPrefs_print.h"
40 #include "mozilla/SVGIntegrationUtils.h"
41 #include "mozilla/SVGUtils.h"
42 #include "mozilla/ViewportUtils.h"
43 #include "nsCSSRendering.h"
44 #include "nsCSSRenderingGradients.h"
45 #include "nsCaseTreatment.h"
46 #include "nsRefreshDriver.h"
47 #include "nsRegion.h"
48 #include "nsStyleStructInlines.h"
49 #include "nsStyleTransformMatrix.h"
50 #include "nsTransitionManager.h"
51 #include "gfxMatrix.h"
52 #include "nsLayoutUtils.h"
53 #include "nsIScrollableFrame.h"
54 #include "nsIFrameInlines.h"
55 #include "nsStyleConsts.h"
56 #include "BorderConsts.h"
57 #include "mozilla/MathAlgorithms.h"
59 #include "imgIContainer.h"
60 #include "nsImageFrame.h"
61 #include "nsSubDocumentFrame.h"
62 #include "nsViewManager.h"
63 #include "ImageContainer.h"
64 #include "nsCanvasFrame.h"
65 #include "nsSubDocumentFrame.h"
66 #include "StickyScrollContainer.h"
67 #include "mozilla/AnimationPerformanceWarning.h"
68 #include "mozilla/AnimationUtils.h"
69 #include "mozilla/AutoRestore.h"
70 #include "mozilla/EffectCompositor.h"
71 #include "mozilla/EffectSet.h"
72 #include "mozilla/glean/GleanMetrics.h"
73 #include "mozilla/HashTable.h"
74 #include "mozilla/LookAndFeel.h"
75 #include "mozilla/OperatorNewExtensions.h"
76 #include "mozilla/Preferences.h"
77 #include "mozilla/ProfilerLabels.h"
78 #include "mozilla/ProfilerMarkers.h"
79 #include "mozilla/StyleAnimationValue.h"
80 #include "mozilla/ServoBindings.h"
81 #include "mozilla/SVGClipPathFrame.h"
82 #include "mozilla/SVGMaskFrame.h"
83 #include "mozilla/SVGObserverUtils.h"
84 #include "mozilla/Telemetry.h"
85 #include "mozilla/UniquePtr.h"
86 #include "mozilla/Unused.h"
87 #include "mozilla/ViewportFrame.h"
88 #include "mozilla/gfx/gfxVars.h"
89 #include "ActiveLayerTracker.h"
90 #include "nsEscape.h"
91 #include "nsPrintfCString.h"
92 #include "UnitTransforms.h"
93 #include "LayerAnimationInfo.h"
94 #include "mozilla/EventStateManager.h"
95 #include "nsCaret.h"
96 #include "nsDOMTokenList.h"
97 #include "nsCSSProps.h"
98 #include "nsTableCellFrame.h"
99 #include "nsTableColFrame.h"
100 #include "nsTextFrame.h"
101 #include "nsTextPaintStyle.h"
102 #include "nsSliderFrame.h"
103 #include "nsFocusManager.h"
104 #include "TextDrawTarget.h"
105 #include "mozilla/layers/AnimationHelper.h"
106 #include "mozilla/layers/CompositorThread.h"
107 #include "mozilla/layers/InputAPZContext.h"
108 #include "mozilla/layers/RenderRootStateManager.h"
109 #include "mozilla/layers/StackingContextHelper.h"
110 #include "mozilla/layers/TreeTraversal.h"
111 #include "mozilla/layers/WebRenderBridgeChild.h"
112 #include "mozilla/layers/WebRenderLayerManager.h"
113 #include "mozilla/layers/WebRenderMessages.h"
114 #include "mozilla/layers/WebRenderScrollData.h"
116 namespace mozilla {
118 using namespace dom;
119 using namespace gfx;
120 using namespace layout;
121 using namespace layers;
122 using namespace image;
124 LazyLogModule sContentDisplayListLog("dl.content");
125 LazyLogModule sParentDisplayListLog("dl.parent");
127 LazyLogModule& GetLoggerByProcess() {
128 return XRE_IsContentProcess() ? sContentDisplayListLog
129 : sParentDisplayListLog;
132 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
133 void AssertUniqueItem(nsDisplayItem* aItem) {
134 for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) {
135 if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
136 i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
137 if (i->IsPreProcessedItem() || i->IsPreProcessed()) {
138 continue;
140 MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
144 #endif
146 bool ShouldBuildItemForEvents(const DisplayItemType aType) {
147 return aType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO ||
148 (GetDisplayItemFlagsForType(aType) & TYPE_IS_CONTAINER);
151 static bool ItemTypeSupportsHitTesting(const DisplayItemType aType) {
152 switch (aType) {
153 case DisplayItemType::TYPE_BACKGROUND:
154 case DisplayItemType::TYPE_BACKGROUND_COLOR:
155 case DisplayItemType::TYPE_THEMED_BACKGROUND:
156 return true;
157 default:
158 return false;
162 void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder,
163 nsPaintedDisplayItem* aItem,
164 const DisplayItemType aType) {
165 if (ItemTypeSupportsHitTesting(aType)) {
166 aItem->InitializeHitTestInfo(aBuilder);
170 /* static */
171 already_AddRefed<ActiveScrolledRoot> ActiveScrolledRoot::CreateASRForFrame(
172 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame,
173 bool aIsRetained) {
174 nsIFrame* f = do_QueryFrame(aScrollableFrame);
176 RefPtr<ActiveScrolledRoot> asr;
177 if (aIsRetained) {
178 asr = f->GetProperty(ActiveScrolledRootCache());
181 if (!asr) {
182 asr = new ActiveScrolledRoot();
184 if (aIsRetained) {
185 RefPtr<ActiveScrolledRoot> ref = asr;
186 f->SetProperty(ActiveScrolledRootCache(), ref.forget().take());
189 asr->mParent = aParent;
190 asr->mScrollableFrame = aScrollableFrame;
191 asr->mDepth = aParent ? aParent->mDepth + 1 : 1;
192 asr->mRetained = aIsRetained;
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 nsIFrame* frame = aItem->Frame();
272 animationInfo.AddAnimationsForDisplayItem(
273 frame, aDisplayListBuilder, aItem, aItem->GetType(),
274 aManager->LayerManager(), aPosition);
276 // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
277 // are no active animations.
278 uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
279 if (!animationInfo.GetAnimations().IsEmpty()) {
280 OpAddCompositorAnimations anim(
281 CompositorAnimations(animationInfo.GetAnimations(), animationsId));
282 aManager->WrBridge()->AddWebRenderParentCommand(anim);
283 aManager->AddActiveCompositorAnimationId(animationsId);
284 } else if (animationsId) {
285 aManager->AddCompositorAnimationsIdForDiscard(animationsId);
286 animationsId = 0;
289 return animationsId;
292 static bool GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
293 const nsRect& aFillRect,
294 nsDisplayListBuilder* aBuilder) {
295 if (aBuilder->IsForGenerateGlyphMask()) {
296 return false;
299 SVGObserverUtils::GetAndObserveBackgroundClip(aFrame);
301 // The main function of enabling background-clip:text property value.
302 // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
303 // this function to
304 // 1. Generate a mask by all descendant text frames
305 // 2. Push the generated mask into aContext.
307 gfxContext* sourceCtx = aContext;
308 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
309 aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel());
311 // Create a mask surface.
312 RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget();
313 RefPtr<DrawTarget> maskDT = sourceTarget->CreateClippedDrawTarget(
314 bounds.ToUnknownRect(), SurfaceFormat::A8);
315 if (!maskDT || !maskDT->IsValid()) {
316 return false;
318 gfxContext maskCtx(maskDT, /* aPreserveTransform */ true);
319 maskCtx.Multiply(Matrix::Translation(bounds.TopLeft().ToUnknownPoint()));
321 // Shade text shape into mask A8 surface.
322 nsLayoutUtils::PaintFrame(
323 &maskCtx, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()),
324 NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph);
326 // Push the generated mask into aContext, so that the caller can pop and
327 // blend with it.
329 Matrix currentMatrix = sourceCtx->CurrentMatrix();
330 Matrix invCurrentMatrix = currentMatrix;
331 invCurrentMatrix.Invert();
333 RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
334 sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0,
335 maskSurface, invCurrentMatrix);
337 return true;
340 nsDisplayWrapper* nsDisplayWrapList::CreateShallowCopy(
341 nsDisplayListBuilder* aBuilder) {
342 const nsDisplayWrapList* wrappedItem = AsDisplayWrapList();
343 MOZ_ASSERT(wrappedItem);
345 // Create a new nsDisplayWrapList using a copy-constructor. This is done
346 // to preserve the information about bounds.
347 nsDisplayWrapper* wrapper =
348 new (aBuilder) nsDisplayWrapper(aBuilder, *wrappedItem);
349 wrapper->SetType(nsDisplayWrapper::ItemType());
350 MOZ_ASSERT(wrapper);
352 // Set the display list pointer of the new wrapper item to the display list
353 // of the wrapped item.
354 wrapper->mListPtr = wrappedItem->mListPtr;
355 return wrapper;
358 nsDisplayWrapList* nsDisplayListBuilder::MergeItems(
359 nsTArray<nsDisplayItem*>& aItems) {
360 // For merging, we create a temporary item by cloning the last item of the
361 // mergeable items list. This ensures that the temporary item will have the
362 // correct frame and bounds.
363 nsDisplayWrapList* last = aItems.PopLastElement()->AsDisplayWrapList();
364 MOZ_ASSERT(last);
365 nsDisplayWrapList* merged = last->Clone(this);
366 MOZ_ASSERT(merged);
367 AddTemporaryItem(merged);
369 // Create nsDisplayWrappers that point to the internal display lists of the
370 // items we are merging. These nsDisplayWrappers are added to the display list
371 // of the temporary item.
372 for (nsDisplayItem* item : aItems) {
373 MOZ_ASSERT(item);
374 MOZ_ASSERT(merged->CanMerge(item));
375 merged->Merge(item);
376 MOZ_ASSERT(item->AsDisplayWrapList());
377 merged->GetChildren()->AppendToTop(
378 static_cast<nsDisplayWrapList*>(item)->CreateShallowCopy(this));
381 merged->GetChildren()->AppendToTop(last->CreateShallowCopy(this));
383 return merged;
386 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
387 SetCurrentActiveScrolledRoot(
388 const ActiveScrolledRoot* aActiveScrolledRoot) {
389 MOZ_ASSERT(!mUsed);
391 // Set the builder's mCurrentActiveScrolledRoot.
392 mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;
394 // We also need to adjust the builder's mCurrentContainerASR.
395 // mCurrentContainerASR needs to be an ASR that all the container's
396 // contents have finite bounds with respect to. If aActiveScrolledRoot
397 // is an ancestor ASR of mCurrentContainerASR, that means we need to
398 // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
399 // the items that will be created with aActiveScrolledRoot wouldn't
400 // have finite bounds with respect to mCurrentContainerASR. There's one
401 // exception, in the case where there's a content clip on the builder
402 // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
403 // content clip will clip all items that are created while this
404 // AutoCurrentActiveScrolledRootSetter exists. This means that the items
405 // created during our lifetime will have finite bounds with respect to
406 // the content clip's ASR, even if the items' actual ASR is an ancestor
407 // of that. And it also means that mCurrentContainerASR only needs to be
408 // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
409 // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
410 // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
412 // finiteBoundsASR is the leafmost ASR that all items created during
413 // object's lifetime have finite bounds with respect to.
414 const ActiveScrolledRoot* finiteBoundsASR =
415 ActiveScrolledRoot::PickDescendant(mContentClipASR, aActiveScrolledRoot);
417 // mCurrentContainerASR is adjusted so that it's still an ancestor of
418 // finiteBoundsASR.
419 mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
420 mBuilder->mCurrentContainerASR, finiteBoundsASR);
422 // If we are entering out-of-flow content inside a CSS filter, mark
423 // scroll frames wrt. which the content is fixed as containing such content.
424 if (mBuilder->mFilterASR && ActiveScrolledRoot::IsAncestor(
425 aActiveScrolledRoot, mBuilder->mFilterASR)) {
426 for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
427 asr && asr != aActiveScrolledRoot; asr = asr->mParent) {
428 asr->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
432 mUsed = true;
435 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
436 InsertScrollFrame(nsIScrollableFrame* aScrollableFrame) {
437 MOZ_ASSERT(!mUsed);
438 size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
439 const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot;
440 const ActiveScrolledRoot* asr =
441 mBuilder->AllocateActiveScrolledRoot(parentASR, aScrollableFrame);
442 mBuilder->mCurrentActiveScrolledRoot = asr;
444 // All child ASRs of parentASR that were created while this
445 // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
446 // now. Reparent them to asr.
447 for (size_t i = mDescendantsStartIndex; i < descendantsEndIndex; i++) {
448 ActiveScrolledRoot* descendantASR = mBuilder->mActiveScrolledRoots[i];
449 if (ActiveScrolledRoot::IsAncestor(parentASR, descendantASR)) {
450 descendantASR->IncrementDepth();
451 if (descendantASR->mParent == parentASR) {
452 descendantASR->mParent = asr;
457 mUsed = true;
460 nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
461 nsDisplayListBuilder* aBuilder)
462 : mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) {
463 mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot;
466 nsPresContext* nsDisplayListBuilder::CurrentPresContext() {
467 return CurrentPresShellState()->mPresShell->GetPresContext();
470 /* static */
471 nsRect nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
472 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
473 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
474 nsRect* aOutDirtyRect) {
475 nsRect visible = aVisibleRect;
476 nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
478 bool inPartialUpdate =
479 aBuilder->IsRetainingDisplayList() && aBuilder->IsPartialUpdate();
480 if (StaticPrefs::apz_allow_zooming() &&
481 DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame) &&
482 aBuilder->IsPaintingToWindow() && !inPartialUpdate) {
483 dirtyRectRelativeToDirtyFrame =
484 nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize());
486 // If there's a visual viewport size set, restrict the amount of the
487 // fixed-position element we paint to the visual viewport. (In general
488 // the fixed-position element can be as large as the layout viewport,
489 // which at a high zoom level can cause us to paint too large of an
490 // area.)
491 PresShell* presShell = aFrame->PresShell();
492 if (presShell->IsVisualViewportSizeSet()) {
493 dirtyRectRelativeToDirtyFrame =
494 nsRect(presShell->GetVisualViewportOffsetRelativeToLayoutViewport(),
495 presShell->GetVisualViewportSize());
496 // But if we have a displayport, expand it to the displayport, so
497 // that async-scrolling the visual viewport within the layout viewport
498 // will not checkerboard.
499 if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
500 nsRect displayport;
501 // Note that the displayport here is already in the right coordinate
502 // space: it's relative to the scroll port (= layout viewport), but
503 // covers the visual viewport with some margins around it, which is
504 // exactly what we want.
505 if (DisplayPortUtils::GetDisplayPort(
506 rootScrollFrame->GetContent(), &displayport,
507 DisplayPortOptions().With(ContentGeometryType::Fixed))) {
508 dirtyRectRelativeToDirtyFrame = displayport;
512 visible = dirtyRectRelativeToDirtyFrame;
513 if (StaticPrefs::apz_test_logging_enabled() &&
514 presShell->GetDocument()->IsContentDocument()) {
515 nsLayoutUtils::LogAdditionalTestData(
516 aBuilder, "fixedPosDisplayport",
517 ToString(CSSSize::FromAppUnits(visible)));
521 *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
522 visible -= aFrame->GetPosition();
524 nsRect overflowRect = aFrame->InkOverflowRect();
526 if (aFrame->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
527 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
529 * Add a fuzz factor to the overflow rectangle so that elements only
530 * just out of view are pulled into the display list, so they can be
531 * prerendered if necessary.
533 overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
536 visible.IntersectRect(visible, overflowRect);
537 aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect);
539 return visible;
542 nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder* aBuilder,
543 nsIFrame* aFrame,
544 nsDisplayList* aList)
545 : mList(aList) {
546 // Find the element that we need to check for link-ness, bailing out if
547 // we can't find one.
548 Element* elem = Element::FromNodeOrNull(aFrame->GetContent());
549 if (!elem) {
550 return;
553 // If the element has an id and/or name attribute, generate a destination
554 // for possible internal linking.
555 auto maybeGenerateDest = [&](const nsAtom* aAttr) {
556 nsAutoString attrValue;
557 elem->GetAttr(aAttr, attrValue);
558 if (!attrValue.IsEmpty()) {
559 NS_ConvertUTF16toUTF8 dest(attrValue);
560 // Ensure that we only emit a given destination once, although there may
561 // be multiple frames associated with a given element; we'll simply use
562 // the first of them as the target of any links to it.
563 // XXX(jfkthame) This prevents emitting duplicate destinations *on the
564 // same page*, but does not prevent duplicates on subsequent pages, as
565 // each new page is handled by a new temporary DisplayListBuilder. This
566 // seems to be harmless in practice, though a bit wasteful of space. To
567 // fix, we need to maintain the set of already-seen destinations globally
568 // for the print job, rather than attached to the (per-page) builder.
569 if (aBuilder->mDestinations.EnsureInserted(dest)) {
570 auto* destination = MakeDisplayItem<nsDisplayDestination>(
571 aBuilder, aFrame, dest.get(), aFrame->GetRect().TopLeft());
572 mList->AppendToTop(destination);
577 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
578 if (elem->HasID()) {
579 maybeGenerateDest(nsGkAtoms::id);
581 if (elem->HasName()) {
582 maybeGenerateDest(nsGkAtoms::name);
586 // Links don't nest, so if the builder already has a destination, no need to
587 // check for a link element here.
588 if (!aBuilder->mLinkSpec.IsEmpty()) {
589 return;
592 // Check if we have actually found a link.
593 if (!elem->IsLink()) {
594 return;
597 nsCOMPtr<nsIURI> uri = elem->GetHrefURI();
598 if (!uri) {
599 return;
602 // Is it a local (in-page) destination?
603 bool hasRef, eqExRef;
604 nsIURI* docURI;
605 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
606 NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef &&
607 (docURI = aFrame->PresContext()->Document()->GetDocumentURI()) &&
608 NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &eqExRef)) && eqExRef) {
609 if (NS_FAILED(uri->GetRef(aBuilder->mLinkSpec)) ||
610 aBuilder->mLinkSpec.IsEmpty()) {
611 return;
613 // The destination name is simply a string; we don't want URL-escaping
614 // applied to it.
615 NS_UnescapeURL(aBuilder->mLinkSpec);
616 // Mark the link spec as being an internal destination
617 aBuilder->mLinkSpec.Insert('#', 0);
618 } else {
619 if (NS_FAILED(uri->GetSpec(aBuilder->mLinkSpec)) ||
620 aBuilder->mLinkSpec.IsEmpty()) {
621 return;
625 // Record that we need to reset the builder's state on destruction.
626 mBuilderToReset = aBuilder;
629 void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
630 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
631 // Note that we may generate a link here even if the constructor bailed out
632 // without updating aBuilder->LinkSpec(), because it may have been set by
633 // an ancestor that was associated with a link element.
634 if (!aBuilder->mLinkSpec.IsEmpty()) {
635 auto* link = MakeDisplayItem<nsDisplayLink>(
636 aBuilder, aFrame, aBuilder->mLinkSpec.get(), aFrame->GetRect());
637 mList->AppendToTop(link);
641 uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1);
643 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
644 nsDisplayListBuilderMode aMode,
645 bool aBuildCaret,
646 bool aRetainingDisplayList)
647 : mReferenceFrame(aReferenceFrame),
648 mIgnoreScrollFrame(nullptr),
649 mCurrentActiveScrolledRoot(nullptr),
650 mCurrentContainerASR(nullptr),
651 mCurrentFrame(aReferenceFrame),
652 mCurrentReferenceFrame(aReferenceFrame),
653 mCaretFrame(nullptr),
654 mScrollInfoItemsForHoisting(nullptr),
655 mFirstClipChainToDestroy(nullptr),
656 mTableBackgroundSet(nullptr),
657 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
658 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
659 mFilterASR(nullptr),
660 mDirtyRect(-1, -1, -1, -1),
661 mBuildingExtraPagesForPageNum(0),
662 mMode(aMode),
663 mContainsBlendMode(false),
664 mIsBuildingScrollbar(false),
665 mCurrentScrollbarWillHaveLayer(false),
666 mBuildCaret(aBuildCaret),
667 mRetainingDisplayList(aRetainingDisplayList),
668 mPartialUpdate(false),
669 mIgnoreSuppression(false),
670 mIncludeAllOutOfFlows(false),
671 mDescendIntoSubdocuments(true),
672 mSelectedFramesOnly(false),
673 mAllowMergingAndFlattening(true),
674 mInTransform(false),
675 mInEventsOnly(false),
676 mInFilter(false),
677 mInPageSequence(false),
678 mIsInChromePresContext(false),
679 mSyncDecodeImages(false),
680 mIsPaintingToWindow(false),
681 mUseHighQualityScaling(false),
682 mIsPaintingForWebRender(false),
683 mIsCompositingCheap(false),
684 mAncestorHasApzAwareEventHandler(false),
685 mHaveScrollableDisplayPort(false),
686 mWindowDraggingAllowed(false),
687 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
688 mForceLayerForScrollParent(false),
689 mContainsNonMinimalDisplayPort(false),
690 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
691 mBuildingInvisibleItems(false),
692 mIsBuilding(false),
693 mInInvalidSubtree(false),
694 mDisablePartialUpdates(false),
695 mPartialBuildFailed(false),
696 mIsInActiveDocShell(false),
697 mBuildAsyncZoomContainer(false),
698 mIsRelativeToLayoutViewport(false),
699 mUseOverlayScrollbars(false),
700 mAlwaysLayerizeScrollbars(false) {
701 MOZ_COUNT_CTOR(nsDisplayListBuilder);
703 mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting();
705 ShouldRebuildDisplayListDueToPrefChange();
707 mUseOverlayScrollbars =
708 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
710 mAlwaysLayerizeScrollbars =
711 StaticPrefs::layout_scrollbars_always_layerize_track();
713 static_assert(
714 static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
715 "Check TYPE_MAX should not overflow");
717 mIsReusingStackingContextItems =
718 mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
721 static PresShell* GetFocusedPresShell() {
722 nsPIDOMWindowOuter* focusedWnd =
723 nsFocusManager::GetFocusManager()->GetFocusedWindow();
724 if (!focusedWnd) {
725 return nullptr;
728 nsCOMPtr<nsIDocShell> focusedDocShell = focusedWnd->GetDocShell();
729 if (!focusedDocShell) {
730 return nullptr;
733 return focusedDocShell->GetPresShell();
736 void nsDisplayListBuilder::BeginFrame() {
737 nsCSSRendering::BeginFrameTreesLocked();
739 mIsPaintingToWindow = false;
740 mUseHighQualityScaling = false;
741 mIgnoreSuppression = false;
742 mInTransform = false;
743 mInFilter = false;
744 mSyncDecodeImages = false;
746 if (!mBuildCaret) {
747 return;
750 RefPtr<PresShell> presShell = GetFocusedPresShell();
751 if (presShell) {
752 RefPtr<nsCaret> caret = presShell->GetCaret();
753 mCaretFrame = caret->GetPaintGeometry(&mCaretRect);
755 // The focused pres shell may not be in the document that we're
756 // painting, or be in a popup. Check if the display root for
757 // the caret matches the display root that we're painting, and
758 // only use it if it matches.
759 if (mCaretFrame &&
760 nsLayoutUtils::GetDisplayRootFrame(mCaretFrame) !=
761 nsLayoutUtils::GetDisplayRootFrame(mReferenceFrame)) {
762 mCaretFrame = nullptr;
767 void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser* aBrowser,
768 const dom::EffectsInfo& aUpdate) {
769 dom::EffectsInfo update = aUpdate;
770 // For printing we create one display item for each page that an iframe
771 // appears on, the proper visible rect is the union of all the visible rects
772 // we get from each display item.
773 nsPresContext* pc =
774 mReferenceFrame ? mReferenceFrame->PresContext() : nullptr;
775 if (pc && pc->Type() != nsPresContext::eContext_Galley) {
776 Maybe<dom::EffectsInfo> existing = mEffectsUpdates.MaybeGet(aBrowser);
777 if (existing) {
778 // Only the visible rect should differ, the scales should match.
779 MOZ_ASSERT(existing->mRasterScale == aUpdate.mRasterScale &&
780 existing->mTransformToAncestorScale ==
781 aUpdate.mTransformToAncestorScale);
782 if (existing->mVisibleRect) {
783 if (update.mVisibleRect) {
784 update.mVisibleRect =
785 Some(update.mVisibleRect->Union(*existing->mVisibleRect));
786 } else {
787 update.mVisibleRect = existing->mVisibleRect;
792 mEffectsUpdates.InsertOrUpdate(aBrowser, update);
795 void nsDisplayListBuilder::EndFrame() {
796 NS_ASSERTION(!mInInvalidSubtree,
797 "Someone forgot to cleanup mInInvalidSubtree!");
798 mCurrentContainerASR = nullptr;
799 mActiveScrolledRoots.Clear();
800 mEffectsUpdates.Clear();
801 FreeClipChains();
802 FreeTemporaryItems();
803 nsCSSRendering::EndFrameTreesLocked();
804 mCaretFrame = nullptr;
807 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
808 const nsIFrame* aStopAtFrame) {
809 mFramesMarkedForDisplay.AppendElement(aFrame);
810 for (nsIFrame* f = aFrame; f;
811 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
812 if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
813 return;
815 f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
816 if (f == aStopAtFrame) {
817 // we've reached a frame that we know will be painted, so we can stop.
818 break;
823 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) {
824 mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
827 static void MarkFrameForDisplayIfVisibleInternal(nsIFrame* aFrame,
828 const nsIFrame* aStopAtFrame) {
829 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
830 if (f->ForceDescendIntoIfVisible()) {
831 return;
833 f->SetForceDescendIntoIfVisible(true);
835 // This condition must match the condition in
836 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
837 // nsLayoutUtils::GetDisplayListParent
838 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
839 nsIFrame* parent = f->GetParent();
840 if (parent && !parent->ForceDescendIntoIfVisible()) {
841 // If the GetDisplayListParent call is going to walk to a placeholder,
842 // in rare cases the placeholder might be contained in a different
843 // continuation from the oof. So we have to make sure to mark the oofs
844 // parent. In the common case this doesn't make us do any extra work,
845 // just changes the order in which we visit the frames since walking
846 // through placeholders will walk through the parent, and we stop when
847 // we find a ForceDescendIntoIfVisible bit set.
848 MarkFrameForDisplayIfVisibleInternal(parent, aStopAtFrame);
852 if (f == aStopAtFrame) {
853 // we've reached a frame that we know will be painted, so we can stop.
854 break;
859 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
860 nsIFrame* aFrame, const nsIFrame* aStopAtFrame) {
861 AddFrameMarkedForDisplayIfVisible(aFrame);
863 MarkFrameForDisplayIfVisibleInternal(aFrame, aStopAtFrame);
866 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
867 mIsRelativeToLayoutViewport = true;
868 UpdateShouldBuildAsyncZoomContainer();
871 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
872 const Document* document = mReferenceFrame->PresContext()->Document();
873 mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
874 !document->Fullscreen() &&
875 nsLayoutUtils::AllowZoomingForDocument(document);
878 // Certain prefs may cause display list items to be added or removed when they
879 // are toggled. In those cases, we need to fully rebuild the display list.
880 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
881 // If we transition between wrapping the RCD-RSF contents into an async
882 // zoom container vs. not, we need to rebuild the display list. This only
883 // happens when the zooming or container scrolling prefs are toggled
884 // (manually by the user, or during test setup).
885 bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer;
886 UpdateShouldBuildAsyncZoomContainer();
888 bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars;
889 mUseOverlayScrollbars =
890 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
892 bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars;
893 mAlwaysLayerizeScrollbars =
894 StaticPrefs::layout_scrollbars_always_layerize_track();
896 if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) {
897 return true;
900 if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) {
901 return true;
904 if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) {
905 return true;
908 return false;
911 void nsDisplayListBuilder::AddScrollFrameToNotify(
912 nsIScrollableFrame* aScrollFrame) {
913 mScrollFramesToNotify.insert(aScrollFrame);
916 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
917 for (const auto& it : mScrollFramesToNotify) {
918 it->NotifyApzTransaction();
920 mScrollFramesToNotify.clear();
923 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
924 nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect,
925 const nsRect& aDirtyRect) {
926 MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
927 nsRect dirty;
928 nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
929 this, aFrame, aVisibleRect, aDirtyRect, &dirty);
930 if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
931 visible.IsEmpty()) {
932 return false;
935 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
936 // frame, then it will also mark any outer frames to ensure that building
937 // reaches the dirty feame.
938 if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
939 MarkFrameForDisplay(aFrame, aDirtyFrame);
942 return true;
945 static void UnmarkFrameForDisplay(nsIFrame* aFrame,
946 const nsIFrame* aStopAtFrame) {
947 for (nsIFrame* f = aFrame; f;
948 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
949 if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
950 return;
952 f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
953 if (f == aStopAtFrame) {
954 // we've reached a frame that we know will be painted, so we can stop.
955 break;
960 static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
961 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
962 if (!f->ForceDescendIntoIfVisible()) {
963 return;
965 f->SetForceDescendIntoIfVisible(false);
967 // This condition must match the condition in
968 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
969 // nsLayoutUtils::GetDisplayListParent
970 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
971 nsIFrame* parent = f->GetParent();
972 if (parent && parent->ForceDescendIntoIfVisible()) {
973 // If the GetDisplayListParent call is going to walk to a placeholder,
974 // in rare cases the placeholder might be contained in a different
975 // continuation from the oof. So we have to make sure to mark the oofs
976 // parent. In the common case this doesn't make us do any extra work,
977 // just changes the order in which we visit the frames since walking
978 // through placeholders will walk through the parent, and we stop when
979 // we find a ForceDescendIntoIfVisible bit set.
980 UnmarkFrameForDisplayIfVisible(f);
986 nsDisplayListBuilder::~nsDisplayListBuilder() {
987 NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
988 "All frames should have been unmarked");
989 NS_ASSERTION(mFramesWithOOFData.Length() == 0,
990 "All OOF data should have been removed");
991 NS_ASSERTION(mPresShellStates.Length() == 0,
992 "All presshells should have been exited");
994 DisplayItemClipChain* c = mFirstClipChainToDestroy;
995 while (c) {
996 DisplayItemClipChain* next = c->mNextClipChainToDestroy;
997 c->DisplayItemClipChain::~DisplayItemClipChain();
998 c = next;
1001 MOZ_COUNT_DTOR(nsDisplayListBuilder);
1004 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
1005 uint32_t flags = 0;
1006 if (mSyncDecodeImages) {
1007 flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
1009 if (mIsPaintingToWindow) {
1010 flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
1012 if (mUseHighQualityScaling) {
1013 flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING;
1015 return flags;
1018 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
1019 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
1020 uint32_t flags = 0;
1021 if (mSyncDecodeImages) {
1022 flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
1024 if (mIsPaintingToWindow) {
1025 flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
1027 if (mUseHighQualityScaling) {
1028 flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
1030 return flags;
1033 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1034 uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
1035 if (mSyncDecodeImages) {
1036 flags |= imgIContainer::FLAG_SYNC_DECODE;
1037 } else {
1038 flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
1040 if (mIsPaintingToWindow || mUseHighQualityScaling) {
1041 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1043 return flags;
1046 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
1047 const nsRegion& aRegion) {
1048 if (aRegion.IsEmpty()) {
1049 return;
1052 nsRegion tmp;
1053 tmp.Sub(*aVisibleRegion, aRegion);
1054 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1055 // to its bounds either, which can be very bad (see bug 516740).
1056 // Do let aVisibleRegion get more complex if by doing so we reduce its
1057 // area by at least half.
1058 if (tmp.GetNumRects() <= 15 || tmp.Area() <= aVisibleRegion->Area() / 2) {
1059 *aVisibleRegion = tmp;
1063 nsCaret* nsDisplayListBuilder::GetCaret() {
1064 RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
1065 return caret;
1068 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) {
1069 if (mIsPaintingToWindow) {
1070 aPresShell->IncrementPaintCount();
1074 void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
1075 bool aPointerEventsNoneDoc) {
1076 PresShellState* state = mPresShellStates.AppendElement();
1077 state->mPresShell = aReferenceFrame->PresShell();
1078 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
1079 state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
1081 nsIScrollableFrame* sf = state->mPresShell->GetRootScrollFrameAsScrollable();
1082 if (sf && IsInSubdocument()) {
1083 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1084 // that the canvas background color will be set correctly, and that only one
1085 // unscrollable item will be created.
1086 // This is done to avoid, for example, a case where only scrollbar frames
1087 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1088 // and possibly end up with an extra nsDisplaySolidColor item.
1089 // We skip this for the root document, since we don't want to use
1090 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1091 // do it manually there.
1092 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
1093 if (canvasFrame) {
1094 MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
1098 #ifdef DEBUG
1099 state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
1100 nsLayoutPhase::DisplayListBuilding);
1101 #endif
1103 state->mPresShell->UpdateCanvasBackground();
1105 bool buildCaret = mBuildCaret;
1106 if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
1107 state->mIsBackgroundOnly = false;
1108 } else {
1109 state->mIsBackgroundOnly = true;
1110 buildCaret = false;
1113 bool pointerEventsNone = aPointerEventsNoneDoc;
1114 if (IsInSubdocument()) {
1115 pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
1116 .mInsidePointerEventsNoneDoc;
1118 state->mInsidePointerEventsNoneDoc = pointerEventsNone;
1120 state->mPresShellIgnoreScrollFrame =
1121 state->mPresShell->IgnoringViewportScrolling()
1122 ? state->mPresShell->GetRootScrollFrame()
1123 : nullptr;
1125 nsPresContext* pc = aReferenceFrame->PresContext();
1126 mIsInChromePresContext = pc->IsChrome();
1127 nsIDocShell* docShell = pc->GetDocShell();
1129 if (docShell) {
1130 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1133 state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);
1135 if (!buildCaret) {
1136 return;
1139 // Caret frames add visual area to their frame, but we don't update the
1140 // overflow area. Use flags to make sure we build display items for that frame
1141 // instead.
1142 if (mCaretFrame && mCaretFrame->PresShell() == state->mPresShell) {
1143 MarkFrameForDisplay(mCaretFrame, aReferenceFrame);
1147 // A non-blank paint is a paint that does not just contain the canvas
1148 // background.
1149 static bool DisplayListIsNonBlank(nsDisplayList* aList) {
1150 for (nsDisplayItem* i : *aList) {
1151 switch (i->GetType()) {
1152 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
1153 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
1154 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
1155 continue;
1156 case DisplayItemType::TYPE_SOLID_COLOR:
1157 case DisplayItemType::TYPE_BACKGROUND:
1158 case DisplayItemType::TYPE_BACKGROUND_COLOR:
1159 if (i->Frame()->IsCanvasFrame()) {
1160 continue;
1162 return true;
1163 default:
1164 return true;
1167 return false;
1170 // A contentful paint is a paint that does contains DOM content (text,
1171 // images, non-blank canvases, SVG): "First Contentful Paint entry
1172 // contains a DOMHighResTimeStamp reporting the time when the browser
1173 // first rendered any text, image (including background images),
1174 // non-white canvas or SVG. This excludes any content of iframes, but
1175 // includes text with pending webfonts. This is the first time users
1176 // could start consuming page content."
1177 static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
1178 nsDisplayList* aList) {
1179 for (nsDisplayItem* i : *aList) {
1180 DisplayItemType type = i->GetType();
1181 nsDisplayList* children = i->GetChildren();
1183 switch (type) {
1184 case DisplayItemType::TYPE_SUBDOCUMENT: // iframes are ignored
1185 break;
1186 // CANVASes check if they may have been modified (as a stand-in
1187 // actually tracking all modifications)
1188 default:
1189 if (i->IsContentful()) {
1190 bool dummy;
1191 nsRect bound = i->GetBounds(aBuilder, &dummy);
1192 if (!bound.IsEmpty()) {
1193 return true;
1196 if (children) {
1197 if (DisplayListIsContentful(aBuilder, children)) {
1198 return true;
1201 break;
1204 return false;
1207 void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
1208 nsDisplayList* aPaintedContents) {
1209 NS_ASSERTION(
1210 CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
1211 "Presshell mismatch");
1213 if (mIsPaintingToWindow && aPaintedContents) {
1214 nsPresContext* pc = aReferenceFrame->PresContext();
1215 if (!pc->HadNonBlankPaint()) {
1216 if (!CurrentPresShellState()->mIsBackgroundOnly &&
1217 DisplayListIsNonBlank(aPaintedContents)) {
1218 pc->NotifyNonBlankPaint();
1221 nsRootPresContext* rootPresContext = pc->GetRootPresContext();
1222 if (!pc->HasStoppedGeneratingLCP() && rootPresContext) {
1223 if (!CurrentPresShellState()->mIsBackgroundOnly) {
1224 if (pc->HasEverBuiltInvisibleText() ||
1225 DisplayListIsContentful(this, aPaintedContents)) {
1226 pc->NotifyContentfulPaint();
1232 ResetMarkedFramesForDisplayList(aReferenceFrame);
1233 mPresShellStates.RemoveLastElement();
1235 if (!mPresShellStates.IsEmpty()) {
1236 nsPresContext* pc = CurrentPresContext();
1237 nsIDocShell* docShell = pc->GetDocShell();
1238 if (docShell) {
1239 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1241 mIsInChromePresContext = pc->IsChrome();
1242 } else {
1243 for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
1244 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
1246 mFramesMarkedForDisplayIfVisible.SetLength(0);
1250 void nsDisplayListBuilder::FreeClipChains() {
1251 // Iterate the clip chains from newest to oldest (forward
1252 // iteration), so that we destroy descendants first which
1253 // will drop the ref count on their ancestors.
1254 DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;
1256 while (*indirect) {
1257 if (!(*indirect)->mRefCount) {
1258 DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;
1260 mClipDeduplicator.erase(*indirect);
1261 (*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
1262 Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);
1264 *indirect = next;
1265 } else {
1266 indirect = &(*indirect)->mNextClipChainToDestroy;
1271 void nsDisplayListBuilder::FreeTemporaryItems() {
1272 for (nsDisplayItem* i : mTemporaryItems) {
1273 // Temporary display items are not added to the frames.
1274 MOZ_ASSERT(i->Frame());
1275 i->RemoveFrame(i->Frame());
1276 i->Destroy(this);
1279 mTemporaryItems.Clear();
1282 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1283 const nsIFrame* aReferenceFrame) {
1284 // Unmark and pop off the frames marked for display in this pres shell.
1285 uint32_t firstFrameForShell =
1286 CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1287 for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
1288 ++i) {
1289 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
1291 mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1293 firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
1294 for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
1295 mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
1297 mFramesWithOOFData.SetLength(firstFrameForShell);
1300 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1301 CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
1304 void nsDisplayListBuilder::MarkFramesForDisplayList(
1305 nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
1306 nsRect visibleRect = GetVisibleRect();
1307 nsRect dirtyRect = GetDirtyRect();
1309 // If we are entering content that is fixed to the RCD-RSF, we are
1310 // crossing the async zoom container boundary, and need to convert from
1311 // visual to layout coordinates.
1312 if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
1313 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1314 viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
1315 if (viewportFrame->PresShell()->GetRootScrollFrame()) {
1316 #ifdef DEBUG
1317 for (nsIFrame* f : aFrames) {
1318 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
1320 #endif
1321 visibleRect = ViewportUtils::VisualToLayout(visibleRect,
1322 viewportFrame->PresShell());
1323 dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
1324 viewportFrame->PresShell());
1326 #ifdef DEBUG
1327 else {
1328 // This is an edge case that should only happen if we are in a
1329 // document with a XUL root element so that it does not have a root
1330 // scroll frame but it has fixed pos content and all of the frames in
1331 // aFrames are that fixed pos content.
1332 for (nsIFrame* f : aFrames) {
1333 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
1334 f->GetParent() == aDirtyFrame &&
1335 f->StyleDisplay()->mPosition ==
1336 StylePositionProperty::Fixed);
1338 // There's no root scroll frame so there can't be any zooming or async
1339 // panning so we don't need to adjust the visible and dirty rects.
1341 #endif
1345 bool markedFrames = false;
1346 for (nsIFrame* e : aFrames) {
1347 // Skip the AccessibleCaret frame when building no caret.
1348 if (!IsBuildingCaret()) {
1349 nsIContent* content = e->GetContent();
1350 if (content && content->IsInNativeAnonymousSubtree() &&
1351 content->IsElement()) {
1352 const nsAttrValue* classes = content->AsElement()->GetClasses();
1353 if (classes &&
1354 classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) {
1355 continue;
1359 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
1360 markedFrames = true;
1364 if (markedFrames) {
1365 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1366 // to objects on the stack, so we need to clone the chain.
1367 const DisplayItemClipChain* clipChain =
1368 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1369 const DisplayItemClipChain* combinedClipChain =
1370 mClipState.GetCurrentCombinedClipChain(this);
1371 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1373 OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
1374 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1375 visibleRect, dirtyRect);
1376 aDirtyFrame->SetProperty(
1377 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
1378 mFramesWithOOFData.AppendElement(aDirtyFrame);
1381 if (!aDirtyFrame->GetParent()) {
1382 // This is the viewport frame of aDirtyFrame's presshell.
1383 // Store the current display data so that it can be used for fixed
1384 // background images.
1385 NS_ASSERTION(
1386 CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
1387 "Presshell mismatch");
1388 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
1389 "already traversed this presshell's root frame?");
1391 const DisplayItemClipChain* clipChain =
1392 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1393 const DisplayItemClipChain* combinedClipChain =
1394 mClipState.GetCurrentCombinedClipChain(this);
1395 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1396 CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
1397 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1398 GetVisibleRect(), GetDirtyRect());
1403 * Mark all preserve-3d children with
1404 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1405 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1406 * dirty rect for preserve-3d children.
1408 * @param aDirtyFrame is the frame to mark children extending context.
1410 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1411 nsIFrame* aDirtyFrame) {
1412 for (const auto& childList : aDirtyFrame->ChildLists()) {
1413 for (nsIFrame* child : childList.mList) {
1414 if (child->Combines3DTransformWithAncestors()) {
1415 MarkFrameForDisplay(child, aDirtyFrame);
1418 if (child->IsBlockWrapper()) {
1419 // Mark preserve-3d frames inside the block wrapper.
1420 MarkPreserve3DFramesForDisplayList(child);
1426 ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1427 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame) {
1428 RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
1429 aParent, aScrollableFrame, IsRetainingDisplayList());
1430 mActiveScrolledRoots.AppendElement(asr);
1431 return asr;
1434 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1435 const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
1436 const DisplayItemClipChain* aParent) {
1437 MOZ_DIAGNOSTIC_ASSERT(!(aParent && aParent->mOnStack));
1438 void* p = Allocate(sizeof(DisplayItemClipChain),
1439 DisplayListArenaObjectId::CLIPCHAIN);
1440 DisplayItemClipChain* c = new (KnownNotNull, p)
1441 DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy);
1442 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
1443 c->mOnStack = false;
1444 #endif
1445 auto result = mClipDeduplicator.insert(c);
1446 if (!result.second) {
1447 // An equivalent clip chain item was already created, so let's return that
1448 // instead. Destroy the one we just created.
1449 // Note that this can cause clip chains from different coordinate systems to
1450 // collapse into the same clip chain object, because clip chains do not keep
1451 // track of the reference frame that they were created in.
1452 c->DisplayItemClipChain::~DisplayItemClipChain();
1453 Destroy(DisplayListArenaObjectId::CLIPCHAIN, c);
1454 return *(result.first);
1456 mFirstClipChainToDestroy = c;
1457 return c;
1460 struct ClipChainItem {
1461 DisplayItemClip clip;
1462 const ActiveScrolledRoot* asr;
1465 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
1466 const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
1467 for (const ActiveScrolledRoot* asr =
1468 ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
1469 asr; asr = asr->mParent) {
1470 if (aOne == aTwo) {
1471 return aOne;
1473 if (aOne->mASR == asr) {
1474 aOne = aOne->mParent;
1476 if (aTwo->mASR == asr) {
1477 aTwo = aTwo->mParent;
1479 if (!aOne) {
1480 return aTwo;
1482 if (!aTwo) {
1483 return aOne;
1486 return nullptr;
1489 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1490 const DisplayItemClipChain* aAncestor,
1491 const DisplayItemClipChain* aLeafClip1,
1492 const DisplayItemClipChain* aLeafClip2) {
1493 AutoTArray<ClipChainItem, 8> intersectedClips;
1495 const DisplayItemClipChain* clip1 = aLeafClip1;
1496 const DisplayItemClipChain* clip2 = aLeafClip2;
1498 const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
1499 clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);
1501 // Build up the intersection from the leaf to the root and put it into
1502 // intersectedClips. The loop below will convert intersectedClips into an
1503 // actual DisplayItemClipChain.
1504 // (We need to do this in two passes because we need the parent clip in order
1505 // to create the DisplayItemClipChain object, but the parent clip has not
1506 // been created at that point.)
1507 while (!aAncestor || asr != aAncestor->mASR) {
1508 if (clip1 && clip1->mASR == asr) {
1509 if (clip2 && clip2->mASR == asr) {
1510 DisplayItemClip intersection = clip1->mClip;
1511 intersection.IntersectWith(clip2->mClip);
1512 intersectedClips.AppendElement(ClipChainItem{intersection, asr});
1513 clip2 = clip2->mParent;
1514 } else {
1515 intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
1517 clip1 = clip1->mParent;
1518 } else if (clip2 && clip2->mASR == asr) {
1519 intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
1520 clip2 = clip2->mParent;
1522 if (!asr) {
1523 MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
1524 break;
1526 asr = asr->mParent;
1529 // Convert intersectedClips into a DisplayItemClipChain.
1530 const DisplayItemClipChain* parentSC = aAncestor;
1531 for (auto& sc : Reversed(intersectedClips)) {
1532 parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
1534 return parentSC;
1537 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1538 const DisplayItemClipChain* aLeafClip1,
1539 const DisplayItemClipChain* aLeafClip2) {
1540 // aLeafClip2 might be a reference to a clip on the stack. We need to make
1541 // sure that CreateClipChainIntersection will allocate the actual intersected
1542 // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1543 // we supply nullptr as the common ancestor so that
1544 // CreateClipChainIntersection clones the whole chain.
1545 const DisplayItemClipChain* ancestorClip =
1546 aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
1547 : nullptr;
1549 return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2);
1552 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
1553 const DisplayItemClipChain* aClipChain) {
1554 return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
1557 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
1558 const nsIFrame* aFrame, nsPoint* aOffset) const {
1559 auto MaybeApplyAdditionalOffset = [&]() {
1560 if (auto offset = AdditionalOffset()) {
1561 *aOffset += *offset;
1565 if (aFrame == mCurrentFrame) {
1566 if (aOffset) {
1567 *aOffset = mCurrentOffsetToReferenceFrame;
1569 return mCurrentReferenceFrame;
1572 for (const nsIFrame* f = aFrame; f;
1573 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
1574 if (f == mReferenceFrame || f->IsTransformed()) {
1575 if (aOffset) {
1576 *aOffset = aFrame->GetOffsetToCrossDoc(f);
1577 MaybeApplyAdditionalOffset();
1579 return f;
1583 if (aOffset) {
1584 *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1585 MaybeApplyAdditionalOffset();
1588 return mReferenceFrame;
1591 // Sticky frames are active if their nearest scrollable frame is also active.
1592 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
1593 nsIFrame* aFrame) {
1594 MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
1595 StylePositionProperty::Sticky);
1597 StickyScrollContainer* stickyScrollContainer =
1598 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
1599 return stickyScrollContainer &&
1600 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1603 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
1604 nsIFrame** aParent) {
1605 if (aFrame == mReferenceFrame) {
1606 return true;
1609 if (!IsPaintingToWindow()) {
1610 if (aParent) {
1611 *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1613 return false;
1616 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1617 if (!parent) {
1618 return true;
1620 *aParent = parent;
1622 if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
1623 IsStickyFrameActive(this, aFrame)) {
1624 return true;
1627 if (aFrame->IsTransformed()) {
1628 if (EffectCompositor::HasAnimationsForCompositor(
1629 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
1630 return true;
1634 LayoutFrameType parentType = parent->Type();
1635 if (parentType == LayoutFrameType::Scroll ||
1636 parentType == LayoutFrameType::ListControl) {
1637 nsIScrollableFrame* sf = do_QueryFrame(parent);
1638 if (sf->GetScrolledFrame() == aFrame) {
1639 MOZ_ASSERT(!aFrame->IsTransformed());
1640 return sf->IsMaybeAsynchronouslyScrolled();
1644 return false;
1647 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1648 nsIFrame* aFrame) {
1649 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1650 RootReferenceFrame(), aFrame));
1651 nsIFrame* cursor = aFrame;
1652 while (cursor != RootReferenceFrame()) {
1653 nsIFrame* next;
1654 if (IsAnimatedGeometryRoot(cursor, &next)) {
1655 return cursor;
1657 cursor = next;
1659 return cursor;
1662 static nsRect ApplyAllClipNonRoundedIntersection(
1663 const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
1664 nsRect result = aRect;
1665 while (aClipChain) {
1666 result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
1667 aClipChain = aClipChain->mParent;
1669 return result;
1672 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
1673 if (!mWindowDraggingAllowed || !IsForPainting()) {
1674 return;
1677 const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
1678 if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
1679 // This frame has the default value and doesn't influence the window
1680 // dragging region.
1681 return;
1684 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;
1686 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1687 nsIFrame* referenceFrame =
1688 const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1690 if (IsInTransform()) {
1691 // Only support 2d rectilinear transforms. Transform support is needed for
1692 // the horizontal flip transform that's applied to the urlbar textbox in
1693 // RTL mode - it should be able to exclude itself from the draggable region.
1694 referenceFrameToRootReferenceFrame =
1695 ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
1696 nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
1697 RelativeTo{mReferenceFrame})
1698 .GetMatrix());
1699 Matrix referenceFrameToRootReferenceFrame2d;
1700 if (!referenceFrameToRootReferenceFrame.Is2D(
1701 &referenceFrameToRootReferenceFrame2d) ||
1702 !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1703 return;
1705 } else {
1706 MOZ_ASSERT(referenceFrame == mReferenceFrame,
1707 "referenceFrameToRootReferenceFrame needs to be adjusted");
1710 // We do some basic visibility checking on the frame's border box here.
1711 // We intersect it both with the current dirty rect and with the current
1712 // clip. Either one is just a conservative approximation on its own, but
1713 // their intersection luckily works well enough for our purposes, so that
1714 // we don't have to do full-blown visibility computations.
1715 // The most important case we need to handle is the scrolled-off tab:
1716 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1717 // should not be allowed to interfere with the window dragging region. Using
1718 // just the current DisplayItemClip is not enough to cover this case
1719 // completely because clips are reset while building stacking context
1720 // contents, so for example we'd fail to clip frames that have a clip path
1721 // applied to them. But the current dirty rect doesn't get reset in that
1722 // case, so we use it to make this case work.
1723 nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
1724 borderBox += ToReferenceFrame(aFrame);
1725 const DisplayItemClipChain* clip =
1726 ClipState().GetCurrentCombinedClipChain(this);
1727 borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
1728 if (borderBox.IsEmpty()) {
1729 return;
1732 LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
1733 borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1735 LayoutDeviceRect transformedDevPixelBorderBox =
1736 TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1737 transformedDevPixelBorderBox.Round();
1738 LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1740 if (!transformedDevPixelBorderBox.ToIntRect(
1741 &transformedDevPixelBorderBoxInt)) {
1742 return;
1745 LayoutDeviceIntRegion& region =
1746 styleUI->mWindowDragging == StyleWindowDragging::Drag
1747 ? mWindowDraggingRegion
1748 : mWindowNoDraggingRegion;
1750 if (!IsRetainingDisplayList()) {
1751 region.OrWith(transformedDevPixelBorderBoxInt);
1752 return;
1755 gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
1756 if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
1757 mRetainedWindowDraggingRegion.Add(aFrame, rect);
1758 } else {
1759 mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
1763 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
1764 LayoutDeviceIntRegion result;
1765 if (!IsRetainingDisplayList()) {
1766 result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
1767 return result;
1770 LayoutDeviceIntRegion dragRegion =
1771 mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();
1773 LayoutDeviceIntRegion noDragRegion =
1774 mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();
1776 result.Sub(dragRegion, noDragRegion);
1777 return result;
1780 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1781 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes);
1782 aSizes.mLayoutRetainedDisplayListSize +=
1783 aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get());
1786 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1787 mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);
1789 size_t n = 0;
1790 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
1791 n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1792 n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1793 n += mEffectsUpdates.ShallowSizeOfExcludingThis(mallocSizeOf);
1794 n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1795 n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1796 n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
1797 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1799 aSizes.mLayoutRetainedDisplayListSize += n;
1802 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1803 for (nsDisplayItem* item : *this) {
1804 item->AddSizeOfExcludingThis(aSizes);
1805 if (RetainedDisplayList* children = item->GetChildren()) {
1806 children->AddSizeOfExcludingThis(aSizes);
1810 size_t n = 0;
1812 n += mDAG.mDirectPredecessorList.ShallowSizeOfExcludingThis(
1813 aSizes.mState.mMallocSizeOf);
1814 n += mDAG.mNodesInfo.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1815 n += mOldItems.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1817 aSizes.mLayoutRetainedDisplayListSize += n;
1820 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1821 MallocSizeOf aMallocSizeOf) const {
1822 size_t n = 0;
1823 n += mFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
1824 for (const auto& frame : mFrames) {
1825 const UniquePtr<WeakFrame>& weakFrame = frame.mWeakFrame;
1826 n += aMallocSizeOf(weakFrame.get());
1828 n += mRects.ShallowSizeOfExcludingThis(aMallocSizeOf);
1829 return n;
1833 * Removes modified frames and rects from this WeakFrameRegion.
1835 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1836 MOZ_ASSERT(mFrames.Length() == mRects.Length());
1838 uint32_t i = 0;
1839 uint32_t length = mFrames.Length();
1841 while (i < length) {
1842 auto& wrapper = mFrames[i];
1844 if (!wrapper.mWeakFrame->IsAlive() ||
1845 AnyContentAncestorModified(wrapper.mWeakFrame->GetFrame())) {
1846 // To avoid multiple O(n) shifts in the array, move the last element of
1847 // the array to the current position and decrease the array length.
1848 mFrameSet.Remove(wrapper.mFrame);
1849 mFrames[i] = std::move(mFrames[length - 1]);
1850 mRects[i] = std::move(mRects[length - 1]);
1851 length--;
1852 } else {
1853 i++;
1857 mFrames.TruncateLength(length);
1858 mRects.TruncateLength(length);
1861 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1862 mRetainedWindowDraggingRegion.RemoveModifiedFramesAndRects();
1863 mRetainedWindowNoDraggingRegion.RemoveModifiedFramesAndRects();
1864 mRetainedWindowOpaqueRegion.RemoveModifiedFramesAndRects();
1867 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1868 mRetainedWindowDraggingRegion.Clear();
1869 mRetainedWindowNoDraggingRegion.Clear();
1870 mRetainedWindowOpaqueRegion.Clear();
1873 const uint32_t gWillChangeAreaMultiplier = 3;
1874 static uint32_t GetLayerizationCost(const nsSize& aSize) {
1875 // There's significant overhead for each layer created from Gecko
1876 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1877 // Therefore we set a minimum cost threshold of a 64x64 area.
1878 const int minBudgetCost = 64 * 64;
1880 const uint32_t budgetCost = std::max(
1881 minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
1882 nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
1884 return budgetCost;
1887 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
1888 const nsSize& aSize) {
1889 MOZ_ASSERT(IsForPainting());
1891 if (aFrame->MayHaveWillChangeBudget()) {
1892 // The frame is already in the will-change budget.
1893 return true;
1896 const nsPresContext* presContext = aFrame->PresContext();
1897 const nsRect area = presContext->GetVisibleArea();
1898 const uint32_t budgetLimit =
1899 nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1900 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1901 const uint32_t cost = GetLayerizationCost(aSize);
1903 DocumentWillChangeBudget& documentBudget =
1904 mDocumentWillChangeBudgets.LookupOrInsert(presContext);
1906 const bool onBudget =
1907 (documentBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
1909 if (onBudget) {
1910 documentBudget += cost;
1911 mFrameWillChangeBudgets.InsertOrUpdate(
1912 aFrame, FrameWillChangeBudget(presContext, cost));
1913 aFrame->SetMayHaveWillChangeBudget(true);
1916 return onBudget;
1919 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
1920 const nsSize& aSize) {
1921 if (!IsForPainting()) {
1922 // If this nsDisplayListBuilder is not for painting, the layerization should
1923 // not matter. Do the simple thing and return false.
1924 return false;
1927 const bool onBudget = AddToWillChangeBudget(aFrame, aSize);
1928 if (onBudget) {
1929 return true;
1932 auto* pc = aFrame->PresContext();
1933 auto* doc = pc->Document();
1934 if (!doc->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget)) {
1935 AutoTArray<nsString, 2> params;
1936 params.AppendElement()->AppendInt(gWillChangeAreaMultiplier);
1938 nsRect area = pc->GetVisibleArea();
1939 uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1940 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1941 params.AppendElement()->AppendInt(budgetLimit);
1943 doc->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget, false, params);
1946 return false;
1949 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame* aFrame) {
1950 MOZ_ASSERT(IsForPainting());
1952 if (!aFrame->MayHaveWillChangeBudget()) {
1953 return;
1956 aFrame->SetMayHaveWillChangeBudget(false);
1957 RemoveFromWillChangeBudgets(aFrame);
1960 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame* aFrame) {
1961 if (auto entry = mFrameWillChangeBudgets.Lookup(aFrame)) {
1962 const FrameWillChangeBudget& frameBudget = entry.Data();
1964 auto documentBudget =
1965 mDocumentWillChangeBudgets.Lookup(frameBudget.mPresContext);
1967 if (documentBudget) {
1968 *documentBudget -= frameBudget.mUsage;
1971 entry.Remove();
1975 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1976 mFrameWillChangeBudgets.Clear();
1977 mDocumentWillChangeBudgets.Clear();
1980 void nsDisplayListBuilder::EnterSVGEffectsContents(
1981 nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) {
1982 MOZ_ASSERT(aHoistedItemsStorage);
1983 if (mSVGEffectsFrames.IsEmpty()) {
1984 MOZ_ASSERT(!mScrollInfoItemsForHoisting);
1985 mScrollInfoItemsForHoisting = aHoistedItemsStorage;
1987 mSVGEffectsFrames.AppendElement(aEffectsFrame);
1990 void nsDisplayListBuilder::ExitSVGEffectsContents() {
1991 MOZ_ASSERT(!mSVGEffectsFrames.IsEmpty());
1992 mSVGEffectsFrames.RemoveLastElement();
1993 MOZ_ASSERT(mScrollInfoItemsForHoisting);
1994 if (mSVGEffectsFrames.IsEmpty()) {
1995 mScrollInfoItemsForHoisting = nullptr;
1999 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
2001 * Note: if changing the conditions under which scroll info layers
2002 * are created, make a corresponding change to
2003 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
2005 for (nsIFrame* frame : mSVGEffectsFrames) {
2006 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame)) {
2007 return true;
2010 return false;
2013 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
2014 nsDisplayScrollInfoLayer* aScrollInfoItem) {
2015 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2016 MOZ_ASSERT(mScrollInfoItemsForHoisting);
2017 mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
2020 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2021 nsIFrame* aFrame, nsDisplayList* aList) {
2022 MOZ_ASSERT(aFrame);
2023 MOZ_ASSERT(aList);
2025 if (!BuildCompositorHitTestInfo()) {
2026 return;
2029 const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
2030 if (info != CompositorHitTestInvisibleToHit) {
2031 aList->AppendNewToTop<nsDisplayCompositorHitTestInfo>(this, aFrame);
2035 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem* aItem) {
2036 mReuseableItems.Insert(aItem);
2039 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem* aItem) {
2040 MOZ_ASSERT(aItem->IsReusedItem());
2041 mReuseableItems.Remove(aItem);
2044 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2045 const size_t total = mReuseableItems.Count();
2047 size_t reused = 0;
2048 for (auto* item : mReuseableItems) {
2049 if (item->IsReusedItem()) {
2050 reused++;
2051 item->SetReusable();
2052 } else {
2053 item->Destroy(this);
2057 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
2058 mReuseableItems.Clear();
2061 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
2062 const auto* previous = mCurrentContainerASR;
2063 const auto* asr = aItem->GetActiveScrolledRoot();
2064 mCurrentContainerASR =
2065 ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
2067 if (previous != mCurrentContainerASR) {
2068 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
2069 mCurrentContainerASR);
2072 aItem->SetReusedItem();
2075 void nsDisplayListSet::CopyTo(const nsDisplayListSet& aDestination) const {
2076 for (size_t i = 0; i < mLists.size(); ++i) {
2077 auto* from = mLists[i];
2078 auto* to = aDestination.mLists[i];
2080 from->CopyTo(to);
2084 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
2085 aDestination.BorderBackground()->AppendToTop(BorderBackground());
2086 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2087 aDestination.Floats()->AppendToTop(Floats());
2088 aDestination.Content()->AppendToTop(Content());
2089 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
2090 aDestination.Outlines()->AppendToTop(Outlines());
2093 nsRect nsDisplayList::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2094 nsRect bounds;
2095 for (nsDisplayItem* i : *this) {
2096 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
2098 return bounds;
2101 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR(
2102 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
2103 nsRect* aBuildingRect) const {
2104 nsRect bounds;
2105 for (nsDisplayItem* i : *this) {
2106 nsRect r = i->GetClippedBounds(aBuilder);
2107 if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
2108 if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) {
2109 r = clip.ref();
2112 if (aBuildingRect) {
2113 aBuildingRect->UnionRect(*aBuildingRect, i->GetBuildingRect());
2115 bounds.UnionRect(bounds, r);
2117 return bounds;
2120 nsRect nsDisplayList::GetBuildingRect() const {
2121 nsRect result;
2122 for (nsDisplayItem* i : *this) {
2123 result.UnionRect(result, i->GetBuildingRect());
2125 return result;
2128 WindowRenderer* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView** aView) {
2129 if (aView) {
2130 *aView = RootReferenceFrame()->GetView();
2132 if (RootReferenceFrame() !=
2133 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2134 return nullptr;
2136 nsIWidget* window = RootReferenceFrame()->GetNearestWidget();
2137 if (window) {
2138 return window->GetWindowRenderer();
2140 return nullptr;
2143 WebRenderLayerManager* nsDisplayListBuilder::GetWidgetLayerManager(
2144 nsView** aView) {
2145 WindowRenderer* renderer = GetWidgetWindowRenderer();
2146 if (renderer) {
2147 return renderer->AsWebRender();
2149 return nullptr;
2152 void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2153 int32_t aAppUnitsPerDevPixel) {
2154 FlattenedDisplayListIterator iter(aBuilder, this);
2155 while (iter.HasNext()) {
2156 nsPaintedDisplayItem* item = iter.GetNextItem()->AsPaintedDisplayItem();
2157 if (!item) {
2158 continue;
2161 nsRect visible = item->GetClippedBounds(aBuilder);
2162 visible = visible.Intersect(item->GetPaintRect(aBuilder, aCtx));
2163 if (visible.IsEmpty()) {
2164 continue;
2167 DisplayItemClip currentClip = item->GetClip();
2168 if (currentClip.HasClip()) {
2169 aCtx->Save();
2170 if (currentClip.IsRectClippedByRoundedCorner(visible)) {
2171 currentClip.ApplyTo(aCtx, aAppUnitsPerDevPixel);
2172 } else {
2173 currentClip.ApplyRectTo(aCtx, aAppUnitsPerDevPixel);
2176 aCtx->NewPath();
2178 item->Paint(aBuilder, aCtx);
2180 if (currentClip.HasClip()) {
2181 aCtx->Restore();
2187 * We paint by executing a layer manager transaction, constructing a
2188 * single layer representing the display list, and then making it the
2189 * root of the layer manager, drawing into the PaintedLayers.
2191 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2192 uint32_t aFlags,
2193 Maybe<double> aDisplayListBuildTime) {
2194 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS);
2196 RefPtr<WebRenderLayerManager> layerManager;
2197 WindowRenderer* renderer = nullptr;
2198 bool widgetTransaction = false;
2199 bool doBeginTransaction = true;
2200 nsView* view = nullptr;
2201 if (aFlags & PAINT_USE_WIDGET_LAYERS) {
2202 renderer = aBuilder->GetWidgetWindowRenderer(&view);
2203 if (renderer) {
2204 // The fallback renderer doesn't retain any content, so it's
2205 // not meaningful to use it when drawing to an external context.
2206 if (aCtx && renderer->AsFallback()) {
2207 MOZ_ASSERT(!(aFlags & PAINT_EXISTING_TRANSACTION));
2208 renderer = nullptr;
2209 } else {
2210 layerManager = renderer->AsWebRender();
2211 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
2212 widgetTransaction = true;
2217 nsIFrame* frame = aBuilder->RootReferenceFrame();
2218 nsPresContext* presContext = frame->PresContext();
2219 PresShell* presShell = presContext->PresShell();
2220 Document* document = presShell->GetDocument();
2222 ScopeExit g([&]() {
2223 #ifdef DEBUG
2224 MOZ_ASSERT(!layerManager || !layerManager->GetTarget());
2225 #endif
2227 // For layers-free mode, we check the invalidation state bits in the
2228 // EndTransaction. So we clear the invalidation state bits after
2229 // EndTransaction.
2230 if (widgetTransaction ||
2231 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2232 // but they still need the invalidation state bits cleared in order for
2233 // invalidation for CSS/SMIL animation to work properly.
2234 (document && document->IsBeingUsedAsImage())) {
2235 DL_LOGD("Clearing invalidation state bits");
2236 frame->ClearInvalidationStateBits();
2240 if (!renderer) {
2241 if (!aCtx) {
2242 NS_WARNING("Nowhere to paint into");
2243 return;
2245 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(false);
2246 Paint(aBuilder, aCtx, presContext->AppUnitsPerDevPixel());
2248 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2249 return;
2252 if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) {
2253 MOZ_ASSERT(layerManager);
2254 if (doBeginTransaction) {
2255 if (aCtx) {
2256 if (!layerManager->BeginTransactionWithTarget(aCtx, nsCString())) {
2257 return;
2259 } else {
2260 if (!layerManager->BeginTransaction(nsCString())) {
2261 return;
2266 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(true);
2267 layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
2269 bool sent = false;
2270 if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
2271 MOZ_ASSERT(!aCtx);
2272 sent = layerManager->EndEmptyTransaction();
2275 if (!sent) {
2276 auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
2278 nsIDocShell* docShell = presContext->GetDocShell();
2279 WrFiltersHolder wrFilters;
2280 gfx::Matrix5x4* colorMatrix =
2281 nsDocShell::Cast(docShell)->GetColorMatrix();
2282 if (colorMatrix) {
2283 wrFilters.filters.AppendElement(
2284 wr::FilterOp::ColorMatrix(colorMatrix->components));
2287 wrManager->EndTransactionWithoutLayer(this, aBuilder,
2288 std::move(wrFilters), nullptr,
2289 aDisplayListBuildTime.valueOr(0.0));
2292 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2293 if (presContext->RefreshDriver()->HasScheduleFlush()) {
2294 presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2295 frame->GetRect());
2298 return;
2301 FallbackRenderer* fallback = renderer->AsFallback();
2302 MOZ_ASSERT(fallback);
2304 if (doBeginTransaction) {
2305 MOZ_ASSERT(!aCtx);
2306 if (!fallback->BeginTransaction()) {
2307 return;
2311 bool temp = aBuilder->SetIsCompositingCheap(renderer->IsCompositingCheap());
2312 fallback->EndTransactionWithList(aBuilder, this,
2313 presContext->AppUnitsPerDevPixel(),
2314 WindowRenderer::END_DEFAULT);
2316 aBuilder->SetIsCompositingCheap(temp);
2319 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
2320 for (auto* item : TakeItems()) {
2321 item->Destroy(aBuilder);
2325 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) {
2326 return aFrame->Style()->PointerEvents() != StylePointerEvents::None;
2329 // A list of frames, and their z depth. Used for sorting
2330 // the results of hit testing.
2331 struct FramesWithDepth {
2332 explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {}
2334 bool operator<(const FramesWithDepth& aOther) const {
2335 if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
2336 // We want to sort so that the shallowest item (highest depth value) is
2337 // first
2338 return mDepth > aOther.mDepth;
2340 return false;
2342 bool operator==(const FramesWithDepth& aOther) const {
2343 return this == &aOther;
2346 float mDepth;
2347 nsTArray<nsIFrame*> mFrames;
2350 // Sort the frames by depth and then moves all the contained frames to the
2351 // destination
2352 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource,
2353 nsTArray<nsIFrame*>* aDest) {
2354 if (aSource.IsEmpty()) {
2355 return;
2357 aSource.StableSort();
2358 uint32_t length = aSource.Length();
2359 for (uint32_t i = 0; i < length; i++) {
2360 aDest->AppendElements(std::move(aSource[i].mFrames));
2362 aSource.Clear();
2365 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2366 nsDisplayItem::HitTestState* aState,
2367 nsTArray<nsIFrame*>* aOutFrames) const {
2368 nsDisplayItem* item;
2370 if (aState->mInPreserves3D) {
2371 // Collect leaves of the current 3D rendering context.
2372 for (nsDisplayItem* item : *this) {
2373 auto itemType = item->GetType();
2374 if (itemType != DisplayItemType::TYPE_TRANSFORM ||
2375 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2376 item->HitTest(aBuilder, aRect, aState, aOutFrames);
2377 } else {
2378 // One of leaves in the current 3D rendering context.
2379 aState->mItemBuffer.AppendElement(item);
2382 return;
2385 int32_t itemBufferStart = aState->mItemBuffer.Length();
2386 for (nsDisplayItem* item : *this) {
2387 aState->mItemBuffer.AppendElement(item);
2390 AutoTArray<FramesWithDepth, 16> temp;
2391 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart;
2392 --i) {
2393 // Pop element off the end of the buffer. We want to shorten the buffer
2394 // so that recursive calls to HitTest have more buffer space.
2395 item = aState->mItemBuffer[i];
2396 aState->mItemBuffer.SetLength(i);
2398 bool snap;
2399 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
2400 auto itemType = item->GetType();
2401 bool same3DContext =
2402 (itemType == DisplayItemType::TYPE_TRANSFORM &&
2403 static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
2404 (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
2405 item->Frame()->Extend3DContext());
2406 if (same3DContext &&
2407 (itemType != DisplayItemType::TYPE_TRANSFORM ||
2408 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
2409 if (!item->GetClip().MayIntersect(aRect)) {
2410 continue;
2412 AutoTArray<nsIFrame*, 1> neverUsed;
2413 // Start gathering leaves of the 3D rendering context, and
2414 // append leaves at the end of mItemBuffer. Leaves are
2415 // processed at following iterations.
2416 aState->mInPreserves3D = true;
2417 item->HitTest(aBuilder, aRect, aState, &neverUsed);
2418 aState->mInPreserves3D = false;
2419 i = aState->mItemBuffer.Length();
2420 continue;
2422 if (same3DContext || item->GetClip().MayIntersect(r)) {
2423 AutoTArray<nsIFrame*, 16> outFrames;
2424 item->HitTest(aBuilder, aRect, aState, &outFrames);
2426 // For 3d transforms with preserve-3d we add hit frames into the temp list
2427 // so we can sort them later, otherwise we add them directly to the output
2428 // list.
2429 nsTArray<nsIFrame*>* writeFrames = aOutFrames;
2430 if (item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
2431 static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2432 if (outFrames.Length()) {
2433 nsDisplayTransform* transform =
2434 static_cast<nsDisplayTransform*>(item);
2435 nsPoint point = aRect.TopLeft();
2436 // A 1x1 rect means a point, otherwise use the center of the rect
2437 if (aRect.width != 1 || aRect.height != 1) {
2438 point = aRect.Center();
2440 temp.AppendElement(
2441 FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
2442 writeFrames = &temp[temp.Length() - 1].mFrames;
2444 } else {
2445 // We may have just finished a run of consecutive preserve-3d
2446 // transforms, so flush these into the destination array before
2447 // processing our frame list.
2448 FlushFramesArray(temp, aOutFrames);
2451 for (uint32_t j = 0; j < outFrames.Length(); j++) {
2452 nsIFrame* f = outFrames.ElementAt(j);
2453 // Filter out some frames depending on the type of hittest
2454 // we are doing. For visibility tests, pass through all frames.
2455 // For pointer tests, only pass through frames that are styled
2456 // to receive pointer events.
2457 if (aBuilder->HitTestIsForVisibility() ||
2458 IsFrameReceivingPointerEvents(f)) {
2459 writeFrames->AppendElement(f);
2463 if (aBuilder->HitTestIsForVisibility()) {
2464 aState->mHitOccludingItem = [&] {
2465 if (aState->mHitOccludingItem) {
2466 // We already hit something before.
2467 return true;
2469 if (aState->mCurrentOpacity == 1.0f &&
2470 item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) {
2471 // An opaque item always occludes everything. Note that we need to
2472 // check wrapping opacity and such as well.
2473 return true;
2475 float threshold = aBuilder->VisibilityThreshold();
2476 if (threshold == 1.0f) {
2477 return false;
2479 float itemOpacity = [&] {
2480 switch (item->GetType()) {
2481 case DisplayItemType::TYPE_OPACITY:
2482 return static_cast<nsDisplayOpacity*>(item)->GetOpacity();
2483 case DisplayItemType::TYPE_BACKGROUND_COLOR:
2484 return static_cast<nsDisplayBackgroundColor*>(item)
2485 ->GetOpacity();
2486 default:
2487 // Be conservative and assume it won't occlude other items.
2488 return 0.0f;
2490 }();
2491 return itemOpacity * aState->mCurrentOpacity >= threshold;
2492 }();
2494 if (aState->mHitOccludingItem) {
2495 // We're exiting early, so pop the remaining items off the buffer.
2496 aState->mItemBuffer.TruncateLength(itemBufferStart);
2497 break;
2502 // Clear any remaining preserve-3d transforms.
2503 FlushFramesArray(temp, aOutFrames);
2504 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
2505 "How did we forget to pop some elements?");
2508 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, Document* aDoc) {
2509 nsIFrame* f = aItem->Frame();
2510 while (f) {
2511 nsPresContext* pc = f->PresContext();
2512 if (pc->Document() == aDoc) {
2513 return f->GetContent();
2515 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(
2516 pc->PresShell()->GetRootFrame());
2518 return nullptr;
2521 struct ZSortItem {
2522 nsDisplayItem* item;
2523 int32_t zIndex;
2525 explicit ZSortItem(nsDisplayItem* aItem)
2526 : item(aItem), zIndex(aItem->ZIndex()) {}
2528 operator nsDisplayItem*() { return item; }
2531 struct ZOrderComparator {
2532 bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const {
2533 // Note that we can't just take the difference of the two
2534 // z-indices here, because that might overflow a 32-bit int.
2535 return aLeft.zIndex < aRight.zIndex;
2539 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); }
2541 struct ContentComparator {
2542 nsIContent* mCommonAncestor;
2544 explicit ContentComparator(nsIContent* aCommonAncestor)
2545 : mCommonAncestor(aCommonAncestor) {}
2547 bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const {
2548 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2549 // subdocument of commonAncestor, because display items for subdocuments
2550 // have been mixed into the same list. Ensure that we're looking at content
2551 // in commonAncestor's document.
2552 Document* commonAncestorDoc = mCommonAncestor->OwnerDoc();
2553 nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc);
2554 nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc);
2555 if (!content1 || !content2) {
2556 NS_ERROR("Document trees are mixed up!");
2557 // Something weird going on
2558 return true;
2560 return nsContentUtils::CompareTreePosition<TreeKind::Flat>(
2561 content1, content2, mCommonAncestor) < 0;
2565 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) {
2566 Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor));
2569 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2570 static_assert(sizeof(nsDisplayItem) <= 176, "nsDisplayItem has grown");
2571 #endif
2573 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2574 : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
2576 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2577 const ActiveScrolledRoot* aActiveScrolledRoot)
2578 : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) {
2579 MOZ_COUNT_CTOR(nsDisplayItem);
2580 MOZ_ASSERT(mFrame);
2581 if (aBuilder->IsRetainingDisplayList()) {
2582 mFrame->AddDisplayItem(this);
2585 aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
2586 NS_ASSERTION(
2587 aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
2588 "visible rect not set");
2590 mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2592 // The visible rect is for mCurrentFrame, so we have to use
2593 // mCurrentOffsetToReferenceFrame
2594 nsRect visible = aBuilder->GetVisibleRect() +
2595 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
2596 SetBuildingRect(visible);
2598 const nsStyleDisplay* disp = mFrame->StyleDisplay();
2599 if (mFrame->BackfaceIsHidden(disp)) {
2600 mItemFlags += ItemFlag::BackfaceHidden;
2602 if (mFrame->Combines3DTransformWithAncestors()) {
2603 mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
2607 void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
2609 bool nsDisplayItem::HasDeletedFrame() const {
2610 bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) ||
2611 (GetType() == DisplayItemType::TYPE_REMOTE &&
2612 !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader());
2613 MOZ_ASSERT(retval || mFrame);
2614 return retval;
2617 /* static */
2618 bool nsDisplayItem::ForceActiveLayers() {
2619 return StaticPrefs::layers_force_active();
2622 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); }
2624 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
2625 bool aStore) {
2626 mClipChain = aClipChain;
2629 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
2630 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2631 if (const DisplayItemClip* clip =
2632 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
2633 return Some(clip->GetClipRect());
2635 #ifdef DEBUG
2636 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
2637 #endif
2638 return Nothing();
2641 const DisplayItemClip& nsDisplayItem::GetClip() const {
2642 const DisplayItemClip* clip =
2643 DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot);
2644 return clip ? *clip : DisplayItemClip::NoClip();
2647 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
2648 const DisplayItemClipChain* aOther,
2649 bool aStore) {
2650 if (!aOther || mClipChain == aOther) {
2651 return;
2654 // aOther might be a reference to a clip on the stack. We need to make sure
2655 // that CreateClipChainIntersection will allocate the actual intersected
2656 // clip in the builder's arena, so for the mClipChain == nullptr case,
2657 // we supply nullptr as the common ancestor so that
2658 // CreateClipChainIntersection clones the whole chain.
2659 const DisplayItemClipChain* ancestorClip =
2660 mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther)
2661 : nullptr;
2663 SetClipChain(
2664 aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther),
2665 aStore);
2668 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2669 bool snap;
2670 nsRect r = GetBounds(aBuilder, &snap);
2671 return GetClip().ApplyNonRoundedIntersection(r);
2674 nsDisplayContainer::nsDisplayContainer(
2675 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2676 const ActiveScrolledRoot* aActiveScrolledRoot, nsDisplayList* aList)
2677 : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
2678 mChildren(aBuilder) {
2679 MOZ_COUNT_CTOR(nsDisplayContainer);
2680 mChildren.AppendToTop(aList);
2681 UpdateBounds(aBuilder);
2683 // Clear and store the clip chain set by nsDisplayItem constructor.
2684 nsDisplayItem::SetClipChain(nullptr, true);
2687 nsRect nsDisplayItem::GetPaintRect(nsDisplayListBuilder* aBuilder,
2688 gfxContext* aCtx) {
2689 bool dummy;
2690 nsRect result = GetBounds(aBuilder, &dummy);
2691 if (aCtx) {
2692 result.IntersectRect(result,
2693 nsLayoutUtils::RoundGfxRectToAppRect(
2694 aCtx->GetClipExtents(),
2695 mFrame->PresContext()->AppUnitsPerDevPixel()));
2697 return result;
2700 bool nsDisplayContainer::CreateWebRenderCommands(
2701 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2702 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2703 nsDisplayListBuilder* aDisplayListBuilder) {
2704 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2705 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
2706 false);
2707 return true;
2710 nsRect nsDisplayContainer::GetBounds(nsDisplayListBuilder* aBuilder,
2711 bool* aSnap) const {
2712 *aSnap = false;
2713 return mBounds;
2716 nsRect nsDisplayContainer::GetComponentAlphaBounds(
2717 nsDisplayListBuilder* aBuilder) const {
2718 return mChildren.GetComponentAlphaBounds(aBuilder);
2721 static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2722 nsDisplayList* aList,
2723 const nsRect& aListBounds) {
2724 return aList->GetOpaqueRegion(aBuilder);
2727 nsRegion nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2728 bool* aSnap) const {
2729 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
2730 GetBounds(aBuilder, aSnap));
2733 Maybe<nsRect> nsDisplayContainer::GetClipWithRespectToASR(
2734 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2735 // Our children should have finite bounds with respect to |aASR|.
2736 if (aASR == mActiveScrolledRoot) {
2737 return Some(mBounds);
2740 return Some(mChildren.GetClippedBoundsWithRespectToASR(aBuilder, aASR));
2743 void nsDisplayContainer::HitTest(nsDisplayListBuilder* aBuilder,
2744 const nsRect& aRect, HitTestState* aState,
2745 nsTArray<nsIFrame*>* aOutFrames) {
2746 mChildren.HitTest(aBuilder, aRect, aState, aOutFrames);
2749 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder* aBuilder) {
2750 // Container item bounds are expected to be clipped.
2751 mBounds =
2752 mChildren.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
2755 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder,
2756 bool* aSnap) const {
2757 *aSnap = true;
2758 return mBounds;
2761 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
2762 gfxContext* aCtx) {
2763 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2764 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2765 Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx),
2766 appUnitsPerDevPixel, *drawTarget);
2767 drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
2770 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) {
2771 aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
2772 << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
2773 << ")";
2776 bool nsDisplaySolidColor::CreateWebRenderCommands(
2777 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2778 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2779 nsDisplayListBuilder* aDisplayListBuilder) {
2780 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
2781 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
2782 wr::LayoutRect r = wr::ToLayoutRect(bounds);
2783 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, mIsCheckerboardBackground,
2784 wr::ToColorF(ToDeviceColor(mColor)));
2786 return true;
2789 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
2790 bool* aSnap) const {
2791 *aSnap = true;
2792 return mRegion.GetBounds();
2795 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
2796 gfxContext* aCtx) {
2797 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2798 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2799 ColorPattern color(ToDeviceColor(mColor));
2800 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2801 Rect rect =
2802 NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
2803 drawTarget->FillRect(rect, color);
2807 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) {
2808 aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255)
2809 << "," << int(mColor.b * 255) << "," << mColor.a << ")";
2812 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2813 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2814 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2815 nsDisplayListBuilder* aDisplayListBuilder) {
2816 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2817 nsRect rect = iter.Get();
2818 LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
2819 rect, mFrame->PresContext()->AppUnitsPerDevPixel());
2820 wr::LayoutRect r = wr::ToLayoutRect(layerRects);
2821 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
2822 wr::ToColorF(ToDeviceColor(mColor)));
2825 return true;
2828 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder,
2829 nsDisplayItem* aItem, nsIFrame* aFrame,
2830 nsITheme::ThemeGeometryType aType) {
2831 if (aBuilder->IsInChromeDocumentOrPopup()) {
2832 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2833 bool preservesAxisAlignedRectangles = false;
2834 nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(
2835 aFrame, aFrame->GetRectRelativeToSelf(), displayRoot,
2836 &preservesAxisAlignedRectangles);
2837 if (preservesAxisAlignedRectangles) {
2838 aBuilder->RegisterThemeGeometry(
2839 aType, aItem,
2840 LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels(
2841 aFrame->PresContext()->AppUnitsPerDevPixel())));
2846 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2847 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
2848 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
2849 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
2850 nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
2851 nsRect rootRect = rootFrame->GetRectRelativeToSelf();
2852 if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) ==
2853 nsLayoutUtils::TRANSFORM_SUCCEEDED) {
2854 return Some(rootRect + aBuilder->ToReferenceFrame(aFrame));
2856 return Nothing();
2859 /* static */ nsDisplayBackgroundImage::InitData
2860 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder,
2861 nsIFrame* aFrame, uint16_t aLayer,
2862 const nsRect& aBackgroundRect,
2863 const ComputedStyle* aBackgroundStyle) {
2864 nsPresContext* presContext = aFrame->PresContext();
2865 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2866 const nsStyleImageLayers::Layer& layer =
2867 aBackgroundStyle->StyleBackground()->mImage.mLayers[aLayer];
2869 bool isTransformedFixed;
2870 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
2871 presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer,
2872 &isTransformedFixed);
2874 // background-attachment:fixed is treated as background-attachment:scroll
2875 // if it's affected by a transform.
2876 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2877 bool shouldTreatAsFixed =
2878 layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2879 !isTransformedFixed;
2881 bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsNone();
2882 bool isRasterImage = state.mImageRenderer.IsRasterImage();
2883 nsCOMPtr<imgIContainer> image;
2884 if (isRasterImage) {
2885 image = state.mImageRenderer.GetImage();
2887 return InitData{aBuilder, aBackgroundStyle, image,
2888 aBackgroundRect, state.mFillArea, state.mDestArea,
2889 aLayer, isRasterImage, shouldFixToViewport};
2892 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2893 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aInitData,
2894 nsIFrame* aFrameForBounds)
2895 : nsPaintedDisplayItem(aBuilder, aFrame),
2896 mBackgroundStyle(aInitData.backgroundStyle),
2897 mImage(aInitData.image),
2898 mDependentFrame(nullptr),
2899 mBackgroundRect(aInitData.backgroundRect),
2900 mFillRect(aInitData.fillArea),
2901 mDestRect(aInitData.destArea),
2902 mLayer(aInitData.layer),
2903 mIsRasterImage(aInitData.isRasterImage),
2904 mShouldFixToViewport(aInitData.shouldFixToViewport) {
2905 MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
2906 #ifdef DEBUG
2907 if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) {
2908 // If this changes, then you also need to adjust css::ImageLoader to
2909 // invalidate mFrame as needed.
2910 MOZ_ASSERT(mFrame->IsCanvasFrame() || mFrame->IsTablePart());
2912 #endif
2914 mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
2915 if (mShouldFixToViewport) {
2916 // Expand the item's visible rect to cover the entire bounds, limited to the
2917 // viewport rect. This is necessary because the background's clip can move
2918 // asynchronously.
2919 if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
2920 aInitData.builder, mFrame)) {
2921 SetBuildingRect(mBounds.Intersect(*viewportRect));
2926 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2927 MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
2928 if (mDependentFrame) {
2929 mDependentFrame->RemoveDisplayItem(this);
2933 static void SetBackgroundClipRegion(
2934 DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame,
2935 const nsStyleImageLayers::Layer& aLayer, const nsRect& aBackgroundRect,
2936 bool aWillPaintBorder) {
2937 nsCSSRendering::ImageLayerClipState clip;
2938 nsCSSRendering::GetImageLayerClip(
2939 aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect,
2940 aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
2942 if (clip.mHasAdditionalBGClipArea) {
2943 aClipState.ClipContentDescendants(
2944 clip.mAdditionalBGClipArea, clip.mBGClipArea,
2945 clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2946 } else {
2947 aClipState.ClipContentDescendants(
2948 clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2953 * This is used for the find bar highlighter overlay. It's only accessible
2954 * through the AnonymousContent API, so it's not exposed to general web pages.
2956 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
2957 nsIFrame* aFrame,
2958 const nsRect& aBackgroundRect,
2959 nsDisplayList* aList, nscolor aColor) {
2960 nsIContent* content = aFrame->GetContent();
2961 if (!content) {
2962 return false;
2965 void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
2966 if (!cutoutRegion) {
2967 return false;
2970 if (NS_GET_A(aColor) == 0) {
2971 return true;
2974 nsRegion region;
2975 region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
2976 region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
2977 aList->AppendNewToTop<nsDisplaySolidColorRegion>(aBuilder, aFrame, region,
2978 aColor);
2980 return true;
2983 enum class TableType : uint8_t {
2984 Table,
2985 TableCol,
2986 TableColGroup,
2987 TableRow,
2988 TableRowGroup,
2989 TableCell,
2991 MAX,
2994 enum class TableTypeBits : uint8_t { Count = 3 };
2996 static_assert(static_cast<uint8_t>(TableType::MAX) <
2997 (1 << (static_cast<uint8_t>(TableTypeBits::Count) + 1)),
2998 "TableType cannot fit with TableTypeBits::Count");
2999 TableType GetTableTypeFromFrame(nsIFrame* aFrame);
3001 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex,
3002 const TableType aType) {
3003 const uint32_t key = (aIndex << static_cast<uint8_t>(TableTypeBits::Count)) |
3004 static_cast<uint8_t>(aType);
3006 return static_cast<uint16_t>(key);
3009 static nsDisplayBackgroundImage* CreateBackgroundImage(
3010 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3011 const nsDisplayBackgroundImage::InitData& aBgData) {
3012 const auto index = aBgData.layer;
3014 if (aSecondaryFrame) {
3015 const auto tableType = GetTableTypeFromFrame(aFrame);
3016 const uint16_t tableItemIndex = CalculateTablePerFrameKey(index, tableType);
3018 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundImage>(
3019 aBuilder, aSecondaryFrame, tableItemIndex, aBgData, aFrame);
3022 return MakeDisplayItemWithIndex<nsDisplayBackgroundImage>(aBuilder, aFrame,
3023 index, aBgData);
3026 static nsDisplayThemedBackground* CreateThemedBackground(
3027 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3028 const nsRect& aBgRect) {
3029 if (aSecondaryFrame) {
3030 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3031 return MakeDisplayItemWithIndex<nsDisplayTableThemedBackground>(
3032 aBuilder, aSecondaryFrame, index, aBgRect, aFrame);
3035 return MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, aBgRect);
3038 static nsDisplayBackgroundColor* CreateBackgroundColor(
3039 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3040 nsRect& aBgRect, const ComputedStyle* aBgSC, nscolor aColor) {
3041 if (aSecondaryFrame) {
3042 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3043 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>(
3044 aBuilder, aSecondaryFrame, index, aBgRect, aBgSC, aColor, aFrame);
3047 return MakeDisplayItem<nsDisplayBackgroundColor>(aBuilder, aFrame, aBgRect,
3048 aBgSC, aColor);
3051 static void DealWithWindowsAppearanceHacks(nsIFrame* aFrame,
3052 nsDisplayListBuilder* aBuilder) {
3053 const auto& disp = *aFrame->StyleDisplay();
3055 // We use default appearance rather than effective appearance because we want
3056 // to handle when titlebar buttons that have appearance: none.
3057 const auto defaultAppearance = disp.mDefaultAppearance;
3058 if (MOZ_LIKELY(defaultAppearance == StyleAppearance::None)) {
3059 return;
3062 if (auto type = disp.GetWindowButtonType()) {
3063 if (auto* widget = aFrame->GetNearestWidget()) {
3064 auto rect = LayoutDevicePixel::FromAppUnitsToNearest(
3065 nsRect(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize()),
3066 aFrame->PresContext()->AppUnitsPerDevPixel());
3067 widget->SetWindowButtonRect(*type, rect);
3072 /*static*/
3073 AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3074 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3075 const nsRect& aBackgroundRect, nsDisplayList* aList,
3076 bool aAllowWillPaintBorderOptimization, const nsRect& aBackgroundOriginRect,
3077 nsIFrame* aSecondaryReferenceFrame,
3078 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
3079 aAutoBuildingDisplayList) {
3080 MOZ_ASSERT(!aFrame->IsCanvasFrame(),
3081 "We don't expect propagated canvas backgrounds here");
3082 #ifdef DEBUG
3084 nsIFrame* bgFrame = nsCSSRendering::FindBackgroundFrame(aFrame);
3085 MOZ_ASSERT(
3086 !bgFrame || bgFrame == aFrame,
3087 "Should only suppress backgrounds, never propagate to another frame");
3089 #endif
3091 DealWithWindowsAppearanceHacks(aFrame, aBuilder);
3093 const bool isThemed = aFrame->IsThemed();
3095 const ComputedStyle* bgSC = aFrame->Style();
3096 const nsStyleBackground* bg = bgSC->StyleBackground();
3097 const bool needsBackgroundColor =
3098 aBuilder->IsForEventDelivery() ||
3099 (EffectCompositor::HasAnimationsForCompositor(
3100 aFrame, DisplayItemType::TYPE_BACKGROUND_COLOR) &&
3101 !isThemed);
3102 if (!needsBackgroundColor && !isThemed && bg->IsTransparent(bgSC)) {
3103 return AppendedBackgroundType::None;
3106 bool drawBackgroundColor = false;
3107 bool drawBackgroundImage = false;
3108 nscolor color = NS_RGBA(0, 0, 0, 0);
3109 // Don't get background color / images if we propagated our background to the
3110 // canvas (that is, if FindBackgroundFrame is null). But don't early return
3111 // yet, since we might still need a background-color item for hit-testing.
3112 if (!isThemed && nsCSSRendering::FindBackgroundFrame(aFrame)) {
3113 color = nsCSSRendering::DetermineBackgroundColor(
3114 aFrame->PresContext(), bgSC, aFrame, drawBackgroundImage,
3115 drawBackgroundColor);
3118 if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
3119 color)) {
3120 return AppendedBackgroundType::None;
3123 const nsStyleBorder& border = *aFrame->StyleBorder();
3124 const bool willPaintBorder =
3125 aAllowWillPaintBorderOptimization && !isThemed &&
3126 !aFrame->StyleEffects()->HasBoxShadowWithInset(true) &&
3127 border.HasBorder();
3129 auto EnsureBuildingDisplayList = [&] {
3130 if (!aAutoBuildingDisplayList || *aAutoBuildingDisplayList) {
3131 return;
3133 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3134 aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3135 aBuilder->GetVisibleRect() + offset,
3136 aBuilder->GetDirtyRect() + offset);
3139 // An auxiliary list is necessary in case we have background blending; if that
3140 // is the case, background items need to be wrapped by a blend container to
3141 // isolate blending to the background
3142 nsDisplayList bgItemList(aBuilder);
3143 // Even if we don't actually have a background color to paint, we may still
3144 // need to create an item for hit testing and we still need to create an item
3145 // for background-color animations.
3146 if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) ||
3147 needsBackgroundColor) {
3148 EnsureBuildingDisplayList();
3149 Maybe<DisplayListClipState::AutoSaveRestore> clipState;
3150 nsRect bgColorRect = aBackgroundRect;
3151 if (!isThemed && !aBuilder->IsForEventDelivery()) {
3152 // Disable the will-paint-border optimization for background
3153 // colors with no border-radius. Enabling it for background colors
3154 // doesn't help much (there are no tiling issues) and clipping the
3155 // background breaks detection of the element's border-box being
3156 // opaque. For nonzero border-radius we still need it because we
3157 // want to inset the background if possible to avoid antialiasing
3158 // artifacts along the rounded corners.
3159 const bool useWillPaintBorderOptimization =
3160 willPaintBorder &&
3161 nsLayoutUtils::HasNonZeroCorner(border.mBorderRadius);
3163 nsCSSRendering::ImageLayerClipState clip;
3164 nsCSSRendering::GetImageLayerClip(
3165 bg->BottomLayer(), aFrame, border, aBackgroundRect, aBackgroundRect,
3166 useWillPaintBorderOptimization,
3167 aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3169 bgColorRect = bgColorRect.Intersect(clip.mBGClipArea);
3170 if (clip.mHasAdditionalBGClipArea) {
3171 bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea);
3173 if (clip.mHasRoundedCorners) {
3174 clipState.emplace(aBuilder);
3175 clipState->ClipContentDescendants(clip.mBGClipArea, clip.mRadii);
3179 nsDisplayBackgroundColor* bgItem = CreateBackgroundColor(
3180 aBuilder, aFrame, aSecondaryReferenceFrame, bgColorRect, bgSC,
3181 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0));
3183 if (bgItem) {
3184 bgItemList.AppendToTop(bgItem);
3188 if (isThemed) {
3189 nsDisplayThemedBackground* bgItem = CreateThemedBackground(
3190 aBuilder, aFrame, aSecondaryReferenceFrame, aBackgroundRect);
3192 if (bgItem) {
3193 bgItem->Init(aBuilder);
3194 bgItemList.AppendToTop(bgItem);
3197 if (!bgItemList.IsEmpty()) {
3198 aList->AppendToTop(&bgItemList);
3199 return AppendedBackgroundType::ThemedBackground;
3202 return AppendedBackgroundType::None;
3205 if (!drawBackgroundImage) {
3206 if (!bgItemList.IsEmpty()) {
3207 aList->AppendToTop(&bgItemList);
3208 return AppendedBackgroundType::Background;
3211 return AppendedBackgroundType::None;
3214 const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
3216 bool needBlendContainer = false;
3217 const nsRect& bgOriginRect =
3218 aBackgroundOriginRect.IsEmpty() ? aBackgroundRect : aBackgroundOriginRect;
3220 // Passing bg == nullptr in this macro will result in one iteration with
3221 // i = 0.
3222 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
3223 if (bg->mImage.mLayers[i].mImage.IsNone()) {
3224 continue;
3227 EnsureBuildingDisplayList();
3229 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3230 needBlendContainer = true;
3233 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3234 if (!aBuilder->IsForEventDelivery()) {
3235 const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
3236 SetBackgroundClipRegion(clipState, aFrame, layer, aBackgroundRect,
3237 willPaintBorder);
3240 nsDisplayList thisItemList(aBuilder);
3241 nsDisplayBackgroundImage::InitData bgData =
3242 nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect,
3243 bgSC);
3245 if (bgData.shouldFixToViewport) {
3246 auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
3247 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3248 aBuilder, aFrame, aBuilder->GetVisibleRect(),
3249 aBuilder->GetDirtyRect());
3251 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
3252 aBuilder);
3253 if (displayData) {
3254 asrSetter.SetCurrentActiveScrolledRoot(
3255 displayData->mContainingBlockActiveScrolledRoot);
3256 asrSetter.SetCurrentScrollParentId(displayData->mScrollParentId);
3257 if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
3258 // Override the dirty rect on the builder to be the dirty rect of
3259 // the viewport.
3260 // displayData->mDirtyRect is relative to the presshell's viewport
3261 // frame (the root frame), and we need it to be relative to aFrame.
3262 nsIFrame* rootFrame =
3263 aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
3264 // There cannot be any transforms between aFrame and rootFrame
3265 // because then bgData.shouldFixToViewport would have been false.
3266 nsRect visibleRect =
3267 displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame);
3268 aBuilder->SetVisibleRect(visibleRect);
3269 nsRect dirtyRect =
3270 displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
3271 aBuilder->SetDirtyRect(dirtyRect);
3275 nsDisplayBackgroundImage* bgItem = nullptr;
3277 // The clip is captured by the nsDisplayFixedPosition, so clear the
3278 // clip for the nsDisplayBackgroundImage inside.
3279 DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
3280 bgImageClip.Clear();
3281 bgItem = CreateBackgroundImage(aBuilder, aFrame,
3282 aSecondaryReferenceFrame, bgData);
3284 if (bgItem) {
3285 thisItemList.AppendToTop(
3286 nsDisplayFixedPosition::CreateForFixedBackground(
3287 aBuilder, aFrame, aSecondaryReferenceFrame, bgItem, i, asr));
3289 } else { // bgData.shouldFixToViewport == false
3290 nsDisplayBackgroundImage* bgItem = CreateBackgroundImage(
3291 aBuilder, aFrame, aSecondaryReferenceFrame, bgData);
3292 if (bgItem) {
3293 thisItemList.AppendToTop(bgItem);
3297 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3298 // asr is scrolled. Even if we wrap a fixed background layer, that's
3299 // fine, because the item will have a scrolled clip that limits the
3300 // item with respect to asr.
3301 if (aSecondaryReferenceFrame) {
3302 const auto tableType = GetTableTypeFromFrame(aFrame);
3303 const uint16_t index = CalculateTablePerFrameKey(i + 1, tableType);
3305 thisItemList.AppendNewToTopWithIndex<nsDisplayTableBlendMode>(
3306 aBuilder, aSecondaryReferenceFrame, index, &thisItemList,
3307 bg->mImage.mLayers[i].mBlendMode, asr, aFrame, true);
3308 } else {
3309 thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
3310 aBuilder, aFrame, i + 1, &thisItemList,
3311 bg->mImage.mLayers[i].mBlendMode, asr, true);
3314 bgItemList.AppendToTop(&thisItemList);
3317 if (needBlendContainer) {
3318 bgItemList.AppendToTop(
3319 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3320 aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr));
3323 if (!bgItemList.IsEmpty()) {
3324 aList->AppendToTop(&bgItemList);
3325 return AppendedBackgroundType::Background;
3328 return AppendedBackgroundType::None;
3331 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3332 // intersects aRect. Assumes that the unrounded border has already
3333 // been checked for intersection.
3334 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame,
3335 const nsPoint& aFrameToReferenceFrame,
3336 const nsRect& aTestRect) {
3337 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize())
3338 .Intersects(aTestRect)) {
3339 return false;
3342 nscoord radii[8];
3343 return !aFrame->GetBorderRadii(radii) ||
3344 nsLayoutUtils::RoundedRectIntersectsRect(
3345 nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii,
3346 aTestRect);
3349 // Returns TRUE if aContainedRect is guaranteed to be contained in
3350 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3351 // handled conservatively by returning FALSE in some situations where
3352 // a more thorough analysis could return TRUE.
3354 // See also RoundedRectIntersectsRect.
3355 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
3356 const nscoord aRadii[8],
3357 const nsRect& aContainedRect) {
3358 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii,
3359 aContainedRect);
3360 return rgn.Contains(aContainedRect);
3363 bool nsDisplayBackgroundImage::CanApplyOpacity(
3364 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3365 return CanBuildWebRenderDisplayItems(aManager, aBuilder);
3368 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3369 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3370 return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip !=
3371 StyleGeometryBox::Text &&
3372 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3373 aManager, *StyleFrame()->PresContext(), StyleFrame(),
3374 mBackgroundStyle->StyleBackground(), mLayer,
3375 aBuilder->GetBackgroundPaintFlags());
3378 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3379 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3380 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3381 nsDisplayListBuilder* aDisplayListBuilder) {
3382 if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(),
3383 aDisplayListBuilder)) {
3384 return false;
3387 uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
3388 bool dummy;
3389 nsCSSRendering::PaintBGParams params =
3390 nsCSSRendering::PaintBGParams::ForSingleLayer(
3391 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy),
3392 mBackgroundRect, StyleFrame(), paintFlags, mLayer,
3393 CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity());
3394 params.bgClipRect = &mBounds;
3395 ImgDrawResult result =
3396 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3397 params, aBuilder, aResources, aSc, aManager, this);
3398 if (result == ImgDrawResult::NOT_SUPPORTED) {
3399 return false;
3402 if (nsIContent* content = StyleFrame()->GetContent()) {
3403 if (imgRequestProxy* requestProxy = mBackgroundStyle->StyleBackground()
3404 ->mImage.mLayers[mLayer]
3405 .mImage.GetImageRequest()) {
3406 // LCP don't consider gradient backgrounds.
3407 LCPHelpers::FinalizeLCPEntryForImage(content->AsElement(), requestProxy,
3408 mBounds - ToReferenceFrame());
3412 return true;
3415 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
3416 const nsRect& aRect,
3417 HitTestState* aState,
3418 nsTArray<nsIFrame*>* aOutFrames) {
3419 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3420 aOutFrames->AppendElement(mFrame);
3424 static nsRect GetInsideClipRect(const nsDisplayItem* aItem,
3425 StyleGeometryBox aClip, const nsRect& aRect,
3426 const nsRect& aBackgroundRect) {
3427 if (aRect.IsEmpty()) {
3428 return {};
3431 nsIFrame* frame = aItem->Frame();
3433 nsRect clipRect = aBackgroundRect;
3434 if (frame->IsCanvasFrame()) {
3435 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3436 clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
3437 } else if (aClip == StyleGeometryBox::PaddingBox ||
3438 aClip == StyleGeometryBox::ContentBox) {
3439 nsMargin border = frame->GetUsedBorder();
3440 if (aClip == StyleGeometryBox::ContentBox) {
3441 border += frame->GetUsedPadding();
3443 border.ApplySkipSides(frame->GetSkipSides());
3444 clipRect.Deflate(border);
3447 return clipRect.Intersect(aRect);
3450 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
3451 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3452 nsRegion result;
3453 *aSnap = false;
3455 if (!mBackgroundStyle) {
3456 return result;
3459 *aSnap = true;
3461 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3462 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3463 // which expects frames to be sent to it in content order, not reverse
3464 // content order which we'll produce here.
3465 // Of course, if there's only one frame in the flow, it doesn't matter.
3466 if (mFrame->StyleBorder()->mBoxDecorationBreak ==
3467 StyleBoxDecorationBreak::Clone ||
3468 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
3469 const nsStyleImageLayers::Layer& layer =
3470 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3471 if (layer.mImage.IsOpaque() && layer.mBlendMode == StyleBlend::Normal &&
3472 layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
3473 layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
3474 layer.mClip != StyleGeometryBox::Text) {
3475 result = GetInsideClipRect(this, layer.mClip, mBounds, mBackgroundRect);
3479 return result;
3482 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform(
3483 nsDisplayListBuilder* aBuilder) const {
3484 if (!mBackgroundStyle) {
3485 return Some(NS_RGBA(0, 0, 0, 0));
3487 return Nothing();
3490 nsRect nsDisplayBackgroundImage::GetPositioningArea() const {
3491 if (!mBackgroundStyle) {
3492 return nsRect();
3494 nsIFrame* attachedToFrame;
3495 bool transformedFixed;
3496 return nsCSSRendering::ComputeImageLayerPositioningArea(
3497 mFrame->PresContext(), mFrame, mBackgroundRect,
3498 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer],
3499 &attachedToFrame, &transformedFixed) +
3500 ToReferenceFrame();
3503 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3504 const {
3505 if (!mBackgroundStyle) {
3506 return false;
3509 nscoord radii[8];
3510 if (mFrame->GetBorderRadii(radii)) {
3511 // A change in the size of the positioning area might change the position
3512 // of the rounded corners.
3513 return true;
3516 const nsStyleImageLayers::Layer& layer =
3517 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3518 return layer.RenderingMightDependOnPositioningAreaSizeChange();
3521 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
3522 gfxContext* aCtx) {
3523 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &mBounds);
3526 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
3527 gfxContext* aCtx,
3528 const nsRect& aBounds,
3529 nsRect* aClipRect) {
3530 gfxContext* ctx = aCtx;
3531 StyleGeometryBox clip =
3532 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip;
3534 if (clip == StyleGeometryBox::Text) {
3535 if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect,
3536 aBuilder)) {
3537 return;
3541 nsCSSRendering::PaintBGParams params =
3542 nsCSSRendering::PaintBGParams::ForSingleLayer(
3543 *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
3544 aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER,
3545 1.0f);
3546 params.bgClipRect = aClipRect;
3547 Unused << nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
3549 if (clip == StyleGeometryBox::Text) {
3550 ctx->PopGroupAndBlend();
3554 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3555 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3556 nsRegion* aInvalidRegion) const {
3557 if (!mBackgroundStyle) {
3558 return;
3561 const auto* geometry =
3562 static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
3564 bool snap;
3565 nsRect bounds = GetBounds(aBuilder, &snap);
3566 nsRect positioningArea = GetPositioningArea();
3567 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
3568 (positioningArea.Size() != geometry->mPositioningArea.Size() &&
3569 RenderingMightDependOnPositioningAreaSizeChange())) {
3570 // Positioning area changed in a way that could cause everything to change,
3571 // so invalidate everything (both old and new painting areas).
3572 aInvalidRegion->Or(bounds, geometry->mBounds);
3573 return;
3575 if (!mDestRect.IsEqualInterior(geometry->mDestRect)) {
3576 // Dest area changed in a way that could cause everything to change,
3577 // so invalidate everything (both old and new painting areas).
3578 aInvalidRegion->Or(bounds, geometry->mBounds);
3579 return;
3581 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3582 // Positioning area is unchanged, so invalidate just the change in the
3583 // painting area.
3584 aInvalidRegion->Xor(bounds, geometry->mBounds);
3588 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder,
3589 bool* aSnap) const {
3590 *aSnap = true;
3591 return mBounds;
3594 nsRect nsDisplayBackgroundImage::GetBoundsInternal(
3595 nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) {
3596 // This allows nsDisplayTableBackgroundImage to change the frame used for
3597 // bounds calculation.
3598 nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame;
3600 nsPresContext* presContext = frame->PresContext();
3602 if (!mBackgroundStyle) {
3603 return nsRect();
3606 nsRect clipRect = mBackgroundRect;
3607 if (frame->IsCanvasFrame()) {
3608 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3609 clipRect = canvasFrame->CanvasArea() + ToReferenceFrame();
3611 const nsStyleImageLayers::Layer& layer =
3612 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3613 return nsCSSRendering::GetBackgroundLayerRect(
3614 presContext, frame, mBackgroundRect, clipRect, layer,
3615 aBuilder->GetBackgroundPaintFlags());
3618 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3619 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aData,
3620 nsIFrame* aCellFrame)
3621 : nsDisplayBackgroundImage(aBuilder, aFrame, aData, aCellFrame),
3622 mStyleFrame(aCellFrame) {
3623 if (aBuilder->IsRetainingDisplayList()) {
3624 mStyleFrame->AddDisplayItem(this);
3628 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3629 if (mStyleFrame) {
3630 mStyleFrame->RemoveDisplayItem(this);
3634 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const {
3635 bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
3636 aRect += ToReferenceFrame();
3637 return result;
3640 nsDisplayThemedBackground::nsDisplayThemedBackground(
3641 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3642 const nsRect& aBackgroundRect)
3643 : nsPaintedDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) {
3644 MOZ_COUNT_CTOR(nsDisplayThemedBackground);
3647 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) {
3648 const nsStyleDisplay* disp = StyleFrame()->StyleDisplay();
3649 mAppearance = disp->EffectiveAppearance();
3650 StyleFrame()->IsThemed(disp, &mThemeTransparency);
3652 // Perform necessary RegisterThemeGeometry
3653 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3654 nsITheme::ThemeGeometryType type =
3655 theme->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance);
3656 if (type != nsITheme::eThemeGeometryTypeUnknown) {
3657 RegisterThemeGeometry(aBuilder, this, StyleFrame(), type);
3660 mBounds = GetBoundsInternal();
3663 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) {
3664 aStream << " (themed, appearance:" << (int)mAppearance << ")";
3667 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
3668 const nsRect& aRect,
3669 HitTestState* aState,
3670 nsTArray<nsIFrame*>* aOutFrames) {
3671 // Assume that any point in our background rect is a hit.
3672 if (mBackgroundRect.Intersects(aRect)) {
3673 aOutFrames->AppendElement(mFrame);
3677 nsRegion nsDisplayThemedBackground::GetOpaqueRegion(
3678 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3679 nsRegion result;
3680 *aSnap = false;
3682 if (mThemeTransparency == nsITheme::eOpaque) {
3683 *aSnap = true;
3684 result = mBackgroundRect;
3686 return result;
3689 Maybe<nscolor> nsDisplayThemedBackground::IsUniform(
3690 nsDisplayListBuilder* aBuilder) const {
3691 return Nothing();
3694 nsRect nsDisplayThemedBackground::GetPositioningArea() const {
3695 return mBackgroundRect;
3698 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
3699 gfxContext* aCtx) {
3700 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), nullptr);
3703 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
3704 gfxContext* aCtx,
3705 const nsRect& aBounds,
3706 nsRect* aClipRect) {
3707 // XXXzw this ignores aClipRect.
3708 nsPresContext* presContext = StyleFrame()->PresContext();
3709 nsITheme* theme = presContext->Theme();
3710 nsRect drawing(mBackgroundRect);
3711 theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(),
3712 mAppearance, &drawing);
3713 drawing.IntersectRect(drawing, aBounds);
3714 theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect,
3715 drawing);
3718 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3719 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3720 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3721 nsDisplayListBuilder* aDisplayListBuilder) {
3722 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3723 return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc,
3724 aManager, StyleFrame(),
3725 mAppearance, mBackgroundRect);
3728 bool nsDisplayThemedBackground::IsWindowActive() const {
3729 return !mFrame->PresContext()->Document()->IsTopLevelWindowInactive();
3732 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3733 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3734 nsRegion* aInvalidRegion) const {
3735 const auto* geometry =
3736 static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
3738 bool snap;
3739 nsRect bounds = GetBounds(aBuilder, &snap);
3740 nsRect positioningArea = GetPositioningArea();
3741 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
3742 // Invalidate everything (both old and new painting areas).
3743 aInvalidRegion->Or(bounds, geometry->mBounds);
3744 return;
3746 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3747 // Positioning area is unchanged, so invalidate just the change in the
3748 // painting area.
3749 aInvalidRegion->Xor(bounds, geometry->mBounds);
3751 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3752 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
3753 IsWindowActive() != geometry->mWindowIsActive) {
3754 aInvalidRegion->Or(*aInvalidRegion, bounds);
3758 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder,
3759 bool* aSnap) const {
3760 *aSnap = true;
3761 return mBounds;
3764 nsRect nsDisplayThemedBackground::GetBoundsInternal() {
3765 nsPresContext* presContext = mFrame->PresContext();
3767 nsRect r = mBackgroundRect - ToReferenceFrame();
3768 presContext->Theme()->GetWidgetOverflow(
3769 presContext->DeviceContext(), mFrame,
3770 mFrame->StyleDisplay()->EffectiveAppearance(), &r);
3771 return r + ToReferenceFrame();
3774 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
3775 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder,
3776 gfxContext* aCtx) {
3777 mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(),
3778 mFrame, ToReferenceFrame(), mColor);
3780 #endif
3782 bool nsDisplayBackgroundColor::CanApplyOpacity(
3783 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3784 // Don't apply opacity if the background color is animated since the color is
3785 // going to be changed on the compositor.
3786 return !EffectCompositor::HasAnimationsForCompositor(
3787 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR);
3790 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3791 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3792 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3793 nsDisplayListBuilder* aDisplayListBuilder) {
3794 gfx::sRGBColor color = mColor;
3795 color.a *= aBuilder.GetInheritedOpacity();
3797 if (color == sRGBColor() &&
3798 !EffectCompositor::HasAnimationsForCompositor(
3799 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3800 return true;
3803 if (HasBackgroundClipText()) {
3804 return false;
3807 uint64_t animationsId = 0;
3808 // We don't support background-color animations on table elements yet.
3809 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
3810 animationsId =
3811 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
3814 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
3815 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3816 wr::LayoutRect r = wr::ToLayoutRect(bounds);
3818 if (animationsId) {
3819 wr::WrAnimationProperty prop{
3820 wr::WrAnimationType::BackgroundColor,
3821 animationsId,
3823 aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(),
3824 wr::ToColorF(ToDeviceColor(color)), &prop);
3825 } else {
3826 aBuilder.StartGroup(this);
3827 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
3828 wr::ToColorF(ToDeviceColor(color)));
3829 aBuilder.FinishGroup();
3832 return true;
3835 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder,
3836 gfxContext* aCtx,
3837 const DisplayItemClip& aClip) {
3838 MOZ_ASSERT(!HasBackgroundClipText());
3840 if (mColor == sRGBColor()) {
3841 return;
3844 nsRect fillRect = mBackgroundRect;
3845 if (aClip.HasClip()) {
3846 fillRect.IntersectRect(fillRect, aClip.GetClipRect());
3849 DrawTarget* dt = aCtx->GetDrawTarget();
3850 int32_t A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
3851 Rect bounds = ToRect(nsLayoutUtils::RectToGfxRect(fillRect, A2D));
3852 MaybeSnapToDevicePixels(bounds, *dt);
3853 ColorPattern fill(ToDeviceColor(mColor));
3855 if (aClip.GetRoundedRectCount()) {
3856 MOZ_ASSERT(aClip.GetRoundedRectCount() == 1);
3858 AutoTArray<DisplayItemClip::RoundedRect, 1> roundedRect;
3859 aClip.AppendRoundedRects(&roundedRect);
3861 bool pushedClip = false;
3862 if (!fillRect.Contains(roundedRect[0].mRect)) {
3863 dt->PushClipRect(bounds);
3864 pushedClip = true;
3867 RectCornerRadii pixelRadii;
3868 nsCSSRendering::ComputePixelRadii(roundedRect[0].mRadii, A2D, &pixelRadii);
3869 dt->FillRoundedRect(
3870 RoundedRect(NSRectToSnappedRect(roundedRect[0].mRect, A2D, *dt),
3871 pixelRadii),
3872 fill);
3873 if (pushedClip) {
3874 dt->PopClip();
3876 } else {
3877 dt->FillRect(bounds, fill);
3881 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
3882 gfxContext* aCtx) {
3883 if (mColor == sRGBColor()) {
3884 return;
3887 #if 0
3888 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3889 // results in a precision induced rounding issue that makes the rect one
3890 // pixel shorter in rare cases. Disabled in favor of the old code for now.
3891 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3892 // reproduce the bug.
3894 // TODO:
3895 // This new path does not include support for background-clip:text; need to
3896 // be fixed if/when we switch to this new code path.
3898 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
3900 Rect rect = NSRectToSnappedRect(mBackgroundRect,
3901 mFrame->PresContext()->AppUnitsPerDevPixel(),
3902 aDrawTarget);
3903 ColorPattern color(ToDeviceColor(mColor));
3904 aDrawTarget.FillRect(rect, color);
3905 #else
3906 gfxContext* ctx = aCtx;
3907 gfxRect bounds = nsLayoutUtils::RectToGfxRect(
3908 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3910 if (HasBackgroundClipText()) {
3911 if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
3912 return;
3915 ctx->SetColor(mColor);
3916 ctx->NewPath();
3917 ctx->SnappedRectangle(bounds);
3918 ctx->Fill();
3919 ctx->PopGroupAndBlend();
3920 return;
3923 ctx->SetColor(mColor);
3924 ctx->NewPath();
3925 ctx->SnappedRectangle(bounds);
3926 ctx->Fill();
3927 #endif
3930 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion(
3931 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3932 *aSnap = false;
3934 if (mColor.a != 1 ||
3935 // Even if the current alpha channel is 1, we treat this item as if it's
3936 // non-opaque if there is a background-color animation since the animation
3937 // might change the alpha channel.
3938 EffectCompositor::HasAnimationsForCompositor(
3939 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3940 return nsRegion();
3943 if (!mHasStyle || HasBackgroundClipText()) {
3944 return nsRegion();
3947 *aSnap = true;
3948 return GetInsideClipRect(this, mBottomLayerClip, mBackgroundRect,
3949 mBackgroundRect);
3952 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform(
3953 nsDisplayListBuilder* aBuilder) const {
3954 return Some(mColor.ToABGR());
3957 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
3958 const nsRect& aRect,
3959 HitTestState* aState,
3960 nsTArray<nsIFrame*>* aOutFrames) {
3961 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3962 // aRect doesn't intersect our border-radius curve.
3963 return;
3966 aOutFrames->AppendElement(mFrame);
3969 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) {
3970 aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << ","
3971 << mColor.a << ")";
3972 aStream << " backgroundRect" << mBackgroundRect;
3975 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
3976 bool* aSnap) const {
3977 *aSnap = false;
3978 return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
3981 nsRect nsDisplayOutline::GetInnerRect() const {
3982 if (nsRect* savedOutlineInnerRect =
3983 mFrame->GetProperty(nsIFrame::OutlineInnerRectProperty())) {
3984 return *savedOutlineInnerRect;
3986 return mFrame->GetRectRelativeToSelf();
3989 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
3990 // TODO join outlines together
3991 MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(),
3992 "Should have not created a nsDisplayOutline!");
3994 nsRect rect = GetInnerRect() + ToReferenceFrame();
3995 nsPresContext* pc = mFrame->PresContext();
3996 if (IsThemedOutline()) {
3997 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
3998 pc->Theme()->DrawWidgetBackground(aCtx, mFrame,
3999 StyleAppearance::FocusOutline, rect,
4000 GetPaintRect(aBuilder, aCtx));
4001 return;
4004 nsCSSRendering::PaintNonThemedOutline(
4005 pc, *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), rect, mFrame->Style());
4008 bool nsDisplayOutline::IsThemedOutline() const {
4009 #ifdef DEBUG
4010 nsPresContext* pc = mFrame->PresContext();
4011 MOZ_ASSERT(
4012 pc->Theme()->ThemeSupportsWidget(pc, mFrame,
4013 StyleAppearance::FocusOutline),
4014 "All of our supported platforms have support for themed focus-outlines");
4015 #endif
4016 return mFrame->StyleOutline()->mOutlineStyle.IsAuto();
4019 bool nsDisplayOutline::CreateWebRenderCommands(
4020 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4021 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4022 nsDisplayListBuilder* aDisplayListBuilder) {
4023 nsPresContext* pc = mFrame->PresContext();
4024 nsRect rect = GetInnerRect() + ToReferenceFrame();
4025 if (IsThemedOutline()) {
4026 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
4027 return pc->Theme()->CreateWebRenderCommandsForWidget(
4028 aBuilder, aResources, aSc, aManager, mFrame,
4029 StyleAppearance::FocusOutline, rect);
4032 bool dummy;
4033 Maybe<nsCSSBorderRenderer> borderRenderer =
4034 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4035 pc, /* aDrawTarget = */ nullptr, mFrame,
4036 GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style());
4038 if (!borderRenderer) {
4039 // No border renderer means "there is no outline".
4040 // Paint nothing and return success.
4041 return true;
4044 borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
4045 return true;
4048 bool nsDisplayOutline::HasRadius() const {
4049 const auto& radius = mFrame->StyleBorder()->mBorderRadius;
4050 return !nsLayoutUtils::HasNonZeroCorner(radius);
4053 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const {
4054 const nsStyleOutline* outline = mFrame->StyleOutline();
4055 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
4056 if (borderBox.Contains(aRect) && !HasRadius() &&
4057 outline->mOutlineOffset.ToCSSPixels() >= 0.0f) {
4058 // aRect is entirely inside the border-rect, and the outline isn't rendered
4059 // inside the border-rect, so the outline is not visible.
4060 return true;
4062 return false;
4065 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
4066 const nsRect& aRect, HitTestState* aState,
4067 nsTArray<nsIFrame*>* aOutFrames) {
4068 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4069 // aRect doesn't intersect our border-radius curve.
4070 return;
4073 aOutFrames->AppendElement(mFrame);
4076 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4077 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4078 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4079 nsDisplayListBuilder* aDisplayListBuilder) {
4080 return true;
4083 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4084 return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
4087 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) {
4088 mOverrideZIndex = Some(aZIndex);
4091 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
4092 nsIFrame* aCaretFrame)
4093 : nsPaintedDisplayItem(aBuilder, aCaretFrame),
4094 mCaret(aBuilder->GetCaret()),
4095 mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) {
4096 MOZ_COUNT_CTOR(nsDisplayCaret);
4097 // The presence of a caret doesn't change the overflow rect
4098 // of the owning frame, so the normal building rect might not
4099 // include the caret at all. We use MarkFrameForDisplay to ensure
4100 // we build this item, and here we override the building rect
4101 // to cover the pixels we're going to draw.
4102 SetBuildingRect(mBounds);
4105 #ifdef NS_BUILD_REFCNT_LOGGING
4106 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret); }
4107 #endif
4109 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder,
4110 bool* aSnap) const {
4111 *aSnap = true;
4112 // The caret returns a rect in the coordinates of mFrame.
4113 return mBounds;
4116 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4117 // Note: Because we exist, we know that the caret is visible, so we don't
4118 // need to check for the caret's visibility.
4119 mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
4122 bool nsDisplayCaret::CreateWebRenderCommands(
4123 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4124 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4125 nsDisplayListBuilder* aDisplayListBuilder) {
4126 using namespace layers;
4127 nsRect caretRect;
4128 nsRect hookRect;
4129 nscolor caretColor;
4130 nsIFrame* frame =
4131 mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor);
4132 MOZ_ASSERT(frame == mFrame, "We're referring different frame");
4133 if (!frame) {
4134 return true;
4137 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
4138 gfx::DeviceColor color = ToDeviceColor(caretColor);
4139 LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
4140 caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
4141 LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
4142 hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
4144 wr::LayoutRect caret = wr::ToLayoutRect(devCaretRect);
4145 wr::LayoutRect hook = wr::ToLayoutRect(devHookRect);
4147 // Note, WR will pixel snap anything that is layout aligned.
4148 aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), false, false,
4149 wr::ToColorF(color));
4151 if (!devHookRect.IsEmpty()) {
4152 aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), false, false,
4153 wr::ToColorF(color));
4155 return true;
4158 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder,
4159 nsIFrame* aFrame)
4160 : nsPaintedDisplayItem(aBuilder, aFrame) {
4161 MOZ_COUNT_CTOR(nsDisplayBorder);
4163 mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
4166 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const {
4167 nsRect paddingRect = GetPaddingRect();
4168 const nsStyleBorder* styleBorder;
4169 if (paddingRect.Contains(aRect) &&
4170 !(styleBorder = mFrame->StyleBorder())->IsBorderImageSizeAvailable() &&
4171 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
4172 // aRect is entirely inside the content rect, and no part
4173 // of the border is rendered inside the content rect, so we are not
4174 // visible
4175 // Skip this if there's a border-image (which draws a background
4176 // too) or if there is a border-radius (which makes the border draw
4177 // further in).
4178 return true;
4181 return false;
4184 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry(
4185 nsDisplayListBuilder* aBuilder) {
4186 return new nsDisplayBorderGeometry(this, aBuilder);
4189 void nsDisplayBorder::ComputeInvalidationRegion(
4190 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4191 nsRegion* aInvalidRegion) const {
4192 const auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
4193 bool snap;
4195 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
4196 // We can probably get away with only invalidating the difference
4197 // between the border and padding rects, but the XUL ui at least
4198 // is apparently painting a background with this?
4199 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4203 bool nsDisplayBorder::CreateWebRenderCommands(
4204 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4205 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4206 nsDisplayListBuilder* aDisplayListBuilder) {
4207 nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
4209 ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder(
4210 this, mFrame, rect, aBuilder, aResources, aSc, aManager,
4211 aDisplayListBuilder);
4213 if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
4214 return false;
4216 return true;
4219 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4220 nsPoint offset = ToReferenceFrame();
4222 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
4223 ? PaintBorderFlags::SyncDecodeImages
4224 : PaintBorderFlags();
4226 Unused << nsCSSRendering::PaintBorder(
4227 mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
4228 nsRect(offset, mFrame->GetSize()), mFrame->Style(), flags,
4229 mFrame->GetSkipSides());
4232 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder,
4233 bool* aSnap) const {
4234 *aSnap = true;
4235 return mBounds;
4238 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
4239 gfxContext* aCtx) {
4240 nsPoint offset = ToReferenceFrame();
4241 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4242 nsPresContext* presContext = mFrame->PresContext();
4244 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
4246 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
4247 GetPaintRect(aBuilder, aCtx), 1.0f);
4250 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
4251 bool* aSnap) const {
4252 *aSnap = false;
4253 return mBounds;
4256 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() {
4257 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
4258 ToReferenceFrame();
4261 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const {
4262 nsPoint origin = ToReferenceFrame();
4263 nsRect frameRect(origin, mFrame->GetSize());
4264 if (!frameRect.Contains(aRect)) {
4265 return false;
4268 // the visible region is entirely inside the border-rect, and box shadows
4269 // never render within the border-rect (unless there's a border radius).
4270 nscoord twipsRadii[8];
4271 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
4272 if (!hasBorderRadii) {
4273 return true;
4276 return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
4279 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4280 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4281 if (shadows.IsEmpty()) {
4282 return false;
4285 bool hasBorderRadius;
4286 // We don't support native themed things yet like box shadows around
4287 // input buttons.
4289 // TODO(emilio): The non-native theme could provide the right rect+radius
4290 // instead relatively painlessly, if we find this causes performance issues or
4291 // what not.
4292 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4295 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4296 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4297 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4298 nsDisplayListBuilder* aDisplayListBuilder) {
4299 if (!CanBuildWebRenderDisplayItems()) {
4300 return false;
4303 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4304 nsPoint offset = ToReferenceFrame();
4305 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4306 bool snap;
4307 nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
4309 bool hasBorderRadius;
4310 bool nativeTheme =
4311 nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4313 // Don't need the full size of the shadow rect like we do in
4314 // nsCSSRendering since WR takes care of calculations for blur
4315 // and spread radius.
4316 nsRect frameRect =
4317 nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
4319 RectCornerRadii borderRadii;
4320 if (hasBorderRadius) {
4321 hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
4322 mFrame, borderRadii);
4325 // Everything here is in app units, change to device units.
4326 LayoutDeviceRect clipRect =
4327 LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel);
4328 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4329 MOZ_ASSERT(!shadows.IsEmpty());
4331 for (const auto& shadow : Reversed(shadows)) {
4332 if (shadow.inset) {
4333 continue;
4336 float blurRadius =
4337 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4338 gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor(
4339 shadow.base, mFrame, aBuilder.GetInheritedOpacity());
4341 // We don't move the shadow rect here since WR does it for us
4342 // Now translate everything to device pixels.
4343 const nsRect& shadowRect = frameRect;
4344 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4345 nsPoint(shadow.base.horizontal.ToAppUnits(),
4346 shadow.base.vertical.ToAppUnits()),
4347 appUnitsPerDevPixel);
4349 LayoutDeviceRect deviceBox =
4350 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4351 wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
4352 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4354 LayoutDeviceSize zeroSize;
4355 wr::BorderRadius borderRadius =
4356 wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
4357 if (hasBorderRadius) {
4358 borderRadius = wr::ToBorderRadius(
4359 LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
4360 LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
4361 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
4362 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
4365 float spreadRadius =
4366 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4368 aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
4369 deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
4370 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius,
4371 spreadRadius, borderRadius,
4372 wr::BoxShadowClipMode::Outset);
4375 return true;
4378 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4379 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4380 nsRegion* aInvalidRegion) const {
4381 const auto* geometry =
4382 static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
4383 bool snap;
4384 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
4385 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
4386 nsRegion oldShadow, newShadow;
4387 nscoord dontCare[8];
4388 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
4389 if (hasBorderRadius) {
4390 // If we have rounded corners then we need to invalidate the frame area
4391 // too since we paint into it.
4392 oldShadow = geometry->mBounds;
4393 newShadow = GetBounds(aBuilder, &snap);
4394 } else {
4395 oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
4396 newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
4398 aInvalidRegion->Or(oldShadow, newShadow);
4402 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
4403 gfxContext* aCtx) {
4404 nsPoint offset = ToReferenceFrame();
4405 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4406 nsPresContext* presContext = mFrame->PresContext();
4408 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS);
4410 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
4413 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4414 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4415 const nsPoint& aReferenceOffset) {
4416 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4417 if (shadows.IsEmpty()) {
4418 // Means we don't have to paint anything
4419 return true;
4422 bool hasBorderRadius;
4423 bool nativeTheme =
4424 nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
4426 // We don't support native themed things yet like box shadows around
4427 // input buttons.
4428 return !nativeTheme;
4431 /* static */
4432 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4433 wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
4434 nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect) {
4435 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) {
4436 return;
4439 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
4441 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4443 LayoutDeviceRect clipRect =
4444 LayoutDeviceRect::FromAppUnits(aVisibleRect, appUnitsPerDevPixel);
4446 for (const auto& shadow : Reversed(shadows)) {
4447 if (!shadow.inset) {
4448 continue;
4451 nsRect shadowRect =
4452 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
4453 RectCornerRadii innerRadii;
4454 nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
4456 // Now translate everything to device pixels.
4457 LayoutDeviceRect deviceBoxRect =
4458 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4459 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4460 sRGBColor shadowColor =
4461 nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0);
4463 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4464 nsPoint(shadow.base.horizontal.ToAppUnits(),
4465 shadow.base.vertical.ToAppUnits()),
4466 appUnitsPerDevPixel);
4468 float blurRadius =
4469 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4471 wr::BorderRadius borderRadius = wr::ToBorderRadius(
4472 LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
4473 LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
4474 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
4475 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
4476 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4477 float spreadRadius =
4478 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4480 aBuilder.PushBoxShadow(
4481 wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
4482 !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
4483 wr::ToLayoutVector2D(shadowOffset),
4484 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, spreadRadius,
4485 borderRadius, wr::BoxShadowClipMode::Inset);
4489 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4490 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4491 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4492 nsDisplayListBuilder* aDisplayListBuilder) {
4493 if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame,
4494 ToReferenceFrame())) {
4495 return false;
4498 bool snap;
4499 nsRect visible = GetBounds(aDisplayListBuilder, &snap);
4500 nsPoint offset = ToReferenceFrame();
4501 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4502 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4503 aBuilder, aSc, visible, mFrame, borderRect);
4505 return true;
4508 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4509 nsIFrame* aFrame, nsDisplayList* aList)
4510 : nsDisplayWrapList(aBuilder, aFrame, aList,
4511 aBuilder->CurrentActiveScrolledRoot(), false) {}
4513 nsDisplayWrapList::nsDisplayWrapList(
4514 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4515 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
4516 : nsPaintedDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
4517 mList(aBuilder),
4518 mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
4519 mOverrideZIndex(0),
4520 mHasZIndexOverride(false),
4521 mClearingClipChain(aClearClipChain) {
4522 MOZ_COUNT_CTOR(nsDisplayWrapList);
4524 mBaseBuildingRect = GetBuildingRect();
4526 mListPtr = &mList;
4527 mListPtr->AppendToTop(aList);
4528 mOriginalClipChain = mClipChain;
4529 nsDisplayWrapList::UpdateBounds(aBuilder);
4532 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4533 nsIFrame* aFrame, nsDisplayItem* aItem)
4534 : nsPaintedDisplayItem(aBuilder, aFrame,
4535 aBuilder->CurrentActiveScrolledRoot()),
4536 mList(aBuilder),
4537 mOverrideZIndex(0),
4538 mHasZIndexOverride(false) {
4539 MOZ_COUNT_CTOR(nsDisplayWrapList);
4541 mBaseBuildingRect = GetBuildingRect();
4543 mListPtr = &mList;
4544 mListPtr->AppendToTop(aItem);
4545 mOriginalClipChain = mClipChain;
4546 nsDisplayWrapList::UpdateBounds(aBuilder);
4548 if (!aFrame || !aFrame->IsTransformed()) {
4549 return;
4552 // See the previous nsDisplayWrapList constructor
4553 if (aItem->Frame() == aFrame) {
4554 mToReferenceFrame = aItem->ToReferenceFrame();
4557 nsRect visible = aBuilder->GetVisibleRect() +
4558 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
4560 SetBuildingRect(visible);
4563 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList); }
4565 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder,
4566 const nsRect& aRect, HitTestState* aState,
4567 nsTArray<nsIFrame*>* aOutFrames) {
4568 mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames);
4571 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder,
4572 bool* aSnap) const {
4573 *aSnap = false;
4574 return mBounds;
4577 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4578 bool* aSnap) const {
4579 *aSnap = false;
4580 bool snap;
4581 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
4582 GetBounds(aBuilder, &snap));
4585 Maybe<nscolor> nsDisplayWrapList::IsUniform(
4586 nsDisplayListBuilder* aBuilder) const {
4587 // We could try to do something but let's conservatively just return Nothing.
4588 return Nothing();
4591 void nsDisplayWrapper::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4592 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4595 nsRect nsDisplayWrapList::GetComponentAlphaBounds(
4596 nsDisplayListBuilder* aBuilder) const {
4597 return mListPtr->GetComponentAlphaBounds(aBuilder);
4600 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4601 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4602 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4603 nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList) {
4604 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4605 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
4606 aNewClipList);
4607 return true;
4610 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder,
4611 nsIFrame* aFrame, nsDisplayList* aList,
4612 nsDisplayItemWrapper* aWrapper) {
4613 if (!aList->GetTop()) {
4614 return NS_OK;
4616 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
4617 if (!item) {
4618 return NS_ERROR_OUT_OF_MEMORY;
4620 // aList was emptied
4621 aList->AppendToTop(item);
4622 return NS_OK;
4625 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
4626 nsDisplayList* aList,
4627 nsDisplayItemWrapper* aWrapper) {
4628 for (nsDisplayItem* item : aList->TakeItems()) {
4629 item = aWrapper->WrapItem(aBuilder, item);
4630 if (!item) {
4631 return NS_ERROR_OUT_OF_MEMORY;
4633 aList->AppendToTop(item);
4635 // aList was emptied
4636 return NS_OK;
4639 nsresult nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
4640 nsIFrame* aFrame,
4641 const nsDisplayListSet& aIn,
4642 const nsDisplayListSet& aOut) {
4643 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
4644 NS_ENSURE_SUCCESS(rv, rv);
4646 if (&aOut == &aIn) {
4647 return NS_OK;
4649 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
4650 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
4651 aOut.Floats()->AppendToTop(aIn.Floats());
4652 aOut.Content()->AppendToTop(aIn.Content());
4653 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
4654 aOut.Outlines()->AppendToTop(aIn.Outlines());
4655 return NS_OK;
4658 nsresult nsDisplayItemWrapper::WrapListsInPlace(
4659 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4660 const nsDisplayListSet& aLists) {
4661 nsresult rv;
4662 if (WrapBorderBackground()) {
4663 // Our border-backgrounds are in-flow
4664 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
4665 NS_ENSURE_SUCCESS(rv, rv);
4667 // Our block border-backgrounds are in-flow
4668 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
4669 NS_ENSURE_SUCCESS(rv, rv);
4670 // The floats are not in flow
4671 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
4672 NS_ENSURE_SUCCESS(rv, rv);
4673 // Our child content is in flow
4674 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
4675 NS_ENSURE_SUCCESS(rv, rv);
4676 // The positioned descendants may not be in-flow
4677 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
4678 NS_ENSURE_SUCCESS(rv, rv);
4679 // The outlines may not be in-flow
4680 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
4683 nsDisplayOpacity::nsDisplayOpacity(
4684 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4685 const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsOnly,
4686 bool aNeedsActiveLayer, bool aWrapsBackdropFilter)
4687 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4688 mOpacity(aFrame->StyleEffects()->mOpacity),
4689 mForEventsOnly(aForEventsOnly),
4690 mNeedsActiveLayer(aNeedsActiveLayer),
4691 mChildOpacityState(ChildOpacityState::Unknown),
4692 mWrapsBackdropFilter(aWrapsBackdropFilter) {
4693 MOZ_COUNT_CTOR(nsDisplayOpacity);
4696 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder,
4697 const nsRect& aRect,
4698 nsDisplayItem::HitTestState* aState,
4699 nsTArray<nsIFrame*>* aOutFrames) {
4700 AutoRestore<float> opacity(aState->mCurrentOpacity);
4701 aState->mCurrentOpacity *= mOpacity;
4703 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4704 // only consider fully opaque items? Or make this configurable somehow?
4705 if (aBuilder->HitTestIsForVisibility() && mOpacity == 0.0f) {
4706 return;
4708 nsDisplayWrapList::HitTest(aBuilder, aRect, aState, aOutFrames);
4711 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4712 bool* aSnap) const {
4713 *aSnap = false;
4714 // The only time where mOpacity == 1.0 should be when we have will-change.
4715 // We could report this as opaque then but when the will-change value starts
4716 // animating the element would become non opaque and could cause repaints.
4717 return nsRegion();
4720 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4721 if (GetOpacity() == 0.0f) {
4722 return;
4725 if (GetOpacity() == 1.0f) {
4726 GetChildren()->Paint(aBuilder, aCtx,
4727 mFrame->PresContext()->AppUnitsPerDevPixel());
4728 return;
4731 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4732 // allocation.
4733 aCtx->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4734 GetChildren()->Paint(aBuilder, aCtx,
4735 mFrame->PresContext()->AppUnitsPerDevPixel());
4736 aCtx->GetDrawTarget()->PopLayer();
4739 /* static */
4740 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
4741 nsIFrame* aFrame) {
4742 return EffectCompositor::HasAnimationsForCompositor(
4743 aFrame, DisplayItemType::TYPE_OPACITY) ||
4744 (ActiveLayerTracker::IsStyleAnimated(
4745 aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()));
4748 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager,
4749 nsDisplayListBuilder* aBuilder) const {
4750 return !EffectCompositor::HasAnimationsForCompositor(
4751 mFrame, DisplayItemType::TYPE_OPACITY);
4754 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4755 // children that don't overlap and can all apply the opacity to themselves.
4756 static const size_t kOpacityMaxChildCount = 3;
4758 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4759 // are likely have more child items than |kOpacityMaxChildCount|.
4760 static const size_t kOpacityMaxListSize = kOpacityMaxChildCount * 2;
4763 * Recursively iterates through |aList| and collects at most
4764 * |kOpacityMaxChildCount| display item pointers to items that return true for
4765 * CanApplyOpacity(). The item pointers are added to |aArray|.
4767 * LayerEventRegions and WrapList items are ignored.
4769 * We need to do this recursively, because the child display items might contain
4770 * nested nsDisplayWrapLists.
4772 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4773 * item that returns false for CanApplyOpacity() is encountered.
4774 * Otherwise returns true.
4776 static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager,
4777 nsDisplayListBuilder* aBuilder,
4778 nsDisplayList* aList,
4779 nsTArray<nsPaintedDisplayItem*>& aArray) {
4780 if (aList->Length() > kOpacityMaxListSize) {
4781 // Exit early, since |aList| will likely contain more than
4782 // |kOpacityMaxChildCount| items.
4783 return false;
4786 for (nsDisplayItem* i : *aList) {
4787 const DisplayItemType type = i->GetType();
4789 if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
4790 continue;
4793 // Descend only into wraplists.
4794 if (type == DisplayItemType::TYPE_WRAP_LIST ||
4795 type == DisplayItemType::TYPE_CONTAINER) {
4796 // The current display item has children, process them first.
4797 if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(),
4798 aArray)) {
4799 return false;
4802 continue;
4805 if (aArray.Length() == kOpacityMaxChildCount) {
4806 return false;
4809 auto* item = i->AsPaintedDisplayItem();
4810 if (!item || !item->CanApplyOpacity(aManager, aBuilder)) {
4811 return false;
4814 aArray.AppendElement(item);
4817 return true;
4820 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager,
4821 nsDisplayListBuilder* aBuilder) {
4822 if (mChildOpacityState == ChildOpacityState::Deferred) {
4823 return false;
4826 // Iterate through the child display list and copy at most
4827 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4828 AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items;
4829 if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) {
4830 mChildOpacityState = ChildOpacityState::Deferred;
4831 return false;
4834 struct {
4835 nsPaintedDisplayItem* item{};
4836 nsRect bounds;
4837 } children[kOpacityMaxChildCount];
4839 bool snap;
4840 size_t childCount = 0;
4841 for (nsPaintedDisplayItem* item : items) {
4842 children[childCount].item = item;
4843 children[childCount].bounds = item->GetBounds(aBuilder, &snap);
4844 childCount++;
4847 for (size_t i = 0; i < childCount; i++) {
4848 for (size_t j = i + 1; j < childCount; j++) {
4849 if (children[i].bounds.Intersects(children[j].bounds)) {
4850 mChildOpacityState = ChildOpacityState::Deferred;
4851 return false;
4856 mChildOpacityState = ChildOpacityState::Applied;
4857 return true;
4861 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4862 * that has the same frame as the opacity item, and that supports painting with
4863 * opacity. In this case the opacity item can be optimized away.
4865 bool nsDisplayOpacity::ApplyToMask() {
4866 if (mList.Length() != 1) {
4867 return false;
4870 nsDisplayItem* item = mList.GetBottom();
4871 if (item->Frame() != mFrame) {
4872 // The effect item needs to have the same frame as the opacity item.
4873 return false;
4876 const DisplayItemType type = item->GetType();
4877 if (type == DisplayItemType::TYPE_MASK) {
4878 return true;
4881 return false;
4884 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4885 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder,
4886 float aInheritedOpacity) {
4887 if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
4888 mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
4889 // If we've been split, then we might need to merge, so
4890 // don't flatten us away.
4891 return false;
4894 if (mNeedsActiveLayer || mOpacity == 0.0) {
4895 // If our opacity is zero then we'll discard all descendant display items
4896 // except for layer event regions, so there's no point in doing this
4897 // optimization (and if we do do it, then invalidations of those descendants
4898 // might trigger repainting).
4899 return false;
4902 if (mList.IsEmpty()) {
4903 return false;
4906 // We can only flatten opacity items into a mask if we haven't
4907 // already flattened an earlier ancestor, since the SVG code pulls the opacity
4908 // from style directly, and won't know about the outer opacity value.
4909 if (aInheritedOpacity == 1.0f && ApplyToMask()) {
4910 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame));
4911 mChildOpacityState = ChildOpacityState::Applied;
4912 return true;
4915 // Return true if we successfully applied opacity to child items.
4916 return CanApplyToChildren(aManager, aBuilder);
4919 void nsDisplayOpacity::ComputeInvalidationRegion(
4920 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4921 nsRegion* aInvalidRegion) const {
4922 const auto* geometry =
4923 static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
4925 bool snap;
4926 if (mOpacity != geometry->mOpacity) {
4927 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4931 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) {
4932 aStream << " (opacity " << mOpacity << ", mChildOpacityState: ";
4933 switch (mChildOpacityState) {
4934 case ChildOpacityState::Unknown:
4935 aStream << "Unknown";
4936 break;
4937 case ChildOpacityState::Applied:
4938 aStream << "Applied";
4939 break;
4940 case ChildOpacityState::Deferred:
4941 aStream << "Deferred";
4942 break;
4943 default:
4944 break;
4947 aStream << ")";
4950 bool nsDisplayOpacity::CreateWebRenderCommands(
4951 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4952 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4953 nsDisplayListBuilder* aDisplayListBuilder) {
4954 MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied);
4955 float oldOpacity = aBuilder.GetInheritedOpacity();
4956 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
4957 aBuilder.SetInheritedOpacity(1.0f);
4958 aBuilder.SetInheritedClipChain(nullptr);
4959 float opacity = mOpacity * oldOpacity;
4960 float* opacityForSC = &opacity;
4962 uint64_t animationsId =
4963 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
4964 wr::WrAnimationProperty prop{
4965 wr::WrAnimationType::Opacity,
4966 animationsId,
4969 wr::StackingContextParams params;
4970 params.animation = animationsId ? &prop : nullptr;
4971 params.opacity = opacityForSC;
4972 params.clip =
4973 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
4974 if (mWrapsBackdropFilter) {
4975 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
4977 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
4978 params);
4980 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4981 &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
4982 aBuilder.SetInheritedOpacity(oldOpacity);
4983 aBuilder.SetInheritedClipChain(oldClipChain);
4984 return true;
4987 nsDisplayBlendMode::nsDisplayBlendMode(
4988 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4989 StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
4990 const bool aIsForBackground)
4991 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4992 mBlendMode(aBlendMode),
4993 mIsForBackground(aIsForBackground) {
4994 MOZ_COUNT_CTOR(nsDisplayBlendMode);
4997 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4998 bool* aSnap) const {
4999 *aSnap = false;
5000 // We are never considered opaque
5001 return nsRegion();
5004 bool nsDisplayBlendMode::CreateWebRenderCommands(
5005 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5006 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5007 nsDisplayListBuilder* aDisplayListBuilder) {
5008 wr::StackingContextParams params;
5009 params.mix_blend_mode =
5010 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
5011 params.clip =
5012 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5013 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5014 params);
5016 return nsDisplayWrapList::CreateWebRenderCommands(
5017 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5020 void nsDisplayBlendMode::Paint(nsDisplayListBuilder* aBuilder,
5021 gfxContext* aCtx) {
5022 // This should be switched to use PushLayerWithBlend, once it's
5023 // been implemented for all DrawTarget backends.
5024 DrawTarget* dt = aCtx->GetDrawTarget();
5025 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5026 Rect rect = NSRectToRect(GetPaintRect(aBuilder, aCtx), appUnitsPerDevPixel);
5027 rect.RoundOut();
5029 // Create a temporary DrawTarget that is clipped to the area that
5030 // we're going to draw to. This will include the same transform as
5031 // is currently on |dt|.
5032 RefPtr<DrawTarget> temp =
5033 dt->CreateClippedDrawTarget(rect, SurfaceFormat::B8G8R8A8);
5034 if (!temp) {
5035 return;
5038 gfxContext ctx(temp, /* aPreserveTransform */ true);
5040 GetChildren()->Paint(aBuilder, &ctx,
5041 mFrame->PresContext()->AppUnitsPerDevPixel());
5043 // Draw the temporary DT to the real destination, applying the blend mode, but
5044 // no transform.
5045 temp->Flush();
5046 RefPtr<SourceSurface> surface = temp->Snapshot();
5047 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
5048 dt->SetTransform(Matrix());
5049 dt->DrawSurface(
5050 surface, Rect(surface->GetRect()), Rect(surface->GetRect()),
5051 DrawSurfaceOptions(),
5052 DrawOptions(1.0f, nsCSSRendering::GetGFXBlendMode(mBlendMode)));
5055 gfx::CompositionOp nsDisplayBlendMode::BlendMode() {
5056 return nsCSSRendering::GetGFXBlendMode(mBlendMode);
5059 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const {
5060 // Items for the same content element should be merged into a single
5061 // compositing group.
5062 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
5063 !HasSameContent(aItem)) {
5064 return false;
5067 const auto* item = static_cast<const nsDisplayBlendMode*>(aItem);
5068 if (mIsForBackground || item->mIsForBackground) {
5069 // Don't merge background-blend-mode items
5070 return false;
5073 return true;
5076 /* static */
5077 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForMixBlendMode(
5078 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5079 const ActiveScrolledRoot* aActiveScrolledRoot) {
5080 return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
5081 aActiveScrolledRoot, false);
5084 /* static */
5085 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5086 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5087 nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot) {
5088 if (aSecondaryFrame) {
5089 auto type = GetTableTypeFromFrame(aFrame);
5090 auto index = static_cast<uint16_t>(type);
5092 return MakeDisplayItemWithIndex<nsDisplayTableBlendContainer>(
5093 aBuilder, aSecondaryFrame, index, aList, aActiveScrolledRoot, true,
5094 aFrame);
5097 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>(
5098 aBuilder, aFrame, 1, aList, aActiveScrolledRoot, true);
5101 nsDisplayBlendContainer::nsDisplayBlendContainer(
5102 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5103 const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground)
5104 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5105 mIsForBackground(aIsForBackground) {
5106 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
5109 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder* aBuilder,
5110 gfxContext* aCtx) {
5111 aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5112 GetChildren()->Paint(aBuilder, aCtx,
5113 mFrame->PresContext()->AppUnitsPerDevPixel());
5114 aCtx->GetDrawTarget()->PopLayer();
5117 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5118 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5119 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5120 nsDisplayListBuilder* aDisplayListBuilder) {
5121 wr::StackingContextParams params;
5122 params.flags |= wr::StackingContextFlags::IS_BLEND_CONTAINER;
5123 params.clip =
5124 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5125 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5126 params);
5128 return nsDisplayWrapList::CreateWebRenderCommands(
5129 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5132 nsDisplayOwnLayer::nsDisplayOwnLayer(
5133 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5134 const ActiveScrolledRoot* aActiveScrolledRoot,
5135 nsDisplayOwnLayerFlags aFlags, const ScrollbarData& aScrollbarData,
5136 bool aForceActive, bool aClearClipChain)
5137 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
5138 aClearClipChain),
5139 mFlags(aFlags),
5140 mScrollbarData(aScrollbarData),
5141 mForceActive(aForceActive),
5142 mWrAnimationId(0) {
5143 MOZ_COUNT_CTOR(nsDisplayOwnLayer);
5146 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5147 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb;
5150 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5151 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container;
5154 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5155 return IsScrollbarContainer() && IsScrollbarLayerForRoot();
5158 bool nsDisplayOwnLayer::IsScrollbarLayerForRoot() const {
5159 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5160 mScrollbarData.mTargetViewId ==
5161 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5164 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5165 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM;
5168 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5169 return GetType() == DisplayItemType::TYPE_FIXED_POSITION ||
5170 GetType() == DisplayItemType::TYPE_TABLE_FIXED_POSITION;
5173 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5174 return GetType() == DisplayItemType::TYPE_STICKY_POSITION;
5177 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5178 if (!mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
5179 return false;
5181 return mFrame->PresContext()->HasDynamicToolbar() ||
5182 // For tests on Android, this pref is set to simulate the dynamic
5183 // toolbar
5184 StaticPrefs::apz_fixed_margin_override_enabled();
5187 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5188 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5189 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5190 nsDisplayListBuilder* aDisplayListBuilder) {
5191 Maybe<wr::WrAnimationProperty> prop;
5192 bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() &&
5193 (IsScrollThumbLayer() || IsZoomingLayer() ||
5194 ShouldGetFixedOrStickyAnimationId() ||
5195 (IsRootScrollbarContainer() && HasDynamicToolbar()));
5197 if (needsProp) {
5198 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5199 // to create and set an animation id. That way APZ can adjust the position/
5200 // zoom of this content asynchronously as needed.
5201 RefPtr<WebRenderAPZAnimationData> animationData =
5202 aManager->CommandBuilder()
5203 .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(this);
5204 mWrAnimationId = animationData->GetAnimationId();
5206 prop.emplace();
5207 prop->id = mWrAnimationId;
5208 prop->key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5209 wr::SpatialKeyKind::APZ);
5210 prop->effect_type = wr::WrAnimationType::Transform;
5213 wr::StackingContextParams params;
5214 params.animation = prop.ptrOr(nullptr);
5215 params.clip =
5216 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5217 if (IsScrollbarContainer() && IsRootScrollbarContainer()) {
5218 params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER;
5220 if (IsZoomingLayer() ||
5221 (ShouldGetFixedOrStickyAnimationId() ||
5222 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5223 params.is_2d_scale_translation = true;
5224 params.should_snap = true;
5227 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5228 params);
5230 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
5231 aDisplayListBuilder);
5232 return true;
5235 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData,
5236 WebRenderLayerScrollData* aLayerData) {
5237 bool isRelevantToApz =
5238 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5239 ShouldGetFixedOrStickyAnimationId());
5241 if (!isRelevantToApz) {
5242 return false;
5245 if (!aLayerData) {
5246 return true;
5249 if (IsZoomingLayer()) {
5250 aLayerData->SetZoomAnimationId(mWrAnimationId);
5251 return true;
5254 if (IsFixedPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5255 aLayerData->SetFixedPositionAnimationId(mWrAnimationId);
5256 return true;
5259 if (IsStickyPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5260 aLayerData->SetStickyPositionAnimationId(mWrAnimationId);
5261 return true;
5264 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5266 aLayerData->SetScrollbarData(mScrollbarData);
5268 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5269 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5270 return true;
5273 if (IsScrollThumbLayer()) {
5274 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5275 LayoutDeviceRect bounds = LayoutDeviceIntRect::FromAppUnits(
5276 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
5277 // Subframe scrollbars are subject to the pinch-zoom scale,
5278 // but root scrollbars are not because they are outside of the
5279 // region that is zoomed.
5280 const float resolution =
5281 IsScrollbarLayerForRoot()
5282 ? 1.0f
5283 : mFrame->PresShell()->GetCumulativeResolution();
5284 LayerIntRect layerBounds =
5285 RoundedOut(bounds * LayoutDeviceToLayerScale(resolution));
5286 aLayerData->SetVisibleRect(layerBounds);
5288 return true;
5291 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) {
5292 aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
5293 (int)mFlags, mScrollbarData.mTargetViewId)
5294 .get();
5297 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
5298 nsIFrame* aFrame,
5299 nsSubDocumentFrame* aSubDocFrame,
5300 nsDisplayList* aList,
5301 nsDisplayOwnLayerFlags aFlags)
5302 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5303 aBuilder->CurrentActiveScrolledRoot(), aFlags),
5304 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5305 mShouldFlatten(false),
5306 mSubDocFrame(aSubDocFrame) {
5307 MOZ_COUNT_CTOR(nsDisplaySubDocument);
5309 if (mSubDocFrame && mSubDocFrame != mFrame) {
5310 mSubDocFrame->AddDisplayItem(this);
5314 nsDisplaySubDocument::~nsDisplaySubDocument() {
5315 MOZ_COUNT_DTOR(nsDisplaySubDocument);
5316 if (mSubDocFrame) {
5317 mSubDocFrame->RemoveDisplayItem(this);
5321 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const {
5322 return mSubDocFrame ? mSubDocFrame : mFrame;
5325 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) {
5326 if (aFrame == mSubDocFrame) {
5327 mSubDocFrame = nullptr;
5328 SetDeletedFrame();
5330 nsDisplayOwnLayer::RemoveFrame(aFrame);
5333 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder,
5334 nsIFrame* aFrame) {
5335 return aBuilder->IsPaintingToWindow() &&
5336 DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext());
5339 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder,
5340 bool* aSnap) const {
5341 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5343 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5344 usingDisplayPort) {
5345 *aSnap = false;
5346 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
5349 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
5352 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5353 bool* aSnap) const {
5354 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5356 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5357 usingDisplayPort) {
5358 *aSnap = false;
5359 return nsRegion();
5362 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
5365 /* static */
5366 nsDisplayFixedPosition* nsDisplayFixedPosition::CreateForFixedBackground(
5367 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5368 nsDisplayBackgroundImage* aImage, const uint16_t aIndex,
5369 const ActiveScrolledRoot* aScrollTargetASR) {
5370 nsDisplayList temp(aBuilder);
5371 temp.AppendToTop(aImage);
5373 if (aSecondaryFrame) {
5374 auto tableType = GetTableTypeFromFrame(aFrame);
5375 const uint16_t index = CalculateTablePerFrameKey(aIndex + 1, tableType);
5376 return MakeDisplayItemWithIndex<nsDisplayTableFixedPosition>(
5377 aBuilder, aSecondaryFrame, index, &temp, aFrame, aScrollTargetASR);
5380 return MakeDisplayItemWithIndex<nsDisplayFixedPosition>(
5381 aBuilder, aFrame, aIndex + 1, &temp, aScrollTargetASR);
5384 nsDisplayFixedPosition::nsDisplayFixedPosition(
5385 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5386 const ActiveScrolledRoot* aActiveScrolledRoot,
5387 const ActiveScrolledRoot* aScrollTargetASR)
5388 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5389 mScrollTargetASR(aScrollTargetASR),
5390 mIsFixedBackground(false) {
5391 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5394 nsDisplayFixedPosition::nsDisplayFixedPosition(
5395 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5396 const ActiveScrolledRoot* aScrollTargetASR)
5397 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5398 aBuilder->CurrentActiveScrolledRoot()),
5399 // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5400 mScrollTargetASR(aScrollTargetASR),
5401 mIsFixedBackground(true) {
5402 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5405 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() const {
5406 if (mScrollTargetASR &&
5407 (mIsFixedBackground || !nsLayoutUtils::IsReallyFixedPos(mFrame))) {
5408 return mScrollTargetASR->GetViewId();
5410 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5413 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5414 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5415 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5416 nsDisplayListBuilder* aDisplayListBuilder) {
5417 SideBits sides = SideBits::eNone;
5418 if (!mIsFixedBackground) {
5419 sides = nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5422 // We install this RAII scrolltarget tracker so that any
5423 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5424 // share the same ASR as this item) use the correct scroll target. That way
5425 // attempts to scroll on those items will scroll the root scroll frame.
5426 wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker(
5427 aBuilder, GetActiveScrolledRoot(), GetScrollTargetId(), sides);
5428 return nsDisplayOwnLayer::CreateWebRenderCommands(
5429 aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
5432 bool nsDisplayFixedPosition::UpdateScrollData(
5433 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5434 if (aLayerData) {
5435 if (!mIsFixedBackground) {
5436 aLayerData->SetFixedPositionSides(
5437 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame));
5439 aLayerData->SetFixedPositionScrollContainerId(GetScrollTargetId());
5441 nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5442 return true;
5445 bool nsDisplayFixedPosition::ShouldGetFixedOrStickyAnimationId() {
5446 #if defined(MOZ_WIDGET_ANDROID)
5447 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5448 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) ==
5449 GetScrollTargetId();
5450 #else
5451 return false;
5452 #endif
5455 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream& aStream) {
5456 aStream << nsPrintfCString(
5457 " (containerASR %s) (scrolltarget %" PRIu64 ")",
5458 ActiveScrolledRoot::ToString(mScrollTargetASR).get(),
5459 GetScrollTargetId())
5460 .get();
5463 TableType GetTableTypeFromFrame(nsIFrame* aFrame) {
5464 if (aFrame->IsTableFrame()) {
5465 return TableType::Table;
5468 if (aFrame->IsTableColFrame()) {
5469 return TableType::TableCol;
5472 if (aFrame->IsTableColGroupFrame()) {
5473 return TableType::TableColGroup;
5476 if (aFrame->IsTableRowFrame()) {
5477 return TableType::TableRow;
5480 if (aFrame->IsTableRowGroupFrame()) {
5481 return TableType::TableRowGroup;
5484 if (aFrame->IsTableCellFrame()) {
5485 return TableType::TableCell;
5488 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5489 return TableType::Table;
5492 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5493 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5494 nsIFrame* aAncestorFrame, const ActiveScrolledRoot* aScrollTargetASR)
5495 : nsDisplayFixedPosition(aBuilder, aFrame, aList, aScrollTargetASR),
5496 mAncestorFrame(aAncestorFrame) {
5497 if (aBuilder->IsRetainingDisplayList()) {
5498 mAncestorFrame->AddDisplayItem(this);
5502 nsDisplayStickyPosition::nsDisplayStickyPosition(
5503 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5504 const ActiveScrolledRoot* aActiveScrolledRoot,
5505 const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
5506 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5507 mContainerASR(aContainerASR),
5508 mClippedToDisplayPort(aClippedToDisplayPort),
5509 mShouldFlatten(false) {
5510 MOZ_COUNT_CTOR(nsDisplayStickyPosition);
5513 // Returns the smallest distance from "0" to the range [min, max] where
5514 // min <= max. Despite the name, the return value is actually a 1-D vector,
5515 // and so may be negative if max < 0.
5516 static nscoord DistanceToRange(nscoord min, nscoord max) {
5517 MOZ_ASSERT(min <= max);
5518 if (max < 0) {
5519 return max;
5521 if (min > 0) {
5522 return min;
5524 MOZ_ASSERT(min <= 0 && max >= 0);
5525 return 0;
5528 // Returns the magnitude of the part of the range [min, max] that is greater
5529 // than zero. The return value is always non-negative.
5530 static nscoord PositivePart(nscoord min, nscoord max) {
5531 MOZ_ASSERT(min <= max);
5532 if (min >= 0) {
5533 return max - min;
5535 if (max > 0) {
5536 return max;
5538 return 0;
5541 // Returns the magnitude of the part of the range [min, max] that is less
5542 // than zero. The return value is always non-negative.
5543 static nscoord NegativePart(nscoord min, nscoord max) {
5544 MOZ_ASSERT(min <= max);
5545 if (max <= 0) {
5546 return max - min;
5548 if (min < 0) {
5549 return 0 - min;
5551 return 0;
5554 StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() {
5555 StickyScrollContainer* stickyScrollContainer =
5556 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
5557 if (stickyScrollContainer) {
5558 // If there's no ASR for the scrollframe that this sticky item is attached
5559 // to, then don't create a WR sticky item for it either. Trying to do so
5560 // will end in sadness because WR will interpret some coordinates as
5561 // relative to the nearest enclosing scrollframe, which will correspond
5562 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5563 // same as the scrollframe this sticky item is actually supposed to be
5564 // attached to, thus the sadness.
5565 // Not sending WR the sticky item is ok, because the enclosing scrollframe
5566 // will never be asynchronously scrolled. Instead we will always position
5567 // the sticky items correctly on the gecko side and WR will never need to
5568 // adjust their position itself.
5569 MOZ_ASSERT(
5570 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5571 if (!stickyScrollContainer->ScrollFrame()
5572 ->IsMaybeAsynchronouslyScrolled()) {
5573 stickyScrollContainer = nullptr;
5576 return stickyScrollContainer;
5579 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5580 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5581 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5582 nsDisplayListBuilder* aDisplayListBuilder) {
5583 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5585 Maybe<wr::SpaceAndClipChainHelper> saccHelper;
5587 if (stickyScrollContainer) {
5588 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5590 bool snap;
5591 nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
5593 Maybe<float> topMargin;
5594 Maybe<float> rightMargin;
5595 Maybe<float> bottomMargin;
5596 Maybe<float> leftMargin;
5597 wr::StickyOffsetBounds vBounds = {0.0, 0.0};
5598 wr::StickyOffsetBounds hBounds = {0.0, 0.0};
5599 nsPoint appliedOffset;
5601 nsRectAbsolute outer;
5602 nsRectAbsolute inner;
5603 stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5605 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
5606 nsPoint offset =
5607 scrollFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5609 // Adjust the scrollPort coordinates to be relative to the reference frame,
5610 // so that it is in the same space as everything else.
5611 nsRect scrollPort =
5612 stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
5613 scrollPort += offset;
5615 // The following computations make more sense upon understanding the
5616 // semantics of "inner" and "outer", which is explained in the comment on
5617 // SetStickyPositionData in Layers.h.
5619 if (outer.YMost() != inner.YMost()) {
5620 // Question: How far will itemBounds.y be from the top of the scrollport
5621 // when we have scrolled from the current scroll position of "0" to
5622 // reach the range [inner.YMost(), outer.YMost()] where the item gets
5623 // stuck?
5624 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5625 // needs to be adjusted by the distance to the range, less any other
5626 // sticky ranges that fall between 0 and the range. If the distance is
5627 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5628 // scrolling upwards (decreasing scroll offset) to reach that range,
5629 // which would increase itemBounds.y and make it farther away from the
5630 // top of the scrollport. So in that case the adjustment is -distance.
5631 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5632 // we would be scrolling downwards, itemBounds.y would decrease, and we
5633 // again need to adjust by -distance. If we are already in the range
5634 // then no adjustment is needed and distance is 0 so again using
5635 // -distance works. If the distance is positive, and the item has both
5636 // top and bottom sticky ranges, then the bottom sticky range may fall
5637 // (entirely[1] or partly[2]) between the current scroll position.
5638 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5639 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5640 // In these cases, the item doesn't actually move for that part of the
5641 // distance, so we need to subtract out that bit, which can be computed
5642 // as the positive portion of the range [outer.Y(), inner.Y()].
5643 nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
5644 if (distance > 0) {
5645 distance -= PositivePart(outer.Y(), inner.Y());
5647 topMargin = Some(NSAppUnitsToFloatPixels(
5648 itemBounds.y - scrollPort.y - distance, auPerDevPixel));
5649 // Question: What is the maximum positive ("downward") offset that WR
5650 // will have to apply to this item in order to prevent the item from
5651 // visually moving?
5652 // Answer: Since the item is "sticky" in the range [inner.YMost(),
5653 // outer.YMost()], the maximum offset will be the size of the range, which
5654 // is outer.YMost() - inner.YMost().
5655 vBounds.max =
5656 NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
5657 // Question: how much of an offset has layout already applied to the item?
5658 // Answer: if we are
5659 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5660 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5661 // then layout has already applied some offset to the position of the
5662 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5663 // and |outer.YMost() - inner.YMost()| in case (b).
5664 if (inner.YMost() < 0) {
5665 appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
5666 MOZ_ASSERT(appliedOffset.y > 0);
5669 if (outer.Y() != inner.Y()) {
5670 // Similar logic as in the previous section, but this time we care about
5671 // the distance from itemBounds.YMost() to scrollPort.YMost().
5672 nscoord distance = DistanceToRange(outer.Y(), inner.Y());
5673 if (distance < 0) {
5674 distance += NegativePart(inner.YMost(), outer.YMost());
5676 bottomMargin = Some(NSAppUnitsToFloatPixels(
5677 scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
5678 // And here WR will be moving the item upwards rather than downwards so
5679 // again things are inverted from the previous block.
5680 vBounds.min =
5681 NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
5682 // We can't have appliedOffset be both positive and negative, and the top
5683 // adjustment takes priority. So here we only update appliedOffset.y if
5684 // it wasn't set by the top-sticky case above.
5685 if (appliedOffset.y == 0 && inner.Y() > 0) {
5686 appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
5687 MOZ_ASSERT(appliedOffset.y < 0);
5690 // Same as above, but for the x-axis
5691 if (outer.XMost() != inner.XMost()) {
5692 nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
5693 if (distance > 0) {
5694 distance -= PositivePart(outer.X(), inner.X());
5696 leftMargin = Some(NSAppUnitsToFloatPixels(
5697 itemBounds.x - scrollPort.x - distance, auPerDevPixel));
5698 hBounds.max =
5699 NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
5700 if (inner.XMost() < 0) {
5701 appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
5702 MOZ_ASSERT(appliedOffset.x > 0);
5705 if (outer.X() != inner.X()) {
5706 nscoord distance = DistanceToRange(outer.X(), inner.X());
5707 if (distance < 0) {
5708 distance += NegativePart(inner.XMost(), outer.XMost());
5710 rightMargin = Some(NSAppUnitsToFloatPixels(
5711 scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
5712 hBounds.min =
5713 NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
5714 if (appliedOffset.x == 0 && inner.X() > 0) {
5715 appliedOffset.x = std::max(0, outer.X()) - inner.X();
5716 MOZ_ASSERT(appliedOffset.x < 0);
5720 LayoutDeviceRect bounds =
5721 LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
5722 wr::LayoutVector2D applied = {
5723 NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
5724 NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
5725 wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame(
5726 wr::ToLayoutRect(bounds), topMargin.ptrOr(nullptr),
5727 rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
5728 leftMargin.ptrOr(nullptr), vBounds, hBounds, applied,
5729 wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5730 wr::SpatialKeyKind::Sticky));
5732 saccHelper.emplace(aBuilder, spatialId);
5733 aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId);
5737 wr::StackingContextParams params;
5738 params.clip =
5739 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5740 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
5741 aBuilder, params);
5742 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc,
5743 aManager, aDisplayListBuilder);
5746 if (stickyScrollContainer) {
5747 aManager->CommandBuilder().PopOverrideForASR(mContainerASR);
5750 return true;
5753 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5754 StickyScrollContainer* aStickyScrollContainer, float aAppUnitsPerDevPixel,
5755 float aScaleX, float aScaleY, LayerRectAbsolute& aStickyOuter,
5756 LayerRectAbsolute& aStickyInner) {
5757 nsRectAbsolute outer;
5758 nsRectAbsolute inner;
5759 aStickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5760 aStickyOuter.SetBox(
5761 NSAppUnitsToFloatPixels(outer.X(), aAppUnitsPerDevPixel) * aScaleX,
5762 NSAppUnitsToFloatPixels(outer.Y(), aAppUnitsPerDevPixel) * aScaleY,
5763 NSAppUnitsToFloatPixels(outer.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5764 NSAppUnitsToFloatPixels(outer.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5765 aStickyInner.SetBox(
5766 NSAppUnitsToFloatPixels(inner.X(), aAppUnitsPerDevPixel) * aScaleX,
5767 NSAppUnitsToFloatPixels(inner.Y(), aAppUnitsPerDevPixel) * aScaleY,
5768 NSAppUnitsToFloatPixels(inner.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5769 NSAppUnitsToFloatPixels(inner.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5772 bool nsDisplayStickyPosition::UpdateScrollData(
5773 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5774 bool hasDynamicToolbar = HasDynamicToolbar();
5775 if (aLayerData && hasDynamicToolbar) {
5776 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5777 if (stickyScrollContainer) {
5778 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5779 float cumulativeResolution =
5780 mFrame->PresShell()->GetCumulativeResolution();
5781 LayerRectAbsolute stickyOuter;
5782 LayerRectAbsolute stickyInner;
5783 CalculateLayerScrollRanges(stickyScrollContainer, auPerDevPixel,
5784 cumulativeResolution, cumulativeResolution,
5785 stickyOuter, stickyInner);
5786 aLayerData->SetStickyScrollRangeOuter(stickyOuter);
5787 aLayerData->SetStickyScrollRangeInner(stickyInner);
5789 SideBits sides =
5790 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5791 aLayerData->SetFixedPositionSides(sides);
5793 ScrollableLayerGuid::ViewID scrollId =
5794 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5795 ->GetScrolledFrame()
5796 ->GetContent());
5797 aLayerData->SetStickyPositionScrollContainerId(scrollId);
5800 // Return true if either there is a dynamic toolbar affecting this sticky
5801 // item or the OwnLayer base implementation returns true for some other
5802 // reason.
5803 bool ret = hasDynamicToolbar;
5804 ret |= nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5805 return ret;
5808 bool nsDisplayStickyPosition::ShouldGetFixedOrStickyAnimationId() {
5809 #if defined(MOZ_WIDGET_ANDROID)
5810 if (HasDynamicToolbar()) { // also implies being in the cross-process RCD
5811 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5812 if (stickyScrollContainer) {
5813 ScrollableLayerGuid::ViewID scrollId =
5814 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5815 ->GetScrolledFrame()
5816 ->GetContent());
5817 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) ==
5818 scrollId;
5821 #endif
5822 return false;
5825 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5826 nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
5827 nsIFrame* aScrollFrame, const CompositorHitTestInfo& aHitInfo,
5828 const nsRect& aHitArea)
5829 : nsDisplayWrapList(aBuilder, aScrollFrame),
5830 mScrollFrame(aScrollFrame),
5831 mScrolledFrame(aScrolledFrame),
5832 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5833 mHitInfo(aHitInfo),
5834 mHitArea(aHitArea) {
5835 #ifdef NS_BUILD_REFCNT_LOGGING
5836 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
5837 #endif
5840 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5841 nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) {
5842 ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
5843 mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(),
5844 ToReferenceFrame(), aLayerManager, mScrollParentId,
5845 mScrollFrame->GetSize(), false);
5846 metadata.GetMetrics().SetIsScrollInfoLayer(true);
5847 nsIScrollableFrame* scrollableFrame = mScrollFrame->GetScrollTargetFrame();
5848 if (scrollableFrame) {
5849 aBuilder->AddScrollFrameToNotify(scrollableFrame);
5852 return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
5855 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5856 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5857 if (aLayerData) {
5858 UniquePtr<ScrollMetadata> metadata =
5859 ComputeScrollMetadata(aData->GetBuilder(), aData->GetManager());
5860 MOZ_ASSERT(aData);
5861 MOZ_ASSERT(metadata);
5862 aLayerData->AppendScrollMetadata(*aData, *metadata);
5864 return true;
5867 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5868 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5869 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5870 nsDisplayListBuilder* aDisplayListBuilder) {
5871 ScrollableLayerGuid::ViewID scrollId =
5872 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame->GetContent());
5874 const LayoutDeviceRect devRect = LayoutDeviceRect::FromAppUnits(
5875 mHitArea, mScrollFrame->PresContext()->AppUnitsPerDevPixel());
5877 const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
5879 aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, mHitInfo,
5880 SideBits::eNone);
5882 return true;
5885 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) {
5886 aStream << " (scrollframe " << mScrollFrame << " scrolledFrame "
5887 << mScrolledFrame << ")";
5890 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5891 nsSubDocumentFrame* aSubDocFrame,
5892 nsDisplayList* aList, int32_t aAPD,
5893 int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags)
5894 : nsDisplaySubDocument(aBuilder, aFrame, aSubDocFrame, aList, aFlags),
5895 mAPD(aAPD),
5896 mParentAPD(aParentAPD) {
5897 MOZ_COUNT_CTOR(nsDisplayZoom);
5900 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder,
5901 bool* aSnap) const {
5902 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
5903 *aSnap = false;
5904 return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD);
5907 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5908 HitTestState* aState,
5909 nsTArray<nsIFrame*>* aOutFrames) {
5910 nsRect rect;
5911 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5912 // rect as well instead of possibly rounding the width or height to zero.
5913 if (aRect.width == 1 && aRect.height == 1) {
5914 rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD));
5915 rect.width = rect.height = 1;
5916 } else {
5917 rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
5919 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5922 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5923 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5924 const ActiveScrolledRoot* aActiveScrolledRoot, FrameMetrics::ViewID aViewID)
5925 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5926 mViewID(aViewID) {
5927 MOZ_COUNT_CTOR(nsDisplayAsyncZoom);
5930 #ifdef NS_BUILD_REFCNT_LOGGING
5931 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5932 MOZ_COUNT_DTOR(nsDisplayAsyncZoom);
5934 #endif
5936 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder* aBuilder,
5937 const nsRect& aRect, HitTestState* aState,
5938 nsTArray<nsIFrame*>* aOutFrames) {
5939 #ifdef DEBUG
5940 nsIScrollableFrame* scrollFrame = do_QueryFrame(mFrame);
5941 MOZ_ASSERT(scrollFrame && ViewportUtils::IsZoomedContentRoot(
5942 scrollFrame->GetScrolledFrame()));
5943 #endif
5944 nsRect rect = ViewportUtils::VisualToLayout(aRect, mFrame->PresShell());
5945 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5948 bool nsDisplayAsyncZoom::UpdateScrollData(
5949 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5950 bool ret = nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5951 MOZ_ASSERT(ret);
5952 if (aLayerData) {
5953 aLayerData->SetAsyncZoomContainerId(mViewID);
5955 return ret;
5958 ///////////////////////////////////////////////////
5959 // nsDisplayTransform Implementation
5962 #ifndef DEBUG
5963 static_assert(sizeof(nsDisplayTransform) <= 512,
5964 "nsDisplayTransform has grown");
5965 #endif
5967 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5968 nsIFrame* aFrame, nsDisplayList* aList,
5969 const nsRect& aChildrenBuildingRect)
5970 : nsPaintedDisplayItem(aBuilder, aFrame),
5971 mChildren(aBuilder),
5972 mTransform(Some(Matrix4x4())),
5973 mChildrenBuildingRect(aChildrenBuildingRect),
5974 mPrerenderDecision(PrerenderDecision::No),
5975 mIsTransformSeparator(true),
5976 mHasTransformGetter(false),
5977 mHasAssociatedPerspective(false) {
5978 MOZ_COUNT_CTOR(nsDisplayTransform);
5979 MOZ_ASSERT(aFrame, "Must have a frame!");
5980 Init(aBuilder, aList);
5983 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5984 nsIFrame* aFrame, nsDisplayList* aList,
5985 const nsRect& aChildrenBuildingRect,
5986 PrerenderDecision aPrerenderDecision)
5987 : nsPaintedDisplayItem(aBuilder, aFrame),
5988 mChildren(aBuilder),
5989 mChildrenBuildingRect(aChildrenBuildingRect),
5990 mPrerenderDecision(aPrerenderDecision),
5991 mIsTransformSeparator(false),
5992 mHasTransformGetter(false),
5993 mHasAssociatedPerspective(false) {
5994 MOZ_COUNT_CTOR(nsDisplayTransform);
5995 MOZ_ASSERT(aFrame, "Must have a frame!");
5996 SetReferenceFrameToAncestor(aBuilder);
5997 Init(aBuilder, aList);
6000 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
6001 nsIFrame* aFrame, nsDisplayList* aList,
6002 const nsRect& aChildrenBuildingRect,
6003 decltype(WithTransformGetter))
6004 : nsPaintedDisplayItem(aBuilder, aFrame),
6005 mChildren(aBuilder),
6006 mChildrenBuildingRect(aChildrenBuildingRect),
6007 mPrerenderDecision(PrerenderDecision::No),
6008 mIsTransformSeparator(false),
6009 mHasTransformGetter(true),
6010 mHasAssociatedPerspective(false) {
6011 MOZ_COUNT_CTOR(nsDisplayTransform);
6012 MOZ_ASSERT(aFrame, "Must have a frame!");
6013 MOZ_ASSERT(aFrame->GetTransformGetter());
6014 Init(aBuilder, aList);
6017 void nsDisplayTransform::SetReferenceFrameToAncestor(
6018 nsDisplayListBuilder* aBuilder) {
6019 if (mFrame == aBuilder->RootReferenceFrame()) {
6020 return;
6022 // We manually recompute mToReferenceFrame without going through the
6023 // builder, since this won't apply the 'additional offset'. Our
6024 // children will already be painting with that applied, and we don't
6025 // want to include it a second time in our transform. We don't recompute
6026 // our visible/building rects, since those should still include the additional
6027 // offset.
6028 // TODO: Are there are things computed using our ToReferenceFrame that should
6029 // have the additional offset applied? Should we instead just manually remove
6030 // the offset from our transform instead of this more general value?
6031 // Can we instead apply the additional offset to us and not our children, like
6032 // we do for all other offsets (and how reference frames are supposed to
6033 // work)?
6034 nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame);
6035 const nsIFrame* referenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
6036 mToReferenceFrame = mFrame->GetOffsetToCrossDoc(referenceFrame);
6039 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
6040 nsDisplayList* aChildren) {
6041 mChildren.AppendToTop(aChildren);
6042 UpdateBounds(aBuilder);
6045 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
6046 return false;
6049 /* Returns the delta specified by the transform-origin property.
6050 * This is a positive delta, meaning that it indicates the direction to move
6051 * to get from (0, 0) of the frame to the transform origin. This function is
6052 * called off the main thread.
6054 /* static */
6055 Point3D nsDisplayTransform::GetDeltaToTransformOrigin(
6056 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6057 float aAppUnitsPerPixel) {
6058 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6059 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6060 aFrame->Combines3DTransformWithAncestors(),
6061 "Shouldn't get a delta for an untransformed frame!");
6063 if (!aFrame->IsTransformed()) {
6064 return Point3D();
6067 /* For both of the coordinates, if the value of transform is a
6068 * percentage, it's relative to the size of the frame. Otherwise, if it's
6069 * a distance, it's already computed for us!
6071 const nsStyleDisplay* display = aFrame->StyleDisplay();
6073 const StyleTransformOrigin& transformOrigin = display->mTransformOrigin;
6074 CSSPoint origin = nsStyleTransformMatrix::Convert2DPosition(
6075 transformOrigin.horizontal, transformOrigin.vertical, aRefBox);
6077 // Note:
6078 // 1. SVG frames have a reference box that can be (and typically is) offset
6079 // from the TopLeft() of the frame. We need to account for that here.
6080 // 2. If we are using transform-box:content-box in CSS layout, we have the
6081 // offset from TopLeft() of the frame as well.
6082 origin.x += CSSPixel::FromAppUnits(aRefBox.X());
6083 origin.y += CSSPixel::FromAppUnits(aRefBox.Y());
6085 float scale = AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel);
6086 float z = transformOrigin.depth._0;
6087 return Point3D(origin.x * scale, origin.y * scale, z * scale);
6090 /* static */
6091 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
6092 float aAppUnitsPerPixel,
6093 Matrix4x4& aOutMatrix) {
6094 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6095 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6096 aFrame->Combines3DTransformWithAncestors(),
6097 "Shouldn't get a delta for an untransformed frame!");
6098 MOZ_ASSERT(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
6100 if (!aFrame->IsTransformed()) {
6101 return false;
6104 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6105 // correctly yet (similar to the aBoundsOverride case for
6106 // GetResultingTransformMatrix)?
6107 nsIFrame* perspectiveFrame =
6108 aFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6109 if (!perspectiveFrame) {
6110 return false;
6113 /* Grab the values for perspective and perspective-origin (if present) */
6114 const nsStyleDisplay* perspectiveDisplay = perspectiveFrame->StyleDisplay();
6115 if (perspectiveDisplay->mChildPerspective.IsNone()) {
6116 return false;
6119 MOZ_ASSERT(perspectiveDisplay->mChildPerspective.IsLength());
6120 float perspective =
6121 perspectiveDisplay->mChildPerspective.AsLength().ToCSSPixels();
6122 perspective = std::max(1.0f, perspective);
6123 if (perspective < std::numeric_limits<Float>::epsilon()) {
6124 return true;
6127 TransformReferenceBox refBox(perspectiveFrame);
6129 Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition(
6130 perspectiveDisplay->mPerspectiveOrigin.horizontal,
6131 perspectiveDisplay->mPerspectiveOrigin.vertical, refBox,
6132 aAppUnitsPerPixel);
6134 /* GetOffsetTo computes the offset required to move from 0,0 in
6135 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6136 * this, it's faster to compute this way.
6138 nsPoint frameToPerspectiveOffset = -aFrame->GetOffsetTo(perspectiveFrame);
6139 Point frameToPerspectiveGfxOffset(
6140 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.x, aAppUnitsPerPixel),
6141 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.y, aAppUnitsPerPixel));
6143 /* Move the perspective origin to be relative to aFrame, instead of relative
6144 * to the containing block which is how it was specified in the style system.
6146 perspectiveOrigin += frameToPerspectiveGfxOffset;
6148 aOutMatrix._34 =
6149 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective),
6150 aAppUnitsPerPixel);
6152 aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
6153 return true;
6156 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6157 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6158 float aAppUnitsPerPixel)
6159 : mFrame(aFrame),
6160 mTranslate(aFrame->StyleDisplay()->mTranslate),
6161 mRotate(aFrame->StyleDisplay()->mRotate),
6162 mScale(aFrame->StyleDisplay()->mScale),
6163 mTransform(aFrame->StyleDisplay()->mTransform),
6164 mMotion(aFrame->StyleDisplay()->mOffsetPath.IsNone()
6165 ? Nothing()
6166 : MotionPathUtils::ResolveMotionPath(aFrame, aRefBox)),
6167 mToTransformOrigin(
6168 GetDeltaToTransformOrigin(aFrame, aRefBox, aAppUnitsPerPixel)) {}
6170 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6171 * translates from local coordinate space to transform coordinate space, then
6172 * hands it back.
6174 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6175 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6176 float aAppUnitsPerPixel) {
6177 return GetResultingTransformMatrixInternal(aProperties, aRefBox, nsPoint(),
6178 aAppUnitsPerPixel, 0);
6181 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6182 const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel,
6183 uint32_t aFlags) {
6184 TransformReferenceBox refBox(aFrame);
6185 FrameTransformProperties props(aFrame, refBox, aAppUnitsPerPixel);
6186 return GetResultingTransformMatrixInternal(props, refBox, aOrigin,
6187 aAppUnitsPerPixel, aFlags);
6190 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal(
6191 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6192 const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags) {
6193 const nsIFrame* frame = aProperties.mFrame;
6194 NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE),
6195 "Must have a frame to compute perspective!");
6197 // Get the underlying transform matrix:
6199 /* Get the matrix, then change its basis to factor in the origin. */
6200 Matrix4x4 result;
6201 // Call IsSVGTransformed() regardless of the value of
6202 // aProperties.HasTransform(), since we still need any
6203 // potential parentsChildrenOnlyTransform.
6204 Matrix svgTransform, parentsChildrenOnlyTransform;
6205 const bool hasSVGTransforms =
6206 frame && frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
6207 frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform);
6208 bool shouldRound = nsLayoutUtils::ShouldSnapToGrid(frame);
6210 /* Transformed frames always have a transform, or are preserving 3d (and might
6211 * still have perspective!) */
6212 if (aProperties.HasTransform()) {
6213 result = nsStyleTransformMatrix::ReadTransforms(
6214 aProperties.mTranslate, aProperties.mRotate, aProperties.mScale,
6215 aProperties.mMotion.ptrOr(nullptr), aProperties.mTransform, aRefBox,
6216 aAppUnitsPerPixel);
6217 } else if (hasSVGTransforms) {
6218 // Correct the translation components for zoom:
6219 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6220 svgTransform._31 *= pixelsPerCSSPx;
6221 svgTransform._32 *= pixelsPerCSSPx;
6222 result = Matrix4x4::From2D(svgTransform);
6225 // Apply any translation due to 'transform-origin' and/or 'transform-box':
6226 result.ChangeBasis(aProperties.mToTransformOrigin);
6228 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
6229 // an explanation of what children-only transforms are.
6230 const bool parentHasChildrenOnlyTransform =
6231 hasSVGTransforms && !parentsChildrenOnlyTransform.IsIdentity();
6233 if (parentHasChildrenOnlyTransform) {
6234 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6235 parentsChildrenOnlyTransform._31 *= pixelsPerCSSPx;
6236 parentsChildrenOnlyTransform._32 *= pixelsPerCSSPx;
6238 Point3D frameOffset(
6239 NSAppUnitsToFloatPixels(-frame->GetPosition().x, aAppUnitsPerPixel),
6240 NSAppUnitsToFloatPixels(-frame->GetPosition().y, aAppUnitsPerPixel), 0);
6241 Matrix4x4 parentsChildrenOnlyTransform3D =
6242 Matrix4x4::From2D(parentsChildrenOnlyTransform)
6243 .ChangeBasis(frameOffset);
6245 result *= parentsChildrenOnlyTransform3D;
6248 Matrix4x4 perspectiveMatrix;
6249 bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE;
6250 if (hasPerspective) {
6251 if (ComputePerspectiveMatrix(frame, aAppUnitsPerPixel, perspectiveMatrix)) {
6252 result *= perspectiveMatrix;
6256 if ((aFlags & INCLUDE_PRESERVE3D_ANCESTORS) && frame &&
6257 frame->Combines3DTransformWithAncestors()) {
6258 // Include the transform set on our parent
6259 nsIFrame* parentFrame =
6260 frame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6261 NS_ASSERTION(parentFrame && parentFrame->IsTransformed() &&
6262 parentFrame->Extend3DContext(),
6263 "Preserve3D mismatch!");
6264 TransformReferenceBox refBox(parentFrame);
6265 FrameTransformProperties props(parentFrame, refBox, aAppUnitsPerPixel);
6267 uint32_t flags =
6268 aFlags & (INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE);
6270 // If this frame isn't transformed (but we exist for backface-visibility),
6271 // then we're not a reference frame so no offset to origin will be added.
6272 // Otherwise we need to manually translate into our parent's coordinate
6273 // space.
6274 if (frame->IsTransformed()) {
6275 nsLayoutUtils::PostTranslate(result, frame->GetPosition(),
6276 aAppUnitsPerPixel, shouldRound);
6278 Matrix4x4 parent = GetResultingTransformMatrixInternal(
6279 props, refBox, nsPoint(0, 0), aAppUnitsPerPixel, flags);
6280 result = result * parent;
6283 if (aFlags & OFFSET_BY_ORIGIN) {
6284 nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel,
6285 shouldRound);
6288 return result;
6291 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6292 static constexpr nsCSSPropertyIDSet opacitySet =
6293 nsCSSPropertyIDSet::OpacityProperties();
6294 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, opacitySet)) {
6295 return true;
6298 EffectCompositor::SetPerformanceWarning(
6299 mFrame, opacitySet,
6300 AnimationPerformanceWarning(
6301 AnimationPerformanceWarning::Type::OpacityFrameInactive));
6303 return false;
6306 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6307 return mPrerenderDecision != PrerenderDecision::No;
6310 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
6311 nsDisplayListBuilder* aBuilder) {
6312 return StaticPrefs::gfx_omta_background_color();
6315 static bool IsInStickyPositionedSubtree(const nsIFrame* aFrame) {
6316 for (const nsIFrame* frame = aFrame; frame;
6317 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame)) {
6318 if (frame->IsStickyPositioned()) {
6319 return true;
6322 return false;
6325 static bool ShouldUsePartialPrerender(const nsIFrame* aFrame) {
6326 return StaticPrefs::layout_animation_prerender_partial() &&
6327 // Bug 1642547: Support partial prerender for position:sticky elements.
6328 !IsInStickyPositionedSubtree(aFrame);
6331 /* static */
6332 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
6333 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect)
6334 -> PrerenderInfo {
6335 PrerenderInfo result;
6336 // If we are in a preserve-3d tree, and we've disallowed async animations, we
6337 // return No prerender decision directly.
6338 if ((aFrame->Extend3DContext() ||
6339 aFrame->Combines3DTransformWithAncestors()) &&
6340 !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
6341 return result;
6344 // Elements whose transform has been modified recently, or which
6345 // have a compositor-animated transform, can be prerendered. An element
6346 // might have only just had its transform animated in which case
6347 // the ActiveLayerManager may not have been notified yet.
6348 static constexpr nsCSSPropertyIDSet transformSet =
6349 nsCSSPropertyIDSet::TransformLikeProperties();
6350 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame) &&
6351 !EffectCompositor::HasAnimationsForCompositor(
6352 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
6353 EffectCompositor::SetPerformanceWarning(
6354 aFrame, transformSet,
6355 AnimationPerformanceWarning(
6356 AnimationPerformanceWarning::Type::TransformFrameInactive));
6358 // This case happens when we're sure that the frame is not animated and its
6359 // preserve-3d ancestors are not, either. So we don't need to pre-render.
6360 // However, this decision shouldn't affect the decisions for other frames in
6361 // the preserve-3d context. We need this flag to determine whether we should
6362 // block async animations on other frames in the current preserve-3d tree.
6363 result.mHasAnimations = false;
6364 return result;
6367 // We should not allow prerender if any ancestor container element has
6368 // mask/clip-path effects.
6370 // With prerender and async transform animation, we do not need to restyle an
6371 // animated element to respect position changes, since that transform is done
6372 // by layer animation. As a result, the container element is not aware of
6373 // position change of that containing element and loses the chance to update
6374 // the content of mask/clip-path.
6376 // Why do we need to update a mask? This is relative to how we generate a
6377 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
6378 // mask layer, to reduce memory usage, we did not choose the size of the
6379 // masked element as mask size. Instead, we read the union of bounds of all
6380 // children display items by nsDisplayWrapList::GetBounds, which is smaller
6381 // than or equal to the masked element's boundary, and use it as the position
6382 // size of the mask layer. That union bounds is actually affected by the
6383 // geometry of the animated element. To keep the content of mask up to date,
6384 // forbidding of prerender is required.
6385 for (nsIFrame* container =
6386 nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
6387 container;
6388 container = nsLayoutUtils::GetCrossDocParentFrameInProcess(container)) {
6389 const nsStyleSVGReset* svgReset = container->StyleSVGReset();
6390 if (svgReset->HasMask() || svgReset->HasClipPath()) {
6391 return result;
6395 // If the incoming dirty rect already contains the entire overflow area,
6396 // we are already rendering the entire content.
6397 nsRect overflow = aFrame->InkOverflowRectRelativeToSelf();
6398 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
6399 // in cases of non-invertible transforms, so we set `untransformedRect` to
6400 // `aDirtyRect` as an initial value for such cases.
6401 nsRect untransformedDirtyRect = *aDirtyRect;
6402 UntransformRect(*aDirtyRect, overflow, aFrame, &untransformedDirtyRect);
6403 if (untransformedDirtyRect.Contains(overflow)) {
6404 *aDirtyRect = untransformedDirtyRect;
6405 result.mDecision = PrerenderDecision::Full;
6406 return result;
6409 float viewportRatio =
6410 StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
6411 uint32_t absoluteLimitX =
6412 StaticPrefs::layout_animation_prerender_absolute_limit_x();
6413 uint32_t absoluteLimitY =
6414 StaticPrefs::layout_animation_prerender_absolute_limit_y();
6415 nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
6417 float resolution = aFrame->PresShell()->GetCumulativeResolution();
6418 if (resolution < 1.0f) {
6419 refSize.SizeTo(
6420 NSCoordSaturatingNonnegativeMultiply(refSize.width, 1.0f / resolution),
6421 NSCoordSaturatingNonnegativeMultiply(refSize.height,
6422 1.0f / resolution));
6425 // Only prerender if the transformed frame's size is <= a multiple of the
6426 // reference frame size (~viewport), and less than an absolute limit.
6427 // Both the ratio and the absolute limit are configurable.
6428 nscoord maxLength = std::max(nscoord(refSize.width * viewportRatio),
6429 nscoord(refSize.height * viewportRatio));
6430 nsSize relativeLimit(maxLength, maxLength);
6431 nsSize absoluteLimit(
6432 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
6433 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
6434 nsSize maxSize = Min(relativeLimit, absoluteLimit);
6436 const auto transform = nsLayoutUtils::GetTransformToAncestor(
6437 RelativeTo{aFrame},
6438 RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});
6439 const gfxRect transformedBounds = transform.TransformAndClipBounds(
6440 gfxRect(overflow.x, overflow.y, overflow.width, overflow.height),
6441 gfxRect::MaxIntRect());
6442 const nsSize frameSize =
6443 nsSize(transformedBounds.width, transformedBounds.height);
6445 uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
6446 uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
6447 if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
6448 *aDirtyRect = overflow;
6449 result.mDecision = PrerenderDecision::Full;
6450 return result;
6453 if (ShouldUsePartialPrerender(aFrame)) {
6454 *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(
6455 aFrame, untransformedDirtyRect, overflow, maxSize);
6456 result.mDecision = PrerenderDecision::Partial;
6457 return result;
6460 if (frameArea > maxLimitArea) {
6461 uint64_t appUnitsPerPixel = AppUnitsPerCSSPixel();
6462 EffectCompositor::SetPerformanceWarning(
6463 aFrame, transformSet,
6464 AnimationPerformanceWarning(
6465 AnimationPerformanceWarning::Type::ContentTooLargeArea,
6467 int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
6468 int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
6469 }));
6470 } else {
6471 EffectCompositor::SetPerformanceWarning(
6472 aFrame, transformSet,
6473 AnimationPerformanceWarning(
6474 AnimationPerformanceWarning::Type::ContentTooLarge,
6476 nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
6477 nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
6478 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
6479 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
6480 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
6481 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
6482 }));
6485 return result;
6488 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
6489 * visible or hit. */
6490 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix) {
6491 if (aMatrix.IsSingular()) {
6492 return false;
6494 if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) {
6495 return false;
6497 return true;
6500 const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const {
6501 if (mTransform) {
6502 return *mTransform;
6505 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6507 if (mHasTransformGetter) {
6508 mTransform.emplace((mFrame->GetTransformGetter())(mFrame, scale));
6509 Point3D newOrigin =
6510 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
6511 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f);
6512 mTransform->ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
6513 } else if (!mIsTransformSeparator) {
6514 DebugOnly<bool> isReference = mFrame->IsTransformed() ||
6515 mFrame->Combines3DTransformWithAncestors() ||
6516 mFrame->Extend3DContext();
6517 MOZ_ASSERT(isReference);
6518 mTransform.emplace(
6519 GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale,
6520 INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN));
6521 } else {
6522 // Use identity matrix
6523 mTransform.emplace();
6526 return *mTransform;
6529 const Matrix4x4Flagged& nsDisplayTransform::GetInverseTransform() const {
6530 if (mInverseTransform) {
6531 return *mInverseTransform;
6534 MOZ_ASSERT(!GetTransform().IsSingular());
6536 mInverseTransform.emplace(GetTransform().Inverse());
6538 return *mInverseTransform;
6541 Matrix4x4 nsDisplayTransform::GetTransformForRendering(
6542 LayoutDevicePoint* aOutOrigin) const {
6543 if (!mFrame->HasPerspective() || mHasTransformGetter ||
6544 mIsTransformSeparator) {
6545 if (!mHasTransformGetter && !mIsTransformSeparator && aOutOrigin) {
6546 // If aOutOrigin is provided, put the offset to origin into it, because
6547 // we need to keep it separate for webrender. The combination of
6548 // *aOutOrigin and the returned matrix here should always be equivalent
6549 // to what GetTransform() would have returned.
6550 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6551 *aOutOrigin = LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale);
6553 // The rounding behavior should also be the same as GetTransform().
6554 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6555 aOutOrigin->Round();
6557 return GetResultingTransformMatrix(mFrame, nsPoint(0, 0), scale,
6558 INCLUDE_PERSPECTIVE);
6560 return GetTransform().GetMatrix();
6562 MOZ_ASSERT(!mHasTransformGetter);
6564 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6565 // Don't include perspective transform, or the offset to origin, since
6566 // nsDisplayPerspective will handle both of those.
6567 return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
6570 const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
6571 nsDisplayListBuilder* aBuilder) {
6572 MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
6574 if (!IsLeafOf3DContext()) {
6575 return GetTransform().GetMatrix();
6578 if (!mTransformPreserves3D) {
6579 const nsIFrame* establisher; // Establisher of the 3D rendering context.
6580 for (establisher = mFrame;
6581 establisher && establisher->Combines3DTransformWithAncestors();
6582 establisher =
6583 establisher->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
6585 const nsIFrame* establisherReference = aBuilder->FindReferenceFrameFor(
6586 nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher));
6588 nsPoint offset = establisher->GetOffsetToCrossDoc(establisherReference);
6589 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6590 uint32_t flags =
6591 INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN;
6592 mTransformPreserves3D = MakeUnique<Matrix4x4>(
6593 GetResultingTransformMatrix(mFrame, offset, scale, flags));
6596 return *mTransformPreserves3D;
6599 bool nsDisplayTransform::CreateWebRenderCommands(
6600 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
6601 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
6602 nsDisplayListBuilder* aDisplayListBuilder) {
6603 // We want to make sure we don't pollute the transform property in the WR
6604 // stacking context by including the position of this frame (relative to the
6605 // parent reference frame). We need to keep those separate; the position of
6606 // this frame goes into the stacking context bounds while the transform goes
6607 // into the transform.
6608 LayoutDevicePoint position;
6609 Matrix4x4 newTransformMatrix = GetTransformForRendering(&position);
6611 gfx::Matrix4x4* transformForSC = &newTransformMatrix;
6612 if (newTransformMatrix.IsIdentity()) {
6613 // If the transform is an identity transform, strip it out so that WR
6614 // doesn't turn this stacking context into a reference frame, as it
6615 // affects positioning. Bug 1345577 tracks a better fix.
6616 transformForSC = nullptr;
6618 // In ChooseScaleAndSetTransform, we round the offset from the reference
6619 // frame used to adjust the transform, if there is no transform, or it
6620 // is just a translation. We need to do the same here.
6621 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6622 position.Round();
6626 auto key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
6627 wr::SpatialKeyKind::Transform);
6629 // We don't send animations for transform separator display items.
6630 uint64_t animationsId =
6631 mIsTransformSeparator
6633 : AddAnimationsForWebRender(
6634 this, aManager, aDisplayListBuilder,
6635 IsPartialPrerender() ? Some(position) : Nothing());
6636 wr::WrAnimationProperty prop{wr::WrAnimationType::Transform, animationsId,
6637 key};
6639 nsDisplayTransform* deferredTransformItem = nullptr;
6640 if (!mFrame->ChildrenHavePerspective()) {
6641 // If it has perspective, we create a new scroll data via the
6642 // UpdateScrollData call because that scenario is more complex. Otherwise
6643 // we can just stash the transform on the StackingContextHelper and
6644 // apply it to any scroll data that are created inside this
6645 // nsDisplayTransform.
6646 deferredTransformItem = this;
6649 // Determine if we're possibly animated (= would need an active layer in FLB).
6650 bool animated = !mIsTransformSeparator &&
6651 ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
6653 wr::StackingContextParams params;
6654 params.mBoundTransform = &newTransformMatrix;
6655 params.animation = animationsId ? &prop : nullptr;
6657 wr::WrTransformInfo transform_info;
6658 if (transformForSC) {
6659 transform_info.transform = wr::ToLayoutTransform(newTransformMatrix);
6660 transform_info.key = key;
6661 params.mTransformPtr = &transform_info;
6662 } else {
6663 params.mTransformPtr = nullptr;
6666 params.prim_flags = !BackfaceIsHidden()
6667 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
6668 : wr::PrimitiveFlags{0};
6669 params.paired_with_perspective = mHasAssociatedPerspective;
6670 params.mDeferredTransformItem = deferredTransformItem;
6671 params.mAnimated = animated;
6672 // Determine if we would have to rasterize any items in local raster space
6673 // (i.e. disable subpixel AA). We don't always need to rasterize locally even
6674 // if the stacking context is possibly animated (at the cost of potentially
6675 // some false negatives with respect to will-change handling), so we pass in
6676 // this determination separately to accurately match with when FLB would
6677 // normally disable subpixel AA.
6678 params.mRasterizeLocally = animated && Frame()->HasAnimationOfTransform();
6679 params.SetPreserve3D(mFrame->Extend3DContext() && !mIsTransformSeparator);
6680 params.clip =
6681 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
6683 LayoutDeviceSize boundsSize = LayoutDeviceSize::FromAppUnits(
6684 mChildBounds.Size(), mFrame->PresContext()->AppUnitsPerDevPixel());
6686 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
6687 params, LayoutDeviceRect(position, boundsSize));
6689 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6690 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
6691 return true;
6694 bool nsDisplayTransform::UpdateScrollData(
6695 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
6696 if (!mFrame->ChildrenHavePerspective()) {
6697 // This case is handled in CreateWebRenderCommands by stashing the transform
6698 // on the stacking context.
6699 return false;
6701 if (aLayerData) {
6702 aLayerData->SetTransform(GetTransform().GetMatrix());
6703 aLayerData->SetTransformIsPerspective(true);
6705 return true;
6708 bool nsDisplayTransform::ShouldSkipTransform(
6709 nsDisplayListBuilder* aBuilder) const {
6710 return (aBuilder->RootReferenceFrame() == mFrame) &&
6711 aBuilder->IsForGenerateGlyphMask();
6714 void nsDisplayTransform::Collect3DTransformLeaves(
6715 nsDisplayListBuilder* aBuilder, nsTArray<nsDisplayTransform*>& aLeaves) {
6716 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6717 aLeaves.AppendElement(this);
6718 return;
6721 FlattenedDisplayListIterator iter(aBuilder, &mChildren);
6722 while (iter.HasNext()) {
6723 nsDisplayItem* item = iter.GetNextItem();
6724 if (item->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
6725 auto* perspective = static_cast<nsDisplayPerspective*>(item);
6726 if (!perspective->GetChildren()->GetTop()) {
6727 continue;
6729 item = perspective->GetChildren()->GetTop();
6731 if (item->GetType() != DisplayItemType::TYPE_TRANSFORM) {
6732 gfxCriticalError() << "Invalid child item within 3D transform of type: "
6733 << item->Name();
6734 continue;
6736 static_cast<nsDisplayTransform*>(item)->Collect3DTransformLeaves(aBuilder,
6737 aLeaves);
6741 static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
6742 const gfx::Polygon& aPolygon) {
6743 MOZ_ASSERT(!aPolygon.IsEmpty());
6745 RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
6746 const nsTArray<Point4D>& points = aPolygon.GetPoints();
6748 pathBuilder->MoveTo(points[0].As2DPoint());
6750 for (size_t i = 1; i < points.Length(); ++i) {
6751 pathBuilder->LineTo(points[i].As2DPoint());
6754 pathBuilder->Close();
6755 return pathBuilder->Finish();
6758 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6759 nsDisplayListBuilder* aBuilder, nsTArray<TransformPolygon>& aLeaves) {
6760 std::list<TransformPolygon> inputLayers;
6762 nsTArray<nsDisplayTransform*> leaves;
6763 Collect3DTransformLeaves(aBuilder, leaves);
6764 for (nsDisplayTransform* item : leaves) {
6765 auto bounds = LayoutDeviceRect::FromAppUnits(
6766 item->mChildBounds, item->mFrame->PresContext()->AppUnitsPerDevPixel());
6767 Matrix4x4 transform = item->GetAccumulatedPreserved3DTransform(aBuilder);
6769 if (!IsFrameVisible(item->mFrame, transform)) {
6770 continue;
6772 gfx::Polygon polygon =
6773 gfx::Polygon::FromRect(gfx::Rect(bounds.ToUnknownRect()));
6775 polygon.TransformToScreenSpace(transform);
6777 if (polygon.GetPoints().Length() >= 3) {
6778 inputLayers.push_back(TransformPolygon(item, std::move(polygon)));
6782 if (inputLayers.empty()) {
6783 return;
6786 BSPTree<nsDisplayTransform> tree(inputLayers);
6787 nsTArray<TransformPolygon> orderedLayers(tree.GetDrawOrder());
6789 for (TransformPolygon& polygon : orderedLayers) {
6790 Matrix4x4 inverse =
6791 polygon.data->GetAccumulatedPreserved3DTransform(aBuilder).Inverse();
6793 MOZ_ASSERT(polygon.geometry);
6794 polygon.geometry->TransformToLayerSpace(inverse);
6797 aLeaves = std::move(orderedLayers);
6800 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder,
6801 gfxContext* aCtx) {
6802 Paint(aBuilder, aCtx, Nothing());
6805 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
6806 const Maybe<gfx::Polygon>& aPolygon) {
6807 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6808 MOZ_ASSERT(!aPolygon);
6809 nsTArray<TransformPolygon> leaves;
6810 CollectSorted3DTransformLeaves(aBuilder, leaves);
6811 for (TransformPolygon& item : leaves) {
6812 item.data->Paint(aBuilder, aCtx, item.geometry);
6814 return;
6817 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
6818 Matrix4x4 trans = ShouldSkipTransform(aBuilder)
6819 ? Matrix4x4()
6820 : GetAccumulatedPreserved3DTransform(aBuilder);
6821 if (!IsFrameVisible(mFrame, trans)) {
6822 return;
6825 Matrix trans2d;
6826 if (trans.CanDraw2D(&trans2d)) {
6827 aCtx->Multiply(ThebesMatrix(trans2d));
6829 if (aPolygon) {
6830 RefPtr<gfx::Path> path =
6831 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6832 aCtx->GetDrawTarget()->PushClip(path);
6835 GetChildren()->Paint(aBuilder, aCtx,
6836 mFrame->PresContext()->AppUnitsPerDevPixel());
6838 if (aPolygon) {
6839 aCtx->GetDrawTarget()->PopClip();
6841 return;
6844 // TODO: Implement 3d transform handling, including plane splitting and
6845 // sorting. See BasicCompositor.
6846 auto pixelBounds = LayoutDeviceRect::FromAppUnitsToOutside(
6847 mChildBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
6848 RefPtr<DrawTarget> untransformedDT =
6849 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6850 IntSize(pixelBounds.Width(), pixelBounds.Height()),
6851 SurfaceFormat::B8G8R8A8, true);
6852 if (!untransformedDT || !untransformedDT->IsValid()) {
6853 return;
6855 untransformedDT->SetTransform(
6856 Matrix::Translation(-Point(pixelBounds.X(), pixelBounds.Y())));
6858 gfxContext groupTarget(untransformedDT, /* aPreserveTransform */ true);
6860 if (aPolygon) {
6861 RefPtr<gfx::Path> path =
6862 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6863 aCtx->GetDrawTarget()->PushClip(path);
6866 GetChildren()->Paint(aBuilder, &groupTarget,
6867 mFrame->PresContext()->AppUnitsPerDevPixel());
6869 if (aPolygon) {
6870 aCtx->GetDrawTarget()->PopClip();
6873 RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
6875 trans.PreTranslate(pixelBounds.X(), pixelBounds.Y(), 0);
6876 aCtx->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf, trans);
6879 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
6880 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6881 // completely bypass the main thread for this animation, so it is always
6882 // worthwhile.
6883 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6884 // already involved so there is less to be gained.
6885 // Therefore we check that the *post-transform* bounds of this item are
6886 // big enough to justify an active layer.
6887 return EffectCompositor::HasAnimationsForCompositor(
6888 mFrame, DisplayItemType::TYPE_TRANSFORM) ||
6889 (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame));
6892 nsRect nsDisplayTransform::TransformUntransformedBounds(
6893 nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
6894 bool snap;
6895 const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
6896 // GetTransform always operates in dev pixels.
6897 const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
6898 return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
6899 factor);
6903 * Returns the bounds for this transform. The bounds are calculated during
6904 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6906 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
6907 bool* aSnap) const {
6908 *aSnap = false;
6909 return mBounds;
6912 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
6913 MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
6915 /* Some transforms can get empty bounds in 2D, but might get transformed again
6916 * and get non-empty bounds. A simple example of this would be a 180 degree
6917 * rotation getting applied twice.
6918 * We should not depend on transforming bounds level by level.
6920 * This function collects the bounds of this transform and stores it in
6921 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6922 * down and include the bounds of the child transforms.
6923 * The bounds are transformed with the accumulated transformation matrix up to
6924 * the 3D context root coordinate space.
6926 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6927 accTransform.Accumulate(GetTransform().GetMatrix());
6929 // Do not dive into another 3D context.
6930 if (!IsLeafOf3DContext()) {
6931 for (nsDisplayItem* i : *GetChildren()) {
6932 i->DoUpdateBoundsPreserves3D(aBuilder);
6936 /* The child transforms that extend 3D context further will have empty bounds,
6937 * so the untransformed bounds here is the bounds of all the non-preserve-3d
6938 * content under this transform.
6940 const nsRect rect = TransformUntransformedBounds(
6941 aBuilder, accTransform.GetCurrentTransform());
6942 aBuilder->AccumulateRect(rect);
6945 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6946 nsDisplayListBuilder* aBuilder) {
6947 MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
6948 IsTransformSeparator());
6949 // Updating is not going through to child 3D context.
6950 ComputeBounds(aBuilder);
6953 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
6954 UpdateUntransformedBounds(aBuilder);
6956 if (IsTransformSeparator()) {
6957 MOZ_ASSERT(GetTransform().IsIdentity());
6958 mBounds = mChildBounds;
6959 return;
6962 if (mFrame->Extend3DContext()) {
6963 if (!Combines3DTransformWithAncestors()) {
6964 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6965 // collect the bounds from the child transforms.
6966 UpdateBoundsFor3D(aBuilder);
6967 } else {
6968 // With nested 3D transforms, the 2D bounds might not be useful.
6969 mBounds = nsRect();
6972 return;
6975 MOZ_ASSERT(!mFrame->Extend3DContext());
6977 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
6978 // but mix-blend-mode relies on having bounds set. See bug 1556956.
6980 // A stand-alone transform.
6981 mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
6984 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
6985 MOZ_ASSERT(mFrame->Extend3DContext() &&
6986 !mFrame->Combines3DTransformWithAncestors() &&
6987 !IsTransformSeparator());
6989 // Always start updating from an establisher of a 3D rendering context.
6990 nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
6991 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6992 accTransform.StartRoot();
6993 ComputeBounds(aBuilder);
6994 mBounds = aBuilder->GetAccumulatedRect();
6997 void nsDisplayTransform::UpdateUntransformedBounds(
6998 nsDisplayListBuilder* aBuilder) {
6999 mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
7000 aBuilder, mActiveScrolledRoot);
7003 #ifdef DEBUG_HIT
7004 # include <time.h>
7005 #endif
7007 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
7008 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
7009 const nsRect& aRect, HitTestState* aState,
7010 nsTArray<nsIFrame*>* aOutFrames) {
7011 if (aState->mInPreserves3D) {
7012 GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
7013 return;
7016 /* Here's how this works:
7017 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
7018 * anything).
7019 * 2. Invert the matrix.
7020 * 3. Use it to transform the rect into the correct space.
7021 * 4. Pass that rect down through to the list's version of HitTest.
7023 // GetTransform always operates in dev pixels.
7024 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7025 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7027 if (!IsFrameVisible(mFrame, matrix)) {
7028 return;
7031 const bool oldHitOccludingItem = aState->mHitOccludingItem;
7033 /* We want to go from transformed-space to regular space.
7034 * Thus we have to invert the matrix, which normally does
7035 * the reverse operation (e.g. regular->transformed)
7038 /* Now, apply the transform and pass it down the channel. */
7039 matrix.Invert();
7040 nsRect resultingRect;
7041 // Magic width/height indicating we're hit testing a point, not a rect
7042 const bool testingPoint = aRect.width == 1 && aRect.height == 1;
7043 if (testingPoint) {
7044 Point4D point =
7045 matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
7046 NSAppUnitsToFloatPixels(aRect.y, factor)));
7047 if (!point.HasPositiveWCoord()) {
7048 return;
7051 Point point2d = point.As2DPoint();
7053 resultingRect =
7054 nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
7055 NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1);
7057 } else {
7058 Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
7059 NSAppUnitsToFloatPixels(aRect.y, factor),
7060 NSAppUnitsToFloatPixels(aRect.width, factor),
7061 NSAppUnitsToFloatPixels(aRect.height, factor));
7063 Rect childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds.x, factor),
7064 NSAppUnitsToFloatPixels(mChildBounds.y, factor),
7065 NSAppUnitsToFloatPixels(mChildBounds.width, factor),
7066 NSAppUnitsToFloatPixels(mChildBounds.height, factor));
7068 Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
7070 resultingRect =
7071 nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
7072 NSFloatPixelsToAppUnits(float(rect.Y()), factor),
7073 NSFloatPixelsToAppUnits(float(rect.Width()), factor),
7074 NSFloatPixelsToAppUnits(float(rect.Height()), factor));
7077 if (resultingRect.IsEmpty()) {
7078 return;
7081 #ifdef DEBUG_HIT
7082 printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
7083 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(),
7084 resultingRect.Y());
7085 uint32_t originalFrameCount = aOutFrames.Length();
7086 #endif
7088 GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
7090 if (aState->mHitOccludingItem && !testingPoint &&
7091 !mChildBounds.Contains(aRect)) {
7092 MOZ_ASSERT(aBuilder->HitTestIsForVisibility());
7093 // We're hit-testing a rect that's bigger than our child bounds, but
7094 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so
7095 // we can't stop hit-testing altogether.
7097 // FIXME(emilio): I think this means that theoretically we might include
7098 // some frames fully behind other transformed-but-opaque frames? Then again
7099 // that's our pre-existing behavior for other untransformed content that
7100 // doesn't fill the whole rect. To be fully correct I think we'd need proper
7101 // "known occluded region" tracking, but that might be overkill for our
7102 // purposes here.
7103 aState->mHitOccludingItem = oldHitOccludingItem;
7106 #ifdef DEBUG_HIT
7107 if (originalFrameCount != aOutFrames.Length())
7108 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7109 dynamic_cast<void*>(aOutFrames.ElementAt(0)));
7110 printf("=== end of hit test ===\n");
7111 #endif
7114 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
7115 const nsPoint& aPoint) {
7116 // GetTransform always operates in dev pixels.
7117 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7118 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7120 NS_ASSERTION(IsFrameVisible(mFrame, matrix),
7121 "We can't have hit a frame that isn't visible!");
7123 Matrix4x4 inverse = matrix;
7124 inverse.Invert();
7125 Point4D point =
7126 inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
7127 NSAppUnitsToFloatPixels(aPoint.y, factor)));
7129 Point point2d = point.As2DPoint();
7131 Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
7132 return transformed.z;
7135 /* The transform is opaque iff the transform consists solely of scales and
7136 * translations and if the underlying content is opaque. Thus if the transform
7137 * is of the form
7139 * |a c e|
7140 * |b d f|
7141 * |0 0 1|
7143 * We need b and c to be zero.
7145 * We also need to check whether the underlying opaque content completely fills
7146 * our visible rect. We use UntransformRect which expands to the axis-aligned
7147 * bounding rect, but that's OK since if
7148 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7149 * certainly contains the actual (non-axis-aligned) untransformed rect.
7151 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7152 bool* aSnap) const {
7153 *aSnap = false;
7155 nsRect untransformedVisible;
7156 if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
7157 return nsRegion();
7160 const Matrix4x4Flagged& matrix = GetTransform();
7161 Matrix matrix2d;
7162 if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
7163 return nsRegion();
7166 nsRegion result;
7168 bool tmpSnap;
7169 const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
7170 const nsRegion opaque =
7171 ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
7173 if (opaque.Contains(untransformedVisible)) {
7174 result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
7176 return result;
7179 nsRect nsDisplayTransform::GetComponentAlphaBounds(
7180 nsDisplayListBuilder* aBuilder) const {
7181 if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
7182 return nsRect();
7185 bool snap;
7186 return GetBounds(aBuilder, &snap);
7189 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7190 * the smallest rectangle (in app space) containing the transformed image of
7191 * that rectangle. That is, it takes the four corners of the rectangle,
7192 * transforms them according to the matrix associated with the specified frame,
7193 * then returns the smallest rectangle containing the four transformed points.
7195 * @param aUntransformedBounds The rectangle (in app units) to transform.
7196 * @param aFrame The frame whose transformation should be applied.
7197 * @param aOrigin The delta from the frame origin to the coordinate space origin
7198 * @return The smallest rectangle containing the image of the transformed
7199 * rectangle.
7201 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds,
7202 const nsIFrame* aFrame,
7203 TransformReferenceBox& aRefBox) {
7204 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7206 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7208 FrameTransformProperties props(aFrame, aRefBox, factor);
7209 return nsLayoutUtils::MatrixTransformRect(
7210 aUntransformedBounds,
7211 GetResultingTransformMatrixInternal(props, aRefBox, nsPoint(), factor,
7212 kTransformRectFlags),
7213 factor);
7216 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7217 const nsRect& aChildBounds,
7218 const nsIFrame* aFrame,
7219 nsRect* aOutRect) {
7220 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7222 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7223 Matrix4x4 transform = GetResultingTransformMatrix(aFrame, nsPoint(), factor,
7224 kTransformRectFlags);
7225 return UntransformRect(aTransformedBounds, aChildBounds, transform, factor,
7226 aOutRect);
7229 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7230 const nsRect& aChildBounds,
7231 const Matrix4x4& aMatrix,
7232 float aAppUnitsPerPixel,
7233 nsRect* aOutRect) {
7234 if (aMatrix.IsSingular()) {
7235 return false;
7238 RectDouble result(
7239 NSAppUnitsToFloatPixels(aTransformedBounds.x, aAppUnitsPerPixel),
7240 NSAppUnitsToFloatPixels(aTransformedBounds.y, aAppUnitsPerPixel),
7241 NSAppUnitsToFloatPixels(aTransformedBounds.width, aAppUnitsPerPixel),
7242 NSAppUnitsToFloatPixels(aTransformedBounds.height, aAppUnitsPerPixel));
7244 RectDouble childGfxBounds(
7245 NSAppUnitsToFloatPixels(aChildBounds.x, aAppUnitsPerPixel),
7246 NSAppUnitsToFloatPixels(aChildBounds.y, aAppUnitsPerPixel),
7247 NSAppUnitsToFloatPixels(aChildBounds.width, aAppUnitsPerPixel),
7248 NSAppUnitsToFloatPixels(aChildBounds.height, aAppUnitsPerPixel));
7250 result = aMatrix.Inverse().ProjectRectBounds(result, childGfxBounds);
7251 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result),
7252 aAppUnitsPerPixel);
7253 return true;
7256 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder,
7257 const nsRect& aRect,
7258 nsRect* aOutRect) const {
7259 if (GetTransform().IsSingular()) {
7260 return false;
7263 // GetTransform always operates in dev pixels.
7264 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7265 RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor),
7266 NSAppUnitsToFloatPixels(aRect.y, factor),
7267 NSAppUnitsToFloatPixels(aRect.width, factor),
7268 NSAppUnitsToFloatPixels(aRect.height, factor));
7270 bool snap;
7271 nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
7272 RectDouble childGfxBounds(
7273 NSAppUnitsToFloatPixels(childBounds.x, factor),
7274 NSAppUnitsToFloatPixels(childBounds.y, factor),
7275 NSAppUnitsToFloatPixels(childBounds.width, factor),
7276 NSAppUnitsToFloatPixels(childBounds.height, factor));
7278 /* We want to untransform the matrix, so invert the transformation first! */
7279 result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds);
7281 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
7283 return true;
7286 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) {
7287 aStream << GetTransform().GetMatrix();
7288 if (IsTransformSeparator()) {
7289 aStream << " transform-separator";
7291 if (IsLeafOf3DContext()) {
7292 aStream << " 3d-context-leaf";
7294 if (mFrame->Extend3DContext()) {
7295 aStream << " extends-3d-context";
7297 if (mFrame->Combines3DTransformWithAncestors()) {
7298 aStream << " combines-3d-with-ancestors";
7301 aStream << " prerender(";
7302 switch (mPrerenderDecision) {
7303 case PrerenderDecision::No:
7304 aStream << "no";
7305 break;
7306 case PrerenderDecision::Partial:
7307 aStream << "partial";
7308 break;
7309 case PrerenderDecision::Full:
7310 aStream << "full";
7311 break;
7313 aStream << ")";
7314 aStream << " childrenBuildingRect" << mChildrenBuildingRect;
7317 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
7318 nsIFrame* aFrame,
7319 nsDisplayList* aList)
7320 : nsPaintedDisplayItem(aBuilder, aFrame), mList(aBuilder) {
7321 mList.AppendToTop(aList);
7322 MOZ_ASSERT(mList.Length() == 1);
7323 MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
7326 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder,
7327 gfxContext* aCtx) {
7328 // Just directly recurse into children, since we'll include the persepctive
7329 // value in any nsDisplayTransform children.
7330 GetChildren()->Paint(aBuilder, aCtx,
7331 mFrame->PresContext()->AppUnitsPerDevPixel());
7334 nsRegion nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7335 bool* aSnap) const {
7336 if (!GetChildren()->GetTop()) {
7337 *aSnap = false;
7338 return nsRegion();
7341 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder, aSnap);
7344 bool nsDisplayPerspective::CreateWebRenderCommands(
7345 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7346 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7347 nsDisplayListBuilder* aDisplayListBuilder) {
7348 float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
7349 Matrix4x4 perspectiveMatrix;
7350 DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
7351 mFrame, appUnitsPerPixel, perspectiveMatrix);
7352 MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
7355 * ClipListToRange can remove our child after we were created.
7357 if (!GetChildren()->GetTop()) {
7358 return false;
7362 * The resulting matrix is still in the coordinate space of the transformed
7363 * frame. Append a translation to the reference frame coordinates.
7365 nsDisplayTransform* transform =
7366 static_cast<nsDisplayTransform*>(GetChildren()->GetTop());
7368 Point3D newOrigin =
7369 Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
7370 appUnitsPerPixel),
7371 NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
7372 appUnitsPerPixel),
7373 0.0f);
7374 Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
7376 perspectiveMatrix.PostTranslate(roundedOrigin);
7378 nsIFrame* perspectiveFrame =
7379 mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
7381 // Passing true here is always correct, since perspective always combines
7382 // transforms with the descendants. However that'd make WR do a lot of work
7383 // that it doesn't really need to do if there aren't other transforms forming
7384 // part of the 3D context.
7386 // WR knows how to treat perspective in that case, so the only thing we need
7387 // to do is to ensure we pass true when we're involved in a 3d context in any
7388 // other way via the transform-style property on either the transformed frame
7389 // or the perspective frame in order to not confuse WR's preserve-3d code in
7390 // very awful ways.
7391 bool preserve3D =
7392 mFrame->Extend3DContext() || perspectiveFrame->Extend3DContext();
7394 wr::StackingContextParams params;
7396 wr::WrTransformInfo transform_info;
7397 transform_info.transform = wr::ToLayoutTransform(perspectiveMatrix);
7398 transform_info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
7399 wr::SpatialKeyKind::Perspective);
7400 params.mTransformPtr = &transform_info;
7402 params.reference_frame_kind = wr::WrReferenceFrameKind::Perspective;
7403 params.prim_flags = !BackfaceIsHidden()
7404 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7405 : wr::PrimitiveFlags{0};
7406 params.SetPreserve3D(preserve3D);
7407 params.clip =
7408 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
7410 Maybe<uint64_t> scrollingRelativeTo;
7411 for (const auto* asr = GetActiveScrolledRoot(); asr; asr = asr->mParent) {
7412 // In OOP documents, the root scrollable frame of the in-process root
7413 // document is always active, so using IsAncestorFrameCrossDocInProcess
7414 // should be fine here.
7415 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7416 asr->mScrollableFrame->GetScrolledFrame(), perspectiveFrame)) {
7417 scrollingRelativeTo.emplace(asr->GetViewId());
7418 break;
7422 // We put the perspective reference frame wrapping the transformed frame,
7423 // even though there may be arbitrarily nested scroll frames in between.
7425 // We need to know how many ancestor scroll-frames are we nested in, in order
7426 // for the async scrolling code in WebRender to calculate the right
7427 // transformation for the perspective contents.
7428 params.scrolling_relative_to = scrollingRelativeTo.ptrOr(nullptr);
7430 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
7431 params);
7433 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7434 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
7436 return true;
7439 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
7440 nsTextFrame* aFrame)
7441 : nsPaintedDisplayItem(aBuilder, aFrame),
7442 mVisIStartEdge(0),
7443 mVisIEndEdge(0) {
7444 MOZ_COUNT_CTOR(nsDisplayText);
7445 mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7446 // Bug 748228
7447 mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
7448 mVisibleRect = aBuilder->GetVisibleRect() +
7449 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
7452 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager,
7453 nsDisplayListBuilder* aBuilder) const {
7454 auto* f = static_cast<nsTextFrame*>(mFrame);
7456 if (f->IsSelected()) {
7457 return false;
7460 const nsStyleText* textStyle = f->StyleText();
7461 if (textStyle->HasTextShadow()) {
7462 return false;
7465 nsTextFrame::TextDecorations decorations;
7466 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7467 decorations);
7468 return !decorations.HasDecorationLines();
7471 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
7472 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
7473 // We don't pass mVisibleRect here, since this can be called from within
7474 // the WebRender fallback painting path, and we don't want to issue
7475 // recorded commands that are dependent on the visible/building rect.
7476 RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
7478 auto* textFrame = static_cast<nsTextFrame*>(mFrame);
7479 LCPTextFrameHelper::MaybeUnionTextFrame(textFrame,
7480 mBounds - ToReferenceFrame());
7483 bool nsDisplayText::CreateWebRenderCommands(
7484 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7485 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7486 nsDisplayListBuilder* aDisplayListBuilder) {
7487 auto* f = static_cast<nsTextFrame*>(mFrame);
7488 auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
7490 nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
7491 // Bug 748228
7492 bounds.Inflate(appUnitsPerDevPixel);
7494 if (bounds.IsEmpty()) {
7495 return true;
7498 // For large font sizes, punt to a blob image, to avoid the blurry rendering
7499 // that results from WR clamping the glyph size used for rasterization.
7501 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7503 // This is not strictly accurate, as final used font sizes might not be the
7504 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7505 // altering the used size of the font actually used).
7506 // It also fails to consider how transforms might affect the device-font-size
7507 // that webrender uses (and clamps).
7508 // But it should be near enough for practical purposes; the limitations just
7509 // mean we might sometimes end up with webrender still applying some bitmap
7510 // scaling, or bail out when we didn't really need to.
7511 constexpr float kWebRenderFontSizeLimit = 320.0;
7512 f->EnsureTextRun(nsTextFrame::eInflated);
7513 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7514 if (textRun &&
7515 textRun->GetFontGroup()->GetStyle()->size > kWebRenderFontSizeLimit) {
7516 return false;
7519 gfx::Point deviceOffset =
7520 LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
7521 .ToUnknownPoint();
7523 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7524 // frames) lets us early reject a bunch of things.
7525 nsRect visible = mVisibleRect;
7527 // Add the "source rect" area from which the given shadows could intersect
7528 // with mVisibleRect, and which therefore needs to included in the paint
7529 // operation, to the `visible` rect that we will use to limit the bounds of
7530 // what we send to the renderer.
7531 auto addShadowSourceToVisible = [&](Span<const StyleSimpleShadow> aShadows) {
7532 for (const auto& shadow : aShadows) {
7533 nsRect sourceRect = mVisibleRect;
7534 // Negate the offsets, because we're looking for the "source" rect that
7535 // could cast a shadow into the visible rect, rather than a "target" area
7536 // onto which the visible rect would cast a shadow.
7537 sourceRect.MoveBy(-shadow.horizontal.ToAppUnits(),
7538 -shadow.vertical.ToAppUnits());
7539 // Inflate to account for the shadow blur.
7540 sourceRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
7541 shadow.blur.ToAppUnits(), appUnitsPerDevPixel));
7542 visible.OrWith(sourceRect);
7546 // Shadows can translate things back into view, so we enlarge the notional
7547 // "visible" rect to ensure we don't skip painting relevant parts that might
7548 // cast a shadow within the visible area.
7549 addShadowSourceToVisible(f->StyleText()->mTextShadow.AsSpan());
7551 // Similarly for shadows that may be cast by ::selection.
7552 if (f->IsSelected()) {
7553 nsTextPaintStyle textPaint(f);
7554 Span<const StyleSimpleShadow> shadows;
7555 f->GetSelectionTextShadow(SelectionType::eNormal, textPaint, &shadows);
7556 addShadowSourceToVisible(shadows);
7559 // Inflate a little extra to allow for potential antialiasing "blur".
7560 visible.Inflate(3 * appUnitsPerDevPixel);
7561 bounds = bounds.Intersect(visible);
7563 gfxContext* textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager,
7564 this, bounds, deviceOffset);
7566 LCPTextFrameHelper::MaybeUnionTextFrame(f, bounds - ToReferenceFrame());
7568 aBuilder.StartGroup(this);
7570 RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
7571 aBuilder.GetInheritedOpacity(), true);
7572 const bool result = textDrawer->GetTextDrawer()->Finish();
7574 if (result) {
7575 aBuilder.FinishGroup();
7576 } else {
7577 aBuilder.CancelGroup(true);
7580 return result;
7583 void nsDisplayText::RenderToContext(gfxContext* aCtx,
7584 nsDisplayListBuilder* aBuilder,
7585 const nsRect& aVisibleRect, float aOpacity,
7586 bool aIsRecording) {
7587 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7589 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7590 // antialiased pixels beyond the measured text extents.
7591 // This is temporary until we do this in the actual calculation of text
7592 // extents.
7593 auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
7594 LayoutDeviceRect extraVisible =
7595 LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D);
7596 extraVisible.Inflate(1);
7598 gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width,
7599 extraVisible.height);
7600 pixelVisible.Inflate(2);
7601 pixelVisible.RoundOut();
7603 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7604 if (!aBuilder->IsForGenerateGlyphMask() && !aIsRecording) {
7605 autoSaveClip.Clip(pixelVisible);
7608 NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
7609 NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
7611 gfxContextMatrixAutoSaveRestore matrixSR;
7613 nsPoint framePt = ToReferenceFrame();
7614 if (f->Style()->IsTextCombined()) {
7615 float scaleFactor = nsTextFrame::GetTextCombineScaleFactor(f);
7616 if (scaleFactor != 1.0f) {
7617 if (auto* textDrawer = aCtx->GetTextDrawer()) {
7618 // WebRender doesn't support scaling text like this yet
7619 textDrawer->FoundUnsupportedFeature();
7620 return;
7622 matrixSR.SetContext(aCtx);
7623 // Setup matrix to compress text for text-combine-upright if
7624 // necessary. This is done here because we want selection be
7625 // compressed at the same time as text.
7626 gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
7627 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7628 if (textRun && textRun->IsRightToLeft()) {
7629 pt.x += gfxFloat(f->GetSize().width) / A2D;
7631 gfxMatrix mat = aCtx->CurrentMatrixDouble()
7632 .PreTranslate(pt)
7633 .PreScale(scaleFactor, 1.0)
7634 .PreTranslate(-pt);
7635 aCtx->SetMatrixDouble(mat);
7638 nsTextFrame::PaintTextParams params(aCtx);
7639 params.framePt = gfx::Point(framePt.x, framePt.y);
7640 params.dirtyRect = extraVisible;
7642 if (aBuilder->IsForGenerateGlyphMask()) {
7643 params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
7644 } else {
7645 params.state = nsTextFrame::PaintTextParams::PaintText;
7648 f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
7649 f->IsSelected(), aOpacity);
7652 // This could go to nsDisplayListInvalidation.h, but
7653 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7654 // would produce circular dependencies.
7655 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
7656 public:
7657 nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
7658 : nsDisplayItemGenericGeometry(aItem, aBuilder),
7659 mVisIStartEdge(aItem->VisIStartEdge()),
7660 mVisIEndEdge(aItem->VisIEndEdge()) {
7661 nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
7662 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7663 mDecorations);
7667 * We store the computed text decorations here since they are
7668 * computed using style data from parent frames. Any changes to these
7669 * styles will only invalidate the parent frame and not this frame.
7671 nsTextFrame::TextDecorations mDecorations;
7672 nscoord mVisIStartEdge;
7673 nscoord mVisIEndEdge;
7676 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry(
7677 nsDisplayListBuilder* aBuilder) {
7678 return new nsDisplayTextGeometry(this, aBuilder);
7681 void nsDisplayText::ComputeInvalidationRegion(
7682 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7683 nsRegion* aInvalidRegion) const {
7684 const nsDisplayTextGeometry* geometry =
7685 static_cast<const nsDisplayTextGeometry*>(aGeometry);
7686 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7688 nsTextFrame::TextDecorations decorations;
7689 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7690 decorations);
7692 bool snap;
7693 const nsRect& newRect = geometry->mBounds;
7694 nsRect oldRect = GetBounds(aBuilder, &snap);
7695 if (decorations != geometry->mDecorations ||
7696 mVisIStartEdge != geometry->mVisIStartEdge ||
7697 mVisIEndEdge != geometry->mVisIEndEdge ||
7698 !oldRect.IsEqualInterior(newRect) ||
7699 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
7700 aInvalidRegion->Or(oldRect, newRect);
7704 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) {
7705 #ifdef DEBUG
7706 aStream << " (\"";
7708 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7709 nsCString buf;
7710 f->ToCString(buf);
7712 aStream << buf.get() << "\")";
7713 #endif
7716 nsDisplayEffectsBase::nsDisplayEffectsBase(
7717 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7718 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
7719 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
7720 aClearClipChain) {
7721 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7724 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
7725 nsIFrame* aFrame,
7726 nsDisplayList* aList)
7727 : nsDisplayWrapList(aBuilder, aFrame, aList) {
7728 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7731 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7732 bool* aSnap) const {
7733 *aSnap = false;
7734 return nsRegion();
7737 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder* aBuilder,
7738 const nsRect& aRect, HitTestState* aState,
7739 nsTArray<nsIFrame*>* aOutFrames) {
7740 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
7741 if (SVGIntegrationUtils::HitTestFrameForEffects(
7742 mFrame, rectCenter - ToReferenceFrame())) {
7743 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
7747 gfxRect nsDisplayEffectsBase::BBoxInUserSpace() const {
7748 return SVGUtils::GetBBox(mFrame);
7751 gfxPoint nsDisplayEffectsBase::UserSpaceOffset() const {
7752 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
7755 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7756 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7757 nsRegion* aInvalidRegion) const {
7758 const auto* geometry =
7759 static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
7760 bool snap;
7761 nsRect bounds = GetBounds(aBuilder, &snap);
7762 if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
7763 geometry->mUserSpaceOffset != UserSpaceOffset() ||
7764 !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
7765 // Filter and mask output can depend on the location of the frame's user
7766 // space and on the frame's BBox. We need to invalidate if either of these
7767 // change relative to the reference frame.
7768 // Invalidations from our inactive layer manager are not enough to catch
7769 // some of these cases because filters can produce output even if there's
7770 // nothing in the filter input.
7771 aInvalidRegion->Or(bounds, geometry->mBounds);
7775 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7776 if (mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
7777 ISVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame);
7778 if (!svgFrame) {
7779 return false;
7781 if (auto* svgElement = SVGElement::FromNode(mFrame->GetContent())) {
7782 // The SVG spec says only to draw filters if the element
7783 // has valid dimensions.
7784 return svgElement->HasValidDimensions();
7786 return false;
7789 return true;
7792 using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams;
7794 static void ComputeMaskGeometry(PaintFramesParams& aParams) {
7795 // Properties are added lazily and may have been removed by a restyle, so
7796 // make sure all applicable ones are set again.
7797 nsIFrame* firstFrame =
7798 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
7800 const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
7802 nsTArray<SVGMaskFrame*> maskFrames;
7803 // XXX check return value?
7804 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
7806 if (maskFrames.Length() == 0) {
7807 return;
7810 gfxContext& ctx = aParams.ctx;
7811 nsIFrame* frame = aParams.frame;
7813 nsPoint offsetToUserSpace =
7814 nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame);
7816 auto cssToDevScale = frame->PresContext()->CSSToDevPixelScale();
7817 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
7819 gfxPoint devPixelOffsetToUserSpace =
7820 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, appUnitsPerDevPixel);
7822 gfxContextMatrixAutoSaveRestore matSR(&ctx);
7823 ctx.SetMatrixDouble(
7824 ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace));
7826 // Convert boaderArea and dirtyRect to user space.
7827 nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
7828 nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
7830 // Union all mask layer rectangles in user space.
7831 LayoutDeviceRect maskInUserSpace;
7832 for (size_t i = 0; i < maskFrames.Length(); i++) {
7833 SVGMaskFrame* maskFrame = maskFrames[i];
7834 LayoutDeviceRect currentMaskSurfaceRect;
7836 if (maskFrame) {
7837 auto rect = maskFrame->GetMaskArea(aParams.frame);
7838 currentMaskSurfaceRect =
7839 CSSRect::FromUnknownRect(ToRect(rect)) * cssToDevScale;
7840 } else {
7841 nsCSSRendering::ImageLayerClipState clipState;
7842 nsCSSRendering::GetImageLayerClip(
7843 svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(),
7844 userSpaceBorderArea, userSpaceDirtyRect,
7845 /* aWillPaintBorder = */ false, appUnitsPerDevPixel, &clipState);
7846 currentMaskSurfaceRect = LayoutDeviceRect::FromUnknownRect(
7847 ToRect(clipState.mDirtyRectInDevPx));
7850 maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
7853 if (!maskInUserSpace.IsEmpty()) {
7854 aParams.maskRect = Some(maskInUserSpace);
7855 } else {
7856 aParams.maskRect = Nothing();
7860 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7861 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7862 const ActiveScrolledRoot* aActiveScrolledRoot, bool aWrapsBackdropFilter)
7863 : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
7864 mWrapsBackdropFilter(aWrapsBackdropFilter) {
7865 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
7867 nsPresContext* presContext = mFrame->PresContext();
7868 uint32_t flags =
7869 aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
7870 const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
7871 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7872 const auto& layer = svgReset->mMask.mLayers[i];
7873 if (!layer.mImage.IsResolved()) {
7874 continue;
7876 const nsRect& borderArea = mFrame->GetRectRelativeToSelf();
7877 // NOTE(emilio): We only care about the dest rect so we don't bother
7878 // computing a clip.
7879 bool isTransformedFixed = false;
7880 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
7881 presContext, aFrame, flags, borderArea, borderArea, layer,
7882 &isTransformedFixed);
7883 mDestRects.AppendElement(state.mDestArea);
7887 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) {
7888 // Do not merge items for box-decoration-break:clone elements,
7889 // since each box should have its own mask in that case.
7890 if (aFrame->StyleBorder()->mBoxDecorationBreak ==
7891 StyleBoxDecorationBreak::Clone) {
7892 return false;
7895 // Do not merge if either frame has a mask. Continuation frames should apply
7896 // the mask independently (just like nsDisplayBackgroundImage).
7897 if (aFrame->StyleSVGReset()->HasMask()) {
7898 return false;
7901 return true;
7904 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem* aItem) const {
7905 // Items for the same content element should be merged into a single
7906 // compositing group.
7907 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
7908 !HasSameContent(aItem)) {
7909 return false;
7912 return CanMergeDisplayMaskFrame(mFrame) &&
7913 CanMergeDisplayMaskFrame(aItem->Frame());
7916 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7917 if (!ValidateSVGFrame()) {
7918 return false;
7921 return SVGUtils::DetermineMaskUsage(mFrame, false).UsingMaskOrClipPath();
7924 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder,
7925 gfxContext* aMaskContext,
7926 bool aHandleOpacity,
7927 bool* aMaskPainted) {
7928 MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
7930 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7931 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7932 PaintFramesParams params(*aMaskContext, mFrame, mBounds, borderArea, aBuilder,
7933 aHandleOpacity, imgParams);
7934 ComputeMaskGeometry(params);
7935 bool maskIsComplete = false;
7936 bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete);
7937 if (aMaskPainted) {
7938 *aMaskPainted = painted;
7941 return maskIsComplete &&
7942 (imgParams.result == ImgDrawResult::SUCCESS ||
7943 imgParams.result == ImgDrawResult::SUCCESS_NOT_COMPLETE ||
7944 imgParams.result == ImgDrawResult::WRONG_SIZE);
7947 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7948 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7949 nsRegion* aInvalidRegion) const {
7950 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
7951 aInvalidRegion);
7953 const auto* geometry =
7954 static_cast<const nsDisplayMasksAndClipPathsGeometry*>(aGeometry);
7955 bool snap;
7956 nsRect bounds = GetBounds(aBuilder, &snap);
7958 if (mDestRects.Length() != geometry->mDestRects.Length()) {
7959 aInvalidRegion->Or(bounds, geometry->mBounds);
7960 } else {
7961 for (size_t i = 0; i < mDestRects.Length(); i++) {
7962 if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) {
7963 aInvalidRegion->Or(bounds, geometry->mBounds);
7964 break;
7970 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7971 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
7972 const std::function<void()>& aPaintChildren) {
7973 // Clip the drawing target by mVisibleRect, which contains the visible
7974 // region of the target frame and its out-of-flow and inflow descendants.
7975 Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx),
7976 mFrame->PresContext()->AppUnitsPerDevPixel());
7977 bounds.RoundOut();
7978 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7979 autoSaveClip.Clip(bounds);
7981 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7982 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7983 PaintFramesParams params(*aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
7984 borderArea, aBuilder, false, imgParams);
7986 ComputeMaskGeometry(params);
7988 SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren);
7991 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder* aBuilder,
7992 gfxContext* aCtx) {
7993 if (!IsValidMask()) {
7994 return;
7996 PaintWithContentsPaintCallback(aBuilder, aCtx, [&] {
7997 GetChildren()->Paint(aBuilder, aCtx,
7998 mFrame->PresContext()->AppUnitsPerDevPixel());
8002 static Maybe<wr::WrClipChainId> CreateSimpleClipRegion(
8003 const nsDisplayMasksAndClipPaths& aDisplayItem,
8004 wr::DisplayListBuilder& aBuilder) {
8005 nsIFrame* frame = aDisplayItem.Frame();
8006 const auto* style = frame->StyleSVGReset();
8007 MOZ_ASSERT(style->HasClipPath() || style->HasMask());
8008 if (!SVGUtils::DetermineMaskUsage(frame, false).IsSimpleClipShape()) {
8009 return Nothing();
8012 const auto& clipPath = style->mClipPath;
8013 const auto& shape = *clipPath.AsShape()._0;
8015 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8016 const nsRect refBox =
8017 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8019 wr::WrClipId clipId{};
8021 switch (shape.tag) {
8022 case StyleBasicShape::Tag::Rect: {
8023 const nsRect rect =
8024 ShapeUtils::ComputeInsetRect(shape.AsRect().rect, refBox) +
8025 aDisplayItem.ToReferenceFrame();
8027 nscoord radii[8] = {0};
8028 if (ShapeUtils::ComputeRectRadii(shape.AsRect().round, refBox, rect,
8029 radii)) {
8030 clipId = aBuilder.DefineRoundedRectClip(
8031 Nothing(),
8032 wr::ToComplexClipRegion(rect, radii, appUnitsPerDevPixel));
8033 } else {
8034 clipId = aBuilder.DefineRectClip(
8035 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8036 rect, appUnitsPerDevPixel)));
8039 break;
8041 case StyleBasicShape::Tag::Ellipse:
8042 case StyleBasicShape::Tag::Circle: {
8043 nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox);
8045 nsSize radii;
8046 if (shape.IsEllipse()) {
8047 radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox);
8048 } else {
8049 nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox);
8050 radii = {radius, radius};
8053 nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center -
8054 nsPoint(radii.width, radii.height),
8055 radii * 2);
8057 nscoord ellipseRadii[8];
8058 for (const auto corner : AllPhysicalHalfCorners()) {
8059 ellipseRadii[corner] =
8060 HalfCornerIsX(corner) ? radii.width : radii.height;
8063 clipId = aBuilder.DefineRoundedRectClip(
8064 Nothing(), wr::ToComplexClipRegion(ellipseRect, ellipseRadii,
8065 appUnitsPerDevPixel));
8067 break;
8069 default:
8070 // Please don't add more exceptions, try to find a way to define the clip
8071 // without using a mask image.
8073 // And if you _really really_ need to add an exception, add it to
8074 // SVGUtils::DetermineMaskUsage
8075 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8076 return Nothing();
8079 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8081 return Some(clipChainId);
8084 static void FillPolygonDataForDisplayItem(
8085 const nsDisplayMasksAndClipPaths& aDisplayItem,
8086 nsTArray<wr::LayoutPoint>& aPoints, wr::FillRule& aFillRule) {
8087 nsIFrame* frame = aDisplayItem.Frame();
8088 const auto* style = frame->StyleSVGReset();
8089 bool isPolygon = style->HasClipPath() && style->mClipPath.IsShape() &&
8090 style->mClipPath.AsShape()._0->IsPolygon();
8091 if (!isPolygon) {
8092 return;
8095 const auto& clipPath = style->mClipPath;
8096 const auto& shape = *clipPath.AsShape()._0;
8097 const nsRect refBox =
8098 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8100 // We only fill polygon data for polygons that are below a complexity
8101 // limit.
8102 nsTArray<nsPoint> vertices =
8103 ShapeUtils::ComputePolygonVertices(shape, refBox);
8104 if (vertices.Length() > wr::POLYGON_CLIP_VERTEX_MAX) {
8105 return;
8108 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8110 for (size_t i = 0; i < vertices.Length(); ++i) {
8111 wr::LayoutPoint point = wr::ToLayoutPoint(
8112 LayoutDevicePoint::FromAppUnits(vertices[i], appUnitsPerDevPixel));
8113 aPoints.AppendElement(point);
8116 aFillRule = (shape.AsPolygon().fill == StyleFillRule::Nonzero)
8117 ? wr::FillRule::Nonzero
8118 : wr::FillRule::Evenodd;
8121 static Maybe<wr::WrClipChainId> CreateWRClipPathAndMasks(
8122 nsDisplayMasksAndClipPaths* aDisplayItem, const LayoutDeviceRect& aBounds,
8123 wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder,
8124 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8125 nsDisplayListBuilder* aDisplayListBuilder) {
8126 if (auto clip = CreateSimpleClipRegion(*aDisplayItem, aBuilder)) {
8127 return clip;
8130 Maybe<wr::ImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(
8131 aDisplayItem, aBuilder, aResources, aSc, aDisplayListBuilder, aBounds);
8132 if (!mask) {
8133 return Nothing();
8136 // We couldn't create a simple clip region, but before we create an image
8137 // mask clip, see if we can get a polygon clip to add to it.
8138 nsTArray<wr::LayoutPoint> points;
8139 wr::FillRule fillRule = wr::FillRule::Nonzero;
8140 FillPolygonDataForDisplayItem(*aDisplayItem, points, fillRule);
8142 wr::WrClipId clipId =
8143 aBuilder.DefineImageMaskClip(mask.ref(), points, fillRule);
8145 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8147 return Some(clipChainId);
8150 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8151 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8152 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8153 nsDisplayListBuilder* aDisplayListBuilder) {
8154 bool snap;
8155 auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8156 nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
8157 LayoutDeviceRect bounds =
8158 LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel);
8160 Maybe<wr::WrClipChainId> clip = CreateWRClipPathAndMasks(
8161 this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder);
8163 float oldOpacity = aBuilder.GetInheritedOpacity();
8165 Maybe<StackingContextHelper> layer;
8166 const StackingContextHelper* sc = &aSc;
8167 if (clip) {
8168 // Create a new stacking context to attach the mask to, ensuring the mask is
8169 // applied to the aggregate, and not the individual elements.
8171 // The stacking context shouldn't have any offset.
8172 bounds.MoveTo(0, 0);
8174 Maybe<float> opacity =
8175 (SVGUtils::DetermineMaskUsage(mFrame, false).IsSimpleClipShape() &&
8176 aBuilder.GetInheritedOpacity() != 1.0f)
8177 ? Some(aBuilder.GetInheritedOpacity())
8178 : Nothing();
8180 wr::StackingContextParams params;
8181 params.clip = wr::WrStackingContextClip::ClipChain(clip->id);
8182 params.opacity = opacity.ptrOr(nullptr);
8183 if (mWrapsBackdropFilter) {
8184 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8186 layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params,
8187 bounds);
8188 sc = layer.ptr();
8191 aBuilder.SetInheritedOpacity(1.0f);
8192 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8193 aBuilder.SetInheritedClipChain(nullptr);
8194 CreateWebRenderCommandsNewClipListOption(aBuilder, aResources, *sc, aManager,
8195 aDisplayListBuilder, layer.isSome());
8196 aBuilder.SetInheritedOpacity(oldOpacity);
8197 aBuilder.SetInheritedClipChain(oldClipChain);
8199 return true;
8202 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8203 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
8204 if (const DisplayItemClip* clip =
8205 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
8206 return Some(clip->GetClipRect());
8208 // This item does not have a clip with respect to |aASR|. However, we
8209 // might still have finite bounds with respect to |aASR|. Check our
8210 // children.
8211 nsDisplayList* childList = GetSameCoordinateSystemChildren();
8212 if (childList) {
8213 return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR));
8215 #ifdef DEBUG
8216 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
8217 #endif
8218 return Nothing();
8221 #ifdef MOZ_DUMP_PAINTING
8222 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) {
8223 nsIFrame* firstFrame =
8224 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8225 bool first = true;
8226 aTo += " effects=(";
8227 SVGClipPathFrame* clipPathFrame;
8228 // XXX Check return value?
8229 SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
8230 if (clipPathFrame) {
8231 if (!first) {
8232 aTo += ", ";
8234 aTo += nsPrintfCString(
8235 "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
8236 first = false;
8237 } else if (mFrame->StyleSVGReset()->HasClipPath()) {
8238 if (!first) {
8239 aTo += ", ";
8241 aTo += "clip(basic-shape)";
8242 first = false;
8245 nsTArray<SVGMaskFrame*> masks;
8246 // XXX check return value?
8247 SVGObserverUtils::GetAndObserveMasks(firstFrame, &masks);
8248 if (!masks.IsEmpty() && masks[0]) {
8249 if (!first) {
8250 aTo += ", ";
8252 aTo += "mask";
8254 aTo += ")";
8256 #endif
8258 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8259 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8260 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8261 nsDisplayListBuilder* aDisplayListBuilder) {
8262 WrFiltersHolder wrFilters;
8263 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8264 auto filterChain = style.StyleEffects()->mBackdropFilters.AsSpan();
8265 bool initialized = true;
8266 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8267 wrFilters) &&
8268 !SVGIntegrationUtils::BuildWebRenderFilters(
8269 mFrame, filterChain, StyleFilterType::BackdropFilter, wrFilters,
8270 initialized)) {
8271 // TODO: If painting backdrop-filters on the content side is implemented,
8272 // consider returning false to fall back to that.
8273 wrFilters = {};
8276 if (!initialized) {
8277 wrFilters = {};
8280 nsCSSRendering::ImageLayerClipState clip;
8281 nsCSSRendering::GetImageLayerClip(
8282 style.StyleBackground()->BottomLayer(), mFrame, *style.StyleBorder(),
8283 mBackdropRect, mBackdropRect, false,
8284 mFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
8286 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
8287 mBackdropRect, mFrame->PresContext()->AppUnitsPerDevPixel());
8289 wr::ComplexClipRegion region =
8290 wr::ToComplexClipRegion(clip.mBGClipArea, clip.mRadii,
8291 mFrame->PresContext()->AppUnitsPerDevPixel());
8293 aBuilder.PushBackdropFilter(wr::ToLayoutRect(bounds), region,
8294 wrFilters.filters, wrFilters.filter_datas,
8295 !BackfaceIsHidden());
8297 wr::StackingContextParams params;
8298 params.clip =
8299 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8300 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8301 params);
8303 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8304 aDisplayListBuilder);
8305 return true;
8308 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder* aBuilder,
8309 gfxContext* aCtx) {
8310 // TODO: Implement backdrop filters
8311 GetChildren()->Paint(aBuilder, aCtx,
8312 mFrame->PresContext()->AppUnitsPerDevPixel());
8315 nsRect nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder* aBuilder,
8316 bool* aSnap) const {
8317 nsRect childBounds = nsDisplayWrapList::GetBounds(aBuilder, aSnap);
8319 *aSnap = false;
8321 return mBackdropRect.Union(childBounds);
8324 /* static */
8325 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder,
8326 nsIFrame* aFrame, nsDisplayList* aList,
8327 nsIFrame* aStyleFrame,
8328 bool aWrapsBackdropFilter)
8329 : nsDisplayEffectsBase(aBuilder, aFrame, aList),
8330 mStyle(aFrame == aStyleFrame ? nullptr : aStyleFrame->Style()),
8331 mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()),
8332 mWrapsBackdropFilter(aWrapsBackdropFilter) {
8333 MOZ_COUNT_CTOR(nsDisplayFilters);
8334 mVisibleRect = aBuilder->GetVisibleRect() +
8335 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
8338 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8339 PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) {
8340 GetChildren()->Paint(aBuilder, aContext,
8341 mFrame->PresContext()->AppUnitsPerDevPixel());
8345 void nsDisplayFilters::PaintWithContentsPaintCallback(
8346 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
8347 const std::function<void(gfxContext* aContext)>& aPaintChildren) {
8348 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
8349 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8350 PaintFramesParams params(*aCtx, mFrame, mVisibleRect, borderArea, aBuilder,
8351 false, imgParams);
8353 gfxPoint userSpaceToFrameSpaceOffset =
8354 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params);
8356 auto filterChain = mStyle ? mStyle->StyleEffects()->mFilters.AsSpan()
8357 : mFrame->StyleEffects()->mFilters.AsSpan();
8358 SVGIntegrationUtils::PaintFilter(
8359 params, filterChain,
8360 [&](gfxContext& aContext, imgDrawingParams&, const gfxMatrix*,
8361 const nsIntRect*) {
8362 gfxContextMatrixAutoSaveRestore autoSR(&aContext);
8363 aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(
8364 -userSpaceToFrameSpaceOffset));
8365 aPaintChildren(&aContext);
8369 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8370 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame);
8373 bool nsDisplayFilters::CreateWebRenderCommands(
8374 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8375 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8376 nsDisplayListBuilder* aDisplayListBuilder) {
8377 WrFiltersHolder wrFilters;
8378 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8379 auto filterChain = style.StyleEffects()->mFilters.AsSpan();
8380 bool initialized = true;
8381 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8382 wrFilters) &&
8383 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame, filterChain,
8384 StyleFilterType::Filter,
8385 wrFilters, initialized)) {
8386 if (mStyle) {
8387 // TODO(bug 1769223): Support fallback filters in the root code-path,
8388 // perhaps. For now treat it the same way as invalid filters.
8389 wrFilters = {};
8390 } else {
8391 // Draw using fallback.
8392 return false;
8396 if (!initialized) {
8397 // https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
8399 // If the filter references a non-existent object or the referenced object
8400 // is not a filter element, then the whole filter chain is ignored. No
8401 // filter is applied to the object.
8403 // Note that other engines have a weird discrepancy between SVG and HTML
8404 // content here, but the spec is clear.
8405 wrFilters = {};
8408 uint64_t clipChainId;
8409 if (wrFilters.post_filters_clip) {
8410 auto devPxRect = LayoutDeviceRect::FromAppUnits(
8411 wrFilters.post_filters_clip.value() + ToReferenceFrame(),
8412 mFrame->PresContext()->AppUnitsPerDevPixel());
8413 auto clipId =
8414 aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect));
8415 clipChainId = aBuilder.DefineClipChain({clipId}, true).id;
8416 } else {
8417 clipChainId = aBuilder.CurrentClipChainId();
8419 wr::WrStackingContextClip clip =
8420 wr::WrStackingContextClip::ClipChain(clipChainId);
8422 float opacity = aBuilder.GetInheritedOpacity();
8423 aBuilder.SetInheritedOpacity(1.0f);
8424 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8425 aBuilder.SetInheritedClipChain(nullptr);
8426 wr::StackingContextParams params;
8427 params.mFilters = std::move(wrFilters.filters);
8428 params.mFilterDatas = std::move(wrFilters.filter_datas);
8429 params.opacity = opacity != 1.0f ? &opacity : nullptr;
8430 params.clip = clip;
8431 if (mWrapsBackdropFilter) {
8432 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8434 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8435 params);
8437 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
8438 aManager, aDisplayListBuilder);
8439 aBuilder.SetInheritedOpacity(opacity);
8440 aBuilder.SetInheritedClipChain(oldClipChain);
8442 return true;
8445 #ifdef MOZ_DUMP_PAINTING
8446 void nsDisplayFilters::PrintEffects(nsACString& aTo) {
8447 nsIFrame* firstFrame =
8448 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8449 bool first = true;
8450 aTo += " effects=(";
8451 // We may exist for a mix of CSS filter functions and/or references to SVG
8452 // filters. If we have invalid references to SVG filters then we paint
8453 // nothing, but otherwise we will apply one or more filters.
8454 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) !=
8455 SVGObserverUtils::eHasRefsSomeInvalid) {
8456 if (!first) {
8457 aTo += ", ";
8459 aTo += "filter";
8461 aTo += ")";
8463 #endif
8465 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder,
8466 nsIFrame* aFrame, nsDisplayList* aList)
8467 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8468 MOZ_COUNT_CTOR(nsDisplaySVGWrapper);
8471 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8472 return !aBuilder->GetWidgetLayerManager();
8475 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8476 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8477 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8478 nsDisplayListBuilder* aDisplayListBuilder) {
8479 return CreateWebRenderCommandsNewClipListOption(
8480 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8483 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder* aBuilder,
8484 nsIFrame* aFrame,
8485 nsDisplayList* aList)
8486 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8487 MOZ_COUNT_CTOR(nsDisplayForeignObject);
8490 #ifdef NS_BUILD_REFCNT_LOGGING
8491 nsDisplayForeignObject::~nsDisplayForeignObject() {
8492 MOZ_COUNT_DTOR(nsDisplayForeignObject);
8494 #endif
8496 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8497 return !aBuilder->GetWidgetLayerManager();
8500 bool nsDisplayForeignObject::CreateWebRenderCommands(
8501 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8502 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8503 nsDisplayListBuilder* aDisplayListBuilder) {
8504 AutoRestore<bool> restoreDoGrouping(aManager->CommandBuilder().mDoGrouping);
8505 aManager->CommandBuilder().mDoGrouping = false;
8506 return CreateWebRenderCommandsNewClipListOption(
8507 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8510 void nsDisplayLink::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8511 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8512 aCtx->GetDrawTarget()->Link(
8513 mLinkSpec.get(), NSRectToRect(GetPaintRect(aBuilder, aCtx), appPerDev));
8516 void nsDisplayDestination::Paint(nsDisplayListBuilder* aBuilder,
8517 gfxContext* aCtx) {
8518 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8519 aCtx->GetDrawTarget()->Destination(
8520 mDestinationName.get(),
8521 NSPointToPoint(GetPaintRect(aBuilder, aCtx).TopLeft(), appPerDev));
8524 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8525 nsDisplayList* aOutResultList, nsIContent* aContent) {
8526 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
8527 // in content document order and SortByZOrder is a stable sort which
8528 // guarantees that boxes produced by the same element are placed together
8529 // in the sort. Consider a position:relative inline element that breaks
8530 // across lines and has absolutely positioned children; all the abs-pos
8531 // children should be z-ordered after all the boxes for the position:relative
8532 // element itself.
8533 PositionedDescendants()->SortByZOrder();
8535 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8536 // 1,2: backgrounds and borders
8537 aOutResultList->AppendToTop(BorderBackground());
8538 // 3: negative z-index children.
8539 for (auto* item : PositionedDescendants()->TakeItems()) {
8540 if (item->ZIndex() < 0) {
8541 aOutResultList->AppendToTop(item);
8542 } else {
8543 PositionedDescendants()->AppendToTop(item);
8547 // 4: block backgrounds
8548 aOutResultList->AppendToTop(BlockBorderBackgrounds());
8549 // 5: floats
8550 aOutResultList->AppendToTop(Floats());
8551 // 7: general content
8552 aOutResultList->AppendToTop(Content());
8553 // 7.5: outlines, in content tree order. We need to sort by content order
8554 // because an element with outline that breaks and has children with outline
8555 // might have placed child outline items between its own outline items.
8556 // The element's outline items need to all come before any child outline
8557 // items.
8558 if (aContent) {
8559 Outlines()->SortByContentOrder(aContent);
8561 aOutResultList->AppendToTop(Outlines());
8562 // 8, 9: non-negative z-index children
8563 aOutResultList->AppendToTop(PositionedDescendants());
8566 uint32_t PaintTelemetry::sPaintLevel = 0;
8568 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8569 // Don't record nested paints.
8570 if (sPaintLevel++ > 0) {
8571 return;
8574 mStart = TimeStamp::Now();
8577 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8578 MOZ_ASSERT(sPaintLevel != 0);
8579 if (--sPaintLevel > 0) {
8580 return;
8583 // If we're in multi-process mode, don't include paint times for the parent
8584 // process.
8585 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8586 return;
8589 // Record the total time.
8590 mozilla::glean::gfx_content::paint_time.AccumulateRawDuration(
8591 TimeStamp::Now() - mStart);
8594 static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
8595 if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
8596 return aFrame;
8599 if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
8600 !aFrame->GetPrevInFlow()) {
8601 return aFrame->GetPlaceholderFrame();
8604 return aFrame;
8607 static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
8608 nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
8609 MOZ_ASSERT(f);
8610 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
8613 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8614 nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
8615 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
8616 const bool aIsTransformed)
8617 : mBuilder(aBuilder),
8618 mPrevFrame(aBuilder->mCurrentFrame),
8619 mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
8620 mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
8621 mPrevAdditionalOffset(aBuilder->mAdditionalOffset),
8622 mPrevVisibleRect(aBuilder->mVisibleRect),
8623 mPrevDirtyRect(aBuilder->mDirtyRect),
8624 mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo),
8625 mPrevAncestorHasApzAwareEventHandler(
8626 aBuilder->mAncestorHasApzAwareEventHandler),
8627 mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
8628 mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
8629 if (aIsTransformed) {
8630 aBuilder->mCurrentOffsetToReferenceFrame =
8631 aBuilder->AdditionalOffset().refOr(nsPoint());
8632 aBuilder->mCurrentReferenceFrame = aForChild;
8633 } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
8634 aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
8635 } else {
8636 aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
8637 aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
8640 // If aForChild is being visited from a frame other than it's ancestor frame,
8641 // mInInvalidSubtree will need to be recalculated the slow way.
8642 if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) {
8643 aBuilder->mInInvalidSubtree =
8644 aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
8645 } else {
8646 aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
8649 aBuilder->mCurrentFrame = aForChild;
8650 aBuilder->mVisibleRect = aVisibleRect;
8651 aBuilder->mDirtyRect =
8652 aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
8655 } // namespace mozilla