Bug 1860328 - Fix a bug where caret misses when dragging and dropping r=emilio
[gecko.git] / layout / painting / nsDisplayList.cpp
blob2343bd3b58aa3a95c0900e5e554a329f0e969eb0
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 mScrollInfoItemsForHoisting(nullptr),
654 mFirstClipChainToDestroy(nullptr),
655 mTableBackgroundSet(nullptr),
656 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
657 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
658 mFilterASR(nullptr),
659 mDirtyRect(-1, -1, -1, -1),
660 mBuildingExtraPagesForPageNum(0),
661 mMode(aMode),
662 mContainsBlendMode(false),
663 mIsBuildingScrollbar(false),
664 mCurrentScrollbarWillHaveLayer(false),
665 mBuildCaret(aBuildCaret),
666 mRetainingDisplayList(aRetainingDisplayList),
667 mPartialUpdate(false),
668 mIgnoreSuppression(false),
669 mIncludeAllOutOfFlows(false),
670 mDescendIntoSubdocuments(true),
671 mSelectedFramesOnly(false),
672 mAllowMergingAndFlattening(true),
673 mInTransform(false),
674 mInEventsOnly(false),
675 mInFilter(false),
676 mInPageSequence(false),
677 mIsInChromePresContext(false),
678 mSyncDecodeImages(false),
679 mIsPaintingToWindow(false),
680 mUseHighQualityScaling(false),
681 mIsPaintingForWebRender(false),
682 mIsCompositingCheap(false),
683 mAncestorHasApzAwareEventHandler(false),
684 mHaveScrollableDisplayPort(false),
685 mWindowDraggingAllowed(false),
686 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
687 mForceLayerForScrollParent(false),
688 mContainsNonMinimalDisplayPort(false),
689 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
690 mBuildingInvisibleItems(false),
691 mIsBuilding(false),
692 mInInvalidSubtree(false),
693 mDisablePartialUpdates(false),
694 mPartialBuildFailed(false),
695 mIsInActiveDocShell(false),
696 mBuildAsyncZoomContainer(false),
697 mIsRelativeToLayoutViewport(false),
698 mUseOverlayScrollbars(false),
699 mAlwaysLayerizeScrollbars(false) {
700 MOZ_COUNT_CTOR(nsDisplayListBuilder);
702 mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting();
704 ShouldRebuildDisplayListDueToPrefChange();
706 mUseOverlayScrollbars =
707 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
709 mAlwaysLayerizeScrollbars =
710 StaticPrefs::layout_scrollbars_always_layerize_track();
712 static_assert(
713 static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
714 "Check TYPE_MAX should not overflow");
716 mIsReusingStackingContextItems =
717 mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
720 void nsDisplayListBuilder::BeginFrame() {
721 nsCSSRendering::BeginFrameTreesLocked();
723 mIsPaintingToWindow = false;
724 mUseHighQualityScaling = false;
725 mIgnoreSuppression = false;
726 mInTransform = false;
727 mInFilter = false;
728 mSyncDecodeImages = false;
731 void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser* aBrowser,
732 const dom::EffectsInfo& aUpdate) {
733 dom::EffectsInfo update = aUpdate;
734 // For printing we create one display item for each page that an iframe
735 // appears on, the proper visible rect is the union of all the visible rects
736 // we get from each display item.
737 nsPresContext* pc =
738 mReferenceFrame ? mReferenceFrame->PresContext() : nullptr;
739 if (pc && pc->Type() != nsPresContext::eContext_Galley) {
740 Maybe<dom::EffectsInfo> existing = mEffectsUpdates.MaybeGet(aBrowser);
741 if (existing) {
742 // Only the visible rect should differ, the scales should match.
743 MOZ_ASSERT(existing->mRasterScale == aUpdate.mRasterScale &&
744 existing->mTransformToAncestorScale ==
745 aUpdate.mTransformToAncestorScale);
746 if (existing->mVisibleRect) {
747 if (update.mVisibleRect) {
748 update.mVisibleRect =
749 Some(update.mVisibleRect->Union(*existing->mVisibleRect));
750 } else {
751 update.mVisibleRect = existing->mVisibleRect;
756 mEffectsUpdates.InsertOrUpdate(aBrowser, update);
759 void nsDisplayListBuilder::EndFrame() {
760 NS_ASSERTION(!mInInvalidSubtree,
761 "Someone forgot to cleanup mInInvalidSubtree!");
762 mCurrentContainerASR = nullptr;
763 mActiveScrolledRoots.Clear();
764 mEffectsUpdates.Clear();
765 FreeClipChains();
766 FreeTemporaryItems();
767 nsCSSRendering::EndFrameTreesLocked();
770 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
771 const nsIFrame* aStopAtFrame) {
772 mFramesMarkedForDisplay.AppendElement(aFrame);
773 for (nsIFrame* f = aFrame; f;
774 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
775 if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
776 return;
778 f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
779 if (f == aStopAtFrame) {
780 // we've reached a frame that we know will be painted, so we can stop.
781 break;
786 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) {
787 mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
790 static void MarkFrameForDisplayIfVisibleInternal(nsIFrame* aFrame,
791 const nsIFrame* aStopAtFrame) {
792 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
793 if (f->ForceDescendIntoIfVisible()) {
794 return;
796 f->SetForceDescendIntoIfVisible(true);
798 // This condition must match the condition in
799 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
800 // nsLayoutUtils::GetDisplayListParent
801 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
802 nsIFrame* parent = f->GetParent();
803 if (parent && !parent->ForceDescendIntoIfVisible()) {
804 // If the GetDisplayListParent call is going to walk to a placeholder,
805 // in rare cases the placeholder might be contained in a different
806 // continuation from the oof. So we have to make sure to mark the oofs
807 // parent. In the common case this doesn't make us do any extra work,
808 // just changes the order in which we visit the frames since walking
809 // through placeholders will walk through the parent, and we stop when
810 // we find a ForceDescendIntoIfVisible bit set.
811 MarkFrameForDisplayIfVisibleInternal(parent, aStopAtFrame);
815 if (f == aStopAtFrame) {
816 // we've reached a frame that we know will be painted, so we can stop.
817 break;
822 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
823 nsIFrame* aFrame, const nsIFrame* aStopAtFrame) {
824 AddFrameMarkedForDisplayIfVisible(aFrame);
826 MarkFrameForDisplayIfVisibleInternal(aFrame, aStopAtFrame);
829 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
830 mIsRelativeToLayoutViewport = true;
831 UpdateShouldBuildAsyncZoomContainer();
834 void nsDisplayListBuilder::ForceLayerForScrollParent() {
835 mForceLayerForScrollParent = true;
836 mNumActiveScrollframesEncountered++;
839 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
840 const Document* document = mReferenceFrame->PresContext()->Document();
841 mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
842 !document->Fullscreen() &&
843 nsLayoutUtils::AllowZoomingForDocument(document);
846 // Certain prefs may cause display list items to be added or removed when they
847 // are toggled. In those cases, we need to fully rebuild the display list.
848 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
849 // If we transition between wrapping the RCD-RSF contents into an async
850 // zoom container vs. not, we need to rebuild the display list. This only
851 // happens when the zooming or container scrolling prefs are toggled
852 // (manually by the user, or during test setup).
853 bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer;
854 UpdateShouldBuildAsyncZoomContainer();
856 bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars;
857 mUseOverlayScrollbars =
858 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
860 bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars;
861 mAlwaysLayerizeScrollbars =
862 StaticPrefs::layout_scrollbars_always_layerize_track();
864 if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) {
865 return true;
868 if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) {
869 return true;
872 if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) {
873 return true;
876 return false;
879 void nsDisplayListBuilder::AddScrollFrameToNotify(
880 nsIScrollableFrame* aScrollFrame) {
881 mScrollFramesToNotify.insert(aScrollFrame);
884 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
885 for (const auto& it : mScrollFramesToNotify) {
886 it->NotifyApzTransaction();
888 mScrollFramesToNotify.clear();
891 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
892 nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect,
893 const nsRect& aDirtyRect) {
894 MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
895 nsRect dirty;
896 nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
897 this, aFrame, aVisibleRect, aDirtyRect, &dirty);
898 if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
899 visible.IsEmpty()) {
900 return false;
903 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
904 // frame, then it will also mark any outer frames to ensure that building
905 // reaches the dirty feame.
906 if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
907 MarkFrameForDisplay(aFrame, aDirtyFrame);
910 return true;
913 static void UnmarkFrameForDisplay(nsIFrame* aFrame,
914 const nsIFrame* aStopAtFrame) {
915 for (nsIFrame* f = aFrame; f;
916 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
917 if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
918 return;
920 f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
921 if (f == aStopAtFrame) {
922 // we've reached a frame that we know will be painted, so we can stop.
923 break;
928 static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
929 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
930 if (!f->ForceDescendIntoIfVisible()) {
931 return;
933 f->SetForceDescendIntoIfVisible(false);
935 // This condition must match the condition in
936 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
937 // nsLayoutUtils::GetDisplayListParent
938 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
939 nsIFrame* parent = f->GetParent();
940 if (parent && parent->ForceDescendIntoIfVisible()) {
941 // If the GetDisplayListParent call is going to walk to a placeholder,
942 // in rare cases the placeholder might be contained in a different
943 // continuation from the oof. So we have to make sure to mark the oofs
944 // parent. In the common case this doesn't make us do any extra work,
945 // just changes the order in which we visit the frames since walking
946 // through placeholders will walk through the parent, and we stop when
947 // we find a ForceDescendIntoIfVisible bit set.
948 UnmarkFrameForDisplayIfVisible(f);
954 nsDisplayListBuilder::~nsDisplayListBuilder() {
955 NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
956 "All frames should have been unmarked");
957 NS_ASSERTION(mFramesWithOOFData.Length() == 0,
958 "All OOF data should have been removed");
959 NS_ASSERTION(mPresShellStates.Length() == 0,
960 "All presshells should have been exited");
962 DisplayItemClipChain* c = mFirstClipChainToDestroy;
963 while (c) {
964 DisplayItemClipChain* next = c->mNextClipChainToDestroy;
965 c->DisplayItemClipChain::~DisplayItemClipChain();
966 c = next;
969 MOZ_COUNT_DTOR(nsDisplayListBuilder);
972 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
973 uint32_t flags = 0;
974 if (mSyncDecodeImages) {
975 flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
977 if (mIsPaintingToWindow) {
978 flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
980 if (mUseHighQualityScaling) {
981 flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING;
983 return flags;
986 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
987 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
988 uint32_t flags = 0;
989 if (mSyncDecodeImages) {
990 flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
992 if (mIsPaintingToWindow) {
993 flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
995 if (mUseHighQualityScaling) {
996 flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
998 return flags;
1001 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1002 uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
1003 if (mSyncDecodeImages) {
1004 flags |= imgIContainer::FLAG_SYNC_DECODE;
1005 } else {
1006 flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
1008 if (mIsPaintingToWindow || mUseHighQualityScaling) {
1009 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1011 return flags;
1014 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
1015 const nsRegion& aRegion) {
1016 if (aRegion.IsEmpty()) {
1017 return;
1020 nsRegion tmp;
1021 tmp.Sub(*aVisibleRegion, aRegion);
1022 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1023 // to its bounds either, which can be very bad (see bug 516740).
1024 // Do let aVisibleRegion get more complex if by doing so we reduce its
1025 // area by at least half.
1026 if (tmp.GetNumRects() <= 15 || tmp.Area() <= aVisibleRegion->Area() / 2) {
1027 *aVisibleRegion = tmp;
1031 nsCaret* nsDisplayListBuilder::GetCaret() {
1032 RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
1033 return caret;
1036 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) {
1037 if (mIsPaintingToWindow) {
1038 aPresShell->IncrementPaintCount();
1042 void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
1043 bool aPointerEventsNoneDoc) {
1044 PresShellState* state = mPresShellStates.AppendElement();
1045 state->mPresShell = aReferenceFrame->PresShell();
1046 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
1047 state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
1049 nsIScrollableFrame* sf = state->mPresShell->GetRootScrollFrameAsScrollable();
1050 if (sf && IsInSubdocument()) {
1051 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1052 // that the canvas background color will be set correctly, and that only one
1053 // unscrollable item will be created.
1054 // This is done to avoid, for example, a case where only scrollbar frames
1055 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1056 // and possibly end up with an extra nsDisplaySolidColor item.
1057 // We skip this for the root document, since we don't want to use
1058 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1059 // do it manually there.
1060 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
1061 if (canvasFrame) {
1062 MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
1066 #ifdef DEBUG
1067 state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
1068 nsLayoutPhase::DisplayListBuilding);
1069 #endif
1071 state->mPresShell->UpdateCanvasBackground();
1073 bool buildCaret = mBuildCaret;
1074 if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
1075 state->mIsBackgroundOnly = false;
1076 } else {
1077 state->mIsBackgroundOnly = true;
1078 buildCaret = false;
1081 bool pointerEventsNone = aPointerEventsNoneDoc;
1082 if (IsInSubdocument()) {
1083 pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
1084 .mInsidePointerEventsNoneDoc;
1086 state->mInsidePointerEventsNoneDoc = pointerEventsNone;
1088 state->mPresShellIgnoreScrollFrame =
1089 state->mPresShell->IgnoringViewportScrolling()
1090 ? state->mPresShell->GetRootScrollFrame()
1091 : nullptr;
1093 nsPresContext* pc = aReferenceFrame->PresContext();
1094 mIsInChromePresContext = pc->IsChrome();
1095 nsIDocShell* docShell = pc->GetDocShell();
1097 if (docShell) {
1098 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1101 state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);
1103 if (!buildCaret) {
1104 return;
1107 RefPtr<nsCaret> caret = state->mPresShell->GetCaret();
1108 // This code run for each pres shell and caret->GetPaintGeometry
1109 // will return nullptr for invisible caret. So only one caret
1110 // can be painted at a time.
1111 state->mCaretFrame = caret->GetPaintGeometry(&mCaretRect);
1113 // Check if the display root for the caret matches the display
1114 // root that we're painting, and only use it if it matches. Likely
1115 // we only need this for popup.
1116 if (state->mCaretFrame &&
1117 nsLayoutUtils::GetDisplayRootFrame(state->mCaretFrame) !=
1118 nsLayoutUtils::GetDisplayRootFrame(aReferenceFrame)) {
1119 state->mCaretFrame = nullptr;
1122 // Caret frames add visual area to their frame, but we don't update the
1123 // overflow area. Use flags to make sure we build display items for that frame
1124 // instead.
1125 if (state->mCaretFrame) {
1126 MOZ_ASSERT(state->mCaretFrame->PresShell() == state->mPresShell);
1127 MarkFrameForDisplay(state->mCaretFrame, aReferenceFrame);
1131 // A non-blank paint is a paint that does not just contain the canvas
1132 // background.
1133 static bool DisplayListIsNonBlank(nsDisplayList* aList) {
1134 for (nsDisplayItem* i : *aList) {
1135 switch (i->GetType()) {
1136 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
1137 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
1138 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
1139 continue;
1140 case DisplayItemType::TYPE_SOLID_COLOR:
1141 case DisplayItemType::TYPE_BACKGROUND:
1142 case DisplayItemType::TYPE_BACKGROUND_COLOR:
1143 if (i->Frame()->IsCanvasFrame()) {
1144 continue;
1146 return true;
1147 default:
1148 return true;
1151 return false;
1154 // A contentful paint is a paint that does contains DOM content (text,
1155 // images, non-blank canvases, SVG): "First Contentful Paint entry
1156 // contains a DOMHighResTimeStamp reporting the time when the browser
1157 // first rendered any text, image (including background images),
1158 // non-white canvas or SVG. This excludes any content of iframes, but
1159 // includes text with pending webfonts. This is the first time users
1160 // could start consuming page content."
1161 static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
1162 nsDisplayList* aList) {
1163 for (nsDisplayItem* i : *aList) {
1164 DisplayItemType type = i->GetType();
1165 nsDisplayList* children = i->GetChildren();
1167 switch (type) {
1168 case DisplayItemType::TYPE_SUBDOCUMENT: // iframes are ignored
1169 break;
1170 // CANVASes check if they may have been modified (as a stand-in
1171 // actually tracking all modifications)
1172 default:
1173 if (i->IsContentful()) {
1174 bool dummy;
1175 nsRect bound = i->GetBounds(aBuilder, &dummy);
1176 if (!bound.IsEmpty()) {
1177 return true;
1180 if (children) {
1181 if (DisplayListIsContentful(aBuilder, children)) {
1182 return true;
1185 break;
1188 return false;
1191 void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
1192 nsDisplayList* aPaintedContents) {
1193 NS_ASSERTION(
1194 CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
1195 "Presshell mismatch");
1197 if (mIsPaintingToWindow && aPaintedContents) {
1198 nsPresContext* pc = aReferenceFrame->PresContext();
1199 if (!pc->HadNonBlankPaint()) {
1200 if (!CurrentPresShellState()->mIsBackgroundOnly &&
1201 DisplayListIsNonBlank(aPaintedContents)) {
1202 pc->NotifyNonBlankPaint();
1205 nsRootPresContext* rootPresContext = pc->GetRootPresContext();
1206 if (!pc->HasStoppedGeneratingLCP() && rootPresContext) {
1207 if (!CurrentPresShellState()->mIsBackgroundOnly) {
1208 if (pc->HasEverBuiltInvisibleText() ||
1209 DisplayListIsContentful(this, aPaintedContents)) {
1210 pc->NotifyContentfulPaint();
1216 ResetMarkedFramesForDisplayList(aReferenceFrame);
1217 mPresShellStates.RemoveLastElement();
1219 if (!mPresShellStates.IsEmpty()) {
1220 nsPresContext* pc = CurrentPresContext();
1221 nsIDocShell* docShell = pc->GetDocShell();
1222 if (docShell) {
1223 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1225 mIsInChromePresContext = pc->IsChrome();
1226 } else {
1227 for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
1228 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
1230 mFramesMarkedForDisplayIfVisible.SetLength(0);
1234 void nsDisplayListBuilder::FreeClipChains() {
1235 // Iterate the clip chains from newest to oldest (forward
1236 // iteration), so that we destroy descendants first which
1237 // will drop the ref count on their ancestors.
1238 DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;
1240 while (*indirect) {
1241 if (!(*indirect)->mRefCount) {
1242 DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;
1244 mClipDeduplicator.erase(*indirect);
1245 (*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
1246 Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);
1248 *indirect = next;
1249 } else {
1250 indirect = &(*indirect)->mNextClipChainToDestroy;
1255 void nsDisplayListBuilder::FreeTemporaryItems() {
1256 for (nsDisplayItem* i : mTemporaryItems) {
1257 // Temporary display items are not added to the frames.
1258 MOZ_ASSERT(i->Frame());
1259 i->RemoveFrame(i->Frame());
1260 i->Destroy(this);
1263 mTemporaryItems.Clear();
1266 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1267 const nsIFrame* aReferenceFrame) {
1268 // Unmark and pop off the frames marked for display in this pres shell.
1269 uint32_t firstFrameForShell =
1270 CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1271 for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
1272 ++i) {
1273 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
1275 mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1277 firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
1278 for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
1279 mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
1281 mFramesWithOOFData.SetLength(firstFrameForShell);
1284 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1285 CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
1288 void nsDisplayListBuilder::MarkFramesForDisplayList(
1289 nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
1290 nsRect visibleRect = GetVisibleRect();
1291 nsRect dirtyRect = GetDirtyRect();
1293 // If we are entering content that is fixed to the RCD-RSF, we are
1294 // crossing the async zoom container boundary, and need to convert from
1295 // visual to layout coordinates.
1296 if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
1297 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1298 viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
1299 if (viewportFrame->PresShell()->GetRootScrollFrame()) {
1300 #ifdef DEBUG
1301 for (nsIFrame* f : aFrames) {
1302 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
1304 #endif
1305 visibleRect = ViewportUtils::VisualToLayout(visibleRect,
1306 viewportFrame->PresShell());
1307 dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
1308 viewportFrame->PresShell());
1310 #ifdef DEBUG
1311 else {
1312 // This is an edge case that should only happen if we are in a
1313 // document with a XUL root element so that it does not have a root
1314 // scroll frame but it has fixed pos content and all of the frames in
1315 // aFrames are that fixed pos content.
1316 for (nsIFrame* f : aFrames) {
1317 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
1318 f->GetParent() == aDirtyFrame &&
1319 f->StyleDisplay()->mPosition ==
1320 StylePositionProperty::Fixed);
1322 // There's no root scroll frame so there can't be any zooming or async
1323 // panning so we don't need to adjust the visible and dirty rects.
1325 #endif
1329 bool markedFrames = false;
1330 for (nsIFrame* e : aFrames) {
1331 // Skip the AccessibleCaret frame when building no caret.
1332 if (!IsBuildingCaret()) {
1333 nsIContent* content = e->GetContent();
1334 if (content && content->IsInNativeAnonymousSubtree() &&
1335 content->IsElement()) {
1336 const nsAttrValue* classes = content->AsElement()->GetClasses();
1337 if (classes &&
1338 classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) {
1339 continue;
1343 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
1344 markedFrames = true;
1348 if (markedFrames) {
1349 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1350 // to objects on the stack, so we need to clone the chain.
1351 const DisplayItemClipChain* clipChain =
1352 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1353 const DisplayItemClipChain* combinedClipChain =
1354 mClipState.GetCurrentCombinedClipChain(this);
1355 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1357 OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
1358 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1359 visibleRect, dirtyRect);
1360 aDirtyFrame->SetProperty(
1361 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
1362 mFramesWithOOFData.AppendElement(aDirtyFrame);
1365 if (!aDirtyFrame->GetParent()) {
1366 // This is the viewport frame of aDirtyFrame's presshell.
1367 // Store the current display data so that it can be used for fixed
1368 // background images.
1369 NS_ASSERTION(
1370 CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
1371 "Presshell mismatch");
1372 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
1373 "already traversed this presshell's root frame?");
1375 const DisplayItemClipChain* clipChain =
1376 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1377 const DisplayItemClipChain* combinedClipChain =
1378 mClipState.GetCurrentCombinedClipChain(this);
1379 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1380 CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
1381 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1382 GetVisibleRect(), GetDirtyRect());
1387 * Mark all preserve-3d children with
1388 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1389 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1390 * dirty rect for preserve-3d children.
1392 * @param aDirtyFrame is the frame to mark children extending context.
1394 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1395 nsIFrame* aDirtyFrame) {
1396 for (const auto& childList : aDirtyFrame->ChildLists()) {
1397 for (nsIFrame* child : childList.mList) {
1398 if (child->Combines3DTransformWithAncestors()) {
1399 MarkFrameForDisplay(child, aDirtyFrame);
1402 if (child->IsBlockWrapper()) {
1403 // Mark preserve-3d frames inside the block wrapper.
1404 MarkPreserve3DFramesForDisplayList(child);
1410 ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1411 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame) {
1412 RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
1413 aParent, aScrollableFrame, IsRetainingDisplayList());
1414 mActiveScrolledRoots.AppendElement(asr);
1415 return asr;
1418 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1419 const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
1420 const DisplayItemClipChain* aParent) {
1421 MOZ_DIAGNOSTIC_ASSERT(!(aParent && aParent->mOnStack));
1422 void* p = Allocate(sizeof(DisplayItemClipChain),
1423 DisplayListArenaObjectId::CLIPCHAIN);
1424 DisplayItemClipChain* c = new (KnownNotNull, p)
1425 DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy);
1426 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
1427 c->mOnStack = false;
1428 #endif
1429 auto result = mClipDeduplicator.insert(c);
1430 if (!result.second) {
1431 // An equivalent clip chain item was already created, so let's return that
1432 // instead. Destroy the one we just created.
1433 // Note that this can cause clip chains from different coordinate systems to
1434 // collapse into the same clip chain object, because clip chains do not keep
1435 // track of the reference frame that they were created in.
1436 c->DisplayItemClipChain::~DisplayItemClipChain();
1437 Destroy(DisplayListArenaObjectId::CLIPCHAIN, c);
1438 return *(result.first);
1440 mFirstClipChainToDestroy = c;
1441 return c;
1444 struct ClipChainItem {
1445 DisplayItemClip clip;
1446 const ActiveScrolledRoot* asr;
1449 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
1450 const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
1451 for (const ActiveScrolledRoot* asr =
1452 ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
1453 asr; asr = asr->mParent) {
1454 if (aOne == aTwo) {
1455 return aOne;
1457 if (aOne->mASR == asr) {
1458 aOne = aOne->mParent;
1460 if (aTwo->mASR == asr) {
1461 aTwo = aTwo->mParent;
1463 if (!aOne) {
1464 return aTwo;
1466 if (!aTwo) {
1467 return aOne;
1470 return nullptr;
1473 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1474 const DisplayItemClipChain* aAncestor,
1475 const DisplayItemClipChain* aLeafClip1,
1476 const DisplayItemClipChain* aLeafClip2) {
1477 AutoTArray<ClipChainItem, 8> intersectedClips;
1479 const DisplayItemClipChain* clip1 = aLeafClip1;
1480 const DisplayItemClipChain* clip2 = aLeafClip2;
1482 const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
1483 clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);
1485 // Build up the intersection from the leaf to the root and put it into
1486 // intersectedClips. The loop below will convert intersectedClips into an
1487 // actual DisplayItemClipChain.
1488 // (We need to do this in two passes because we need the parent clip in order
1489 // to create the DisplayItemClipChain object, but the parent clip has not
1490 // been created at that point.)
1491 while (!aAncestor || asr != aAncestor->mASR) {
1492 if (clip1 && clip1->mASR == asr) {
1493 if (clip2 && clip2->mASR == asr) {
1494 DisplayItemClip intersection = clip1->mClip;
1495 intersection.IntersectWith(clip2->mClip);
1496 intersectedClips.AppendElement(ClipChainItem{intersection, asr});
1497 clip2 = clip2->mParent;
1498 } else {
1499 intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
1501 clip1 = clip1->mParent;
1502 } else if (clip2 && clip2->mASR == asr) {
1503 intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
1504 clip2 = clip2->mParent;
1506 if (!asr) {
1507 MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
1508 break;
1510 asr = asr->mParent;
1513 // Convert intersectedClips into a DisplayItemClipChain.
1514 const DisplayItemClipChain* parentSC = aAncestor;
1515 for (auto& sc : Reversed(intersectedClips)) {
1516 parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
1518 return parentSC;
1521 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1522 const DisplayItemClipChain* aLeafClip1,
1523 const DisplayItemClipChain* aLeafClip2) {
1524 // aLeafClip2 might be a reference to a clip on the stack. We need to make
1525 // sure that CreateClipChainIntersection will allocate the actual intersected
1526 // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1527 // we supply nullptr as the common ancestor so that
1528 // CreateClipChainIntersection clones the whole chain.
1529 const DisplayItemClipChain* ancestorClip =
1530 aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
1531 : nullptr;
1533 return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2);
1536 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
1537 const DisplayItemClipChain* aClipChain) {
1538 return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
1541 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
1542 const nsIFrame* aFrame, nsPoint* aOffset) const {
1543 auto MaybeApplyAdditionalOffset = [&]() {
1544 if (auto offset = AdditionalOffset()) {
1545 *aOffset += *offset;
1549 if (aFrame == mCurrentFrame) {
1550 if (aOffset) {
1551 *aOffset = mCurrentOffsetToReferenceFrame;
1553 return mCurrentReferenceFrame;
1556 for (const nsIFrame* f = aFrame; f;
1557 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
1558 if (f == mReferenceFrame || f->IsTransformed()) {
1559 if (aOffset) {
1560 *aOffset = aFrame->GetOffsetToCrossDoc(f);
1561 MaybeApplyAdditionalOffset();
1563 return f;
1567 if (aOffset) {
1568 *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1569 MaybeApplyAdditionalOffset();
1572 return mReferenceFrame;
1575 // Sticky frames are active if their nearest scrollable frame is also active.
1576 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
1577 nsIFrame* aFrame) {
1578 MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
1579 StylePositionProperty::Sticky);
1581 StickyScrollContainer* stickyScrollContainer =
1582 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
1583 return stickyScrollContainer &&
1584 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1587 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
1588 nsIFrame** aParent) {
1589 if (aFrame == mReferenceFrame) {
1590 return true;
1593 if (!IsPaintingToWindow()) {
1594 if (aParent) {
1595 *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1597 return false;
1600 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1601 if (!parent) {
1602 return true;
1604 *aParent = parent;
1606 if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
1607 IsStickyFrameActive(this, aFrame)) {
1608 return true;
1611 if (aFrame->IsTransformed()) {
1612 if (EffectCompositor::HasAnimationsForCompositor(
1613 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
1614 return true;
1618 LayoutFrameType parentType = parent->Type();
1619 if (parentType == LayoutFrameType::Scroll ||
1620 parentType == LayoutFrameType::ListControl) {
1621 nsIScrollableFrame* sf = do_QueryFrame(parent);
1622 if (sf->GetScrolledFrame() == aFrame) {
1623 MOZ_ASSERT(!aFrame->IsTransformed());
1624 return sf->IsMaybeAsynchronouslyScrolled();
1628 return false;
1631 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1632 nsIFrame* aFrame) {
1633 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1634 RootReferenceFrame(), aFrame));
1635 nsIFrame* cursor = aFrame;
1636 while (cursor != RootReferenceFrame()) {
1637 nsIFrame* next;
1638 if (IsAnimatedGeometryRoot(cursor, &next)) {
1639 return cursor;
1641 cursor = next;
1643 return cursor;
1646 static nsRect ApplyAllClipNonRoundedIntersection(
1647 const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
1648 nsRect result = aRect;
1649 while (aClipChain) {
1650 result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
1651 aClipChain = aClipChain->mParent;
1653 return result;
1656 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
1657 if (!mWindowDraggingAllowed || !IsForPainting()) {
1658 return;
1661 const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
1662 if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
1663 // This frame has the default value and doesn't influence the window
1664 // dragging region.
1665 return;
1668 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;
1670 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1671 nsIFrame* referenceFrame =
1672 const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1674 if (IsInTransform()) {
1675 // Only support 2d rectilinear transforms. Transform support is needed for
1676 // the horizontal flip transform that's applied to the urlbar textbox in
1677 // RTL mode - it should be able to exclude itself from the draggable region.
1678 referenceFrameToRootReferenceFrame =
1679 ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
1680 nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
1681 RelativeTo{mReferenceFrame})
1682 .GetMatrix());
1683 Matrix referenceFrameToRootReferenceFrame2d;
1684 if (!referenceFrameToRootReferenceFrame.Is2D(
1685 &referenceFrameToRootReferenceFrame2d) ||
1686 !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1687 return;
1689 } else {
1690 MOZ_ASSERT(referenceFrame == mReferenceFrame,
1691 "referenceFrameToRootReferenceFrame needs to be adjusted");
1694 // We do some basic visibility checking on the frame's border box here.
1695 // We intersect it both with the current dirty rect and with the current
1696 // clip. Either one is just a conservative approximation on its own, but
1697 // their intersection luckily works well enough for our purposes, so that
1698 // we don't have to do full-blown visibility computations.
1699 // The most important case we need to handle is the scrolled-off tab:
1700 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1701 // should not be allowed to interfere with the window dragging region. Using
1702 // just the current DisplayItemClip is not enough to cover this case
1703 // completely because clips are reset while building stacking context
1704 // contents, so for example we'd fail to clip frames that have a clip path
1705 // applied to them. But the current dirty rect doesn't get reset in that
1706 // case, so we use it to make this case work.
1707 nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
1708 borderBox += ToReferenceFrame(aFrame);
1709 const DisplayItemClipChain* clip =
1710 ClipState().GetCurrentCombinedClipChain(this);
1711 borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
1712 if (borderBox.IsEmpty()) {
1713 return;
1716 LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
1717 borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1719 LayoutDeviceRect transformedDevPixelBorderBox =
1720 TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1721 transformedDevPixelBorderBox.Round();
1722 LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1724 if (!transformedDevPixelBorderBox.ToIntRect(
1725 &transformedDevPixelBorderBoxInt)) {
1726 return;
1729 LayoutDeviceIntRegion& region =
1730 styleUI->mWindowDragging == StyleWindowDragging::Drag
1731 ? mWindowDraggingRegion
1732 : mWindowNoDraggingRegion;
1734 if (!IsRetainingDisplayList()) {
1735 region.OrWith(transformedDevPixelBorderBoxInt);
1736 return;
1739 gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
1740 if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
1741 mRetainedWindowDraggingRegion.Add(aFrame, rect);
1742 } else {
1743 mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
1747 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
1748 LayoutDeviceIntRegion result;
1749 if (!IsRetainingDisplayList()) {
1750 result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
1751 return result;
1754 LayoutDeviceIntRegion dragRegion =
1755 mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();
1757 LayoutDeviceIntRegion noDragRegion =
1758 mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();
1760 result.Sub(dragRegion, noDragRegion);
1761 return result;
1764 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1765 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes);
1766 aSizes.mLayoutRetainedDisplayListSize +=
1767 aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get());
1770 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1771 mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);
1773 size_t n = 0;
1774 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
1775 n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1776 n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1777 n += mEffectsUpdates.ShallowSizeOfExcludingThis(mallocSizeOf);
1778 n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1779 n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1780 n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
1781 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1783 aSizes.mLayoutRetainedDisplayListSize += n;
1786 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1787 for (nsDisplayItem* item : *this) {
1788 item->AddSizeOfExcludingThis(aSizes);
1789 if (RetainedDisplayList* children = item->GetChildren()) {
1790 children->AddSizeOfExcludingThis(aSizes);
1794 size_t n = 0;
1796 n += mDAG.mDirectPredecessorList.ShallowSizeOfExcludingThis(
1797 aSizes.mState.mMallocSizeOf);
1798 n += mDAG.mNodesInfo.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1799 n += mOldItems.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1801 aSizes.mLayoutRetainedDisplayListSize += n;
1804 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1805 MallocSizeOf aMallocSizeOf) const {
1806 size_t n = 0;
1807 n += mFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
1808 for (const auto& frame : mFrames) {
1809 const UniquePtr<WeakFrame>& weakFrame = frame.mWeakFrame;
1810 n += aMallocSizeOf(weakFrame.get());
1812 n += mRects.ShallowSizeOfExcludingThis(aMallocSizeOf);
1813 return n;
1817 * Removes modified frames and rects from this WeakFrameRegion.
1819 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1820 MOZ_ASSERT(mFrames.Length() == mRects.Length());
1822 uint32_t i = 0;
1823 uint32_t length = mFrames.Length();
1825 while (i < length) {
1826 auto& wrapper = mFrames[i];
1828 if (!wrapper.mWeakFrame->IsAlive() ||
1829 AnyContentAncestorModified(wrapper.mWeakFrame->GetFrame())) {
1830 // To avoid multiple O(n) shifts in the array, move the last element of
1831 // the array to the current position and decrease the array length.
1832 mFrameSet.Remove(wrapper.mFrame);
1833 mFrames[i] = std::move(mFrames[length - 1]);
1834 mRects[i] = std::move(mRects[length - 1]);
1835 length--;
1836 } else {
1837 i++;
1841 mFrames.TruncateLength(length);
1842 mRects.TruncateLength(length);
1845 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1846 mRetainedWindowDraggingRegion.RemoveModifiedFramesAndRects();
1847 mRetainedWindowNoDraggingRegion.RemoveModifiedFramesAndRects();
1848 mRetainedWindowOpaqueRegion.RemoveModifiedFramesAndRects();
1851 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1852 mRetainedWindowDraggingRegion.Clear();
1853 mRetainedWindowNoDraggingRegion.Clear();
1854 mRetainedWindowOpaqueRegion.Clear();
1857 const uint32_t gWillChangeAreaMultiplier = 3;
1858 static uint32_t GetLayerizationCost(const nsSize& aSize) {
1859 // There's significant overhead for each layer created from Gecko
1860 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1861 // Therefore we set a minimum cost threshold of a 64x64 area.
1862 const int minBudgetCost = 64 * 64;
1864 const uint32_t budgetCost = std::max(
1865 minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
1866 nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
1868 return budgetCost;
1871 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
1872 const nsSize& aSize) {
1873 MOZ_ASSERT(IsForPainting());
1875 if (aFrame->MayHaveWillChangeBudget()) {
1876 // The frame is already in the will-change budget.
1877 return true;
1880 const nsPresContext* presContext = aFrame->PresContext();
1881 const nsRect area = presContext->GetVisibleArea();
1882 const uint32_t budgetLimit =
1883 nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1884 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1885 const uint32_t cost = GetLayerizationCost(aSize);
1887 DocumentWillChangeBudget& documentBudget =
1888 mDocumentWillChangeBudgets.LookupOrInsert(presContext);
1890 const bool onBudget =
1891 (documentBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
1893 if (onBudget) {
1894 documentBudget += cost;
1895 mFrameWillChangeBudgets.InsertOrUpdate(
1896 aFrame, FrameWillChangeBudget(presContext, cost));
1897 aFrame->SetMayHaveWillChangeBudget(true);
1900 return onBudget;
1903 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
1904 const nsSize& aSize) {
1905 if (!IsForPainting()) {
1906 // If this nsDisplayListBuilder is not for painting, the layerization should
1907 // not matter. Do the simple thing and return false.
1908 return false;
1911 const bool onBudget = AddToWillChangeBudget(aFrame, aSize);
1912 if (onBudget) {
1913 return true;
1916 auto* pc = aFrame->PresContext();
1917 auto* doc = pc->Document();
1918 if (!doc->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget)) {
1919 AutoTArray<nsString, 2> params;
1920 params.AppendElement()->AppendInt(gWillChangeAreaMultiplier);
1922 nsRect area = pc->GetVisibleArea();
1923 uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1924 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1925 params.AppendElement()->AppendInt(budgetLimit);
1927 doc->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget, false, params);
1930 return false;
1933 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame* aFrame) {
1934 MOZ_ASSERT(IsForPainting());
1936 if (!aFrame->MayHaveWillChangeBudget()) {
1937 return;
1940 aFrame->SetMayHaveWillChangeBudget(false);
1941 RemoveFromWillChangeBudgets(aFrame);
1944 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame* aFrame) {
1945 if (auto entry = mFrameWillChangeBudgets.Lookup(aFrame)) {
1946 const FrameWillChangeBudget& frameBudget = entry.Data();
1948 auto documentBudget =
1949 mDocumentWillChangeBudgets.Lookup(frameBudget.mPresContext);
1951 if (documentBudget) {
1952 *documentBudget -= frameBudget.mUsage;
1955 entry.Remove();
1959 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1960 mFrameWillChangeBudgets.Clear();
1961 mDocumentWillChangeBudgets.Clear();
1964 void nsDisplayListBuilder::EnterSVGEffectsContents(
1965 nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) {
1966 MOZ_ASSERT(aHoistedItemsStorage);
1967 if (mSVGEffectsFrames.IsEmpty()) {
1968 MOZ_ASSERT(!mScrollInfoItemsForHoisting);
1969 mScrollInfoItemsForHoisting = aHoistedItemsStorage;
1971 mSVGEffectsFrames.AppendElement(aEffectsFrame);
1974 void nsDisplayListBuilder::ExitSVGEffectsContents() {
1975 MOZ_ASSERT(!mSVGEffectsFrames.IsEmpty());
1976 mSVGEffectsFrames.RemoveLastElement();
1977 MOZ_ASSERT(mScrollInfoItemsForHoisting);
1978 if (mSVGEffectsFrames.IsEmpty()) {
1979 mScrollInfoItemsForHoisting = nullptr;
1983 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
1985 * Note: if changing the conditions under which scroll info layers
1986 * are created, make a corresponding change to
1987 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
1989 for (nsIFrame* frame : mSVGEffectsFrames) {
1990 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame)) {
1991 return true;
1994 return false;
1997 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
1998 nsDisplayScrollInfoLayer* aScrollInfoItem) {
1999 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2000 MOZ_ASSERT(mScrollInfoItemsForHoisting);
2001 mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
2004 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2005 nsIFrame* aFrame, nsDisplayList* aList) {
2006 MOZ_ASSERT(aFrame);
2007 MOZ_ASSERT(aList);
2009 if (!BuildCompositorHitTestInfo()) {
2010 return;
2013 const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
2014 if (info != CompositorHitTestInvisibleToHit) {
2015 aList->AppendNewToTop<nsDisplayCompositorHitTestInfo>(this, aFrame);
2019 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem* aItem) {
2020 mReuseableItems.Insert(aItem);
2023 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem* aItem) {
2024 MOZ_ASSERT(aItem->IsReusedItem());
2025 mReuseableItems.Remove(aItem);
2028 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2029 const size_t total = mReuseableItems.Count();
2031 size_t reused = 0;
2032 for (auto* item : mReuseableItems) {
2033 if (item->IsReusedItem()) {
2034 reused++;
2035 item->SetReusable();
2036 } else {
2037 item->Destroy(this);
2041 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
2042 mReuseableItems.Clear();
2045 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
2046 const auto* previous = mCurrentContainerASR;
2047 const auto* asr = aItem->GetActiveScrolledRoot();
2048 mCurrentContainerASR =
2049 ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
2051 if (previous != mCurrentContainerASR) {
2052 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
2053 mCurrentContainerASR);
2056 aItem->SetReusedItem();
2059 void nsDisplayListSet::CopyTo(const nsDisplayListSet& aDestination) const {
2060 for (size_t i = 0; i < mLists.size(); ++i) {
2061 auto* from = mLists[i];
2062 auto* to = aDestination.mLists[i];
2064 from->CopyTo(to);
2068 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
2069 aDestination.BorderBackground()->AppendToTop(BorderBackground());
2070 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2071 aDestination.Floats()->AppendToTop(Floats());
2072 aDestination.Content()->AppendToTop(Content());
2073 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
2074 aDestination.Outlines()->AppendToTop(Outlines());
2077 nsRect nsDisplayList::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2078 nsRect bounds;
2079 for (nsDisplayItem* i : *this) {
2080 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
2082 return bounds;
2085 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR(
2086 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
2087 nsRect* aBuildingRect) const {
2088 nsRect bounds;
2089 for (nsDisplayItem* i : *this) {
2090 nsRect r = i->GetClippedBounds(aBuilder);
2091 if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
2092 if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) {
2093 r = clip.ref();
2096 if (aBuildingRect) {
2097 aBuildingRect->UnionRect(*aBuildingRect, i->GetBuildingRect());
2099 bounds.UnionRect(bounds, r);
2101 return bounds;
2104 nsRect nsDisplayList::GetBuildingRect() const {
2105 nsRect result;
2106 for (nsDisplayItem* i : *this) {
2107 result.UnionRect(result, i->GetBuildingRect());
2109 return result;
2112 WindowRenderer* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView** aView) {
2113 if (aView) {
2114 *aView = RootReferenceFrame()->GetView();
2116 if (RootReferenceFrame() !=
2117 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2118 return nullptr;
2120 nsIWidget* window = RootReferenceFrame()->GetNearestWidget();
2121 if (window) {
2122 return window->GetWindowRenderer();
2124 return nullptr;
2127 WebRenderLayerManager* nsDisplayListBuilder::GetWidgetLayerManager(
2128 nsView** aView) {
2129 WindowRenderer* renderer = GetWidgetWindowRenderer();
2130 if (renderer) {
2131 return renderer->AsWebRender();
2133 return nullptr;
2136 void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2137 int32_t aAppUnitsPerDevPixel) {
2138 FlattenedDisplayListIterator iter(aBuilder, this);
2139 while (iter.HasNext()) {
2140 nsPaintedDisplayItem* item = iter.GetNextItem()->AsPaintedDisplayItem();
2141 if (!item) {
2142 continue;
2145 nsRect visible = item->GetClippedBounds(aBuilder);
2146 visible = visible.Intersect(item->GetPaintRect(aBuilder, aCtx));
2147 if (visible.IsEmpty()) {
2148 continue;
2151 DisplayItemClip currentClip = item->GetClip();
2152 if (currentClip.HasClip()) {
2153 aCtx->Save();
2154 if (currentClip.IsRectClippedByRoundedCorner(visible)) {
2155 currentClip.ApplyTo(aCtx, aAppUnitsPerDevPixel);
2156 } else {
2157 currentClip.ApplyRectTo(aCtx, aAppUnitsPerDevPixel);
2160 aCtx->NewPath();
2162 item->Paint(aBuilder, aCtx);
2164 if (currentClip.HasClip()) {
2165 aCtx->Restore();
2171 * We paint by executing a layer manager transaction, constructing a
2172 * single layer representing the display list, and then making it the
2173 * root of the layer manager, drawing into the PaintedLayers.
2175 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2176 uint32_t aFlags,
2177 Maybe<double> aDisplayListBuildTime) {
2178 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS);
2180 RefPtr<WebRenderLayerManager> layerManager;
2181 WindowRenderer* renderer = nullptr;
2182 bool widgetTransaction = false;
2183 bool doBeginTransaction = true;
2184 nsView* view = nullptr;
2185 if (aFlags & PAINT_USE_WIDGET_LAYERS) {
2186 renderer = aBuilder->GetWidgetWindowRenderer(&view);
2187 if (renderer) {
2188 // The fallback renderer doesn't retain any content, so it's
2189 // not meaningful to use it when drawing to an external context.
2190 if (aCtx && renderer->AsFallback()) {
2191 MOZ_ASSERT(!(aFlags & PAINT_EXISTING_TRANSACTION));
2192 renderer = nullptr;
2193 } else {
2194 layerManager = renderer->AsWebRender();
2195 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
2196 widgetTransaction = true;
2201 nsIFrame* frame = aBuilder->RootReferenceFrame();
2202 nsPresContext* presContext = frame->PresContext();
2203 PresShell* presShell = presContext->PresShell();
2204 Document* document = presShell->GetDocument();
2206 ScopeExit g([&]() {
2207 #ifdef DEBUG
2208 MOZ_ASSERT(!layerManager || !layerManager->GetTarget());
2209 #endif
2211 // For layers-free mode, we check the invalidation state bits in the
2212 // EndTransaction. So we clear the invalidation state bits after
2213 // EndTransaction.
2214 if (widgetTransaction ||
2215 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2216 // but they still need the invalidation state bits cleared in order for
2217 // invalidation for CSS/SMIL animation to work properly.
2218 (document && document->IsBeingUsedAsImage())) {
2219 DL_LOGD("Clearing invalidation state bits");
2220 frame->ClearInvalidationStateBits();
2224 if (!renderer) {
2225 if (!aCtx) {
2226 NS_WARNING("Nowhere to paint into");
2227 return;
2229 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(false);
2230 Paint(aBuilder, aCtx, presContext->AppUnitsPerDevPixel());
2232 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2233 return;
2236 if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) {
2237 MOZ_ASSERT(layerManager);
2238 if (doBeginTransaction) {
2239 if (aCtx) {
2240 if (!layerManager->BeginTransactionWithTarget(aCtx, nsCString())) {
2241 return;
2243 } else {
2244 if (!layerManager->BeginTransaction(nsCString())) {
2245 return;
2250 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(true);
2251 layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
2253 bool sent = false;
2254 if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
2255 MOZ_ASSERT(!aCtx);
2256 sent = layerManager->EndEmptyTransaction();
2259 if (!sent) {
2260 auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
2262 nsIDocShell* docShell = presContext->GetDocShell();
2263 WrFiltersHolder wrFilters;
2264 gfx::Matrix5x4* colorMatrix =
2265 nsDocShell::Cast(docShell)->GetColorMatrix();
2266 if (colorMatrix) {
2267 wrFilters.filters.AppendElement(
2268 wr::FilterOp::ColorMatrix(colorMatrix->components));
2271 wrManager->EndTransactionWithoutLayer(this, aBuilder,
2272 std::move(wrFilters), nullptr,
2273 aDisplayListBuildTime.valueOr(0.0));
2276 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2277 if (presContext->RefreshDriver()->HasScheduleFlush()) {
2278 presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2279 frame->GetRect());
2282 return;
2285 FallbackRenderer* fallback = renderer->AsFallback();
2286 MOZ_ASSERT(fallback);
2288 if (doBeginTransaction) {
2289 MOZ_ASSERT(!aCtx);
2290 if (!fallback->BeginTransaction()) {
2291 return;
2295 bool temp = aBuilder->SetIsCompositingCheap(renderer->IsCompositingCheap());
2296 fallback->EndTransactionWithList(aBuilder, this,
2297 presContext->AppUnitsPerDevPixel(),
2298 WindowRenderer::END_DEFAULT);
2300 aBuilder->SetIsCompositingCheap(temp);
2303 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
2304 for (auto* item : TakeItems()) {
2305 item->Destroy(aBuilder);
2309 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) {
2310 return aFrame->Style()->PointerEvents() != StylePointerEvents::None;
2313 // A list of frames, and their z depth. Used for sorting
2314 // the results of hit testing.
2315 struct FramesWithDepth {
2316 explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {}
2318 bool operator<(const FramesWithDepth& aOther) const {
2319 if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
2320 // We want to sort so that the shallowest item (highest depth value) is
2321 // first
2322 return mDepth > aOther.mDepth;
2324 return false;
2326 bool operator==(const FramesWithDepth& aOther) const {
2327 return this == &aOther;
2330 float mDepth;
2331 nsTArray<nsIFrame*> mFrames;
2334 // Sort the frames by depth and then moves all the contained frames to the
2335 // destination
2336 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource,
2337 nsTArray<nsIFrame*>* aDest) {
2338 if (aSource.IsEmpty()) {
2339 return;
2341 aSource.StableSort();
2342 uint32_t length = aSource.Length();
2343 for (uint32_t i = 0; i < length; i++) {
2344 aDest->AppendElements(std::move(aSource[i].mFrames));
2346 aSource.Clear();
2349 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2350 nsDisplayItem::HitTestState* aState,
2351 nsTArray<nsIFrame*>* aOutFrames) const {
2352 nsDisplayItem* item;
2354 if (aState->mInPreserves3D) {
2355 // Collect leaves of the current 3D rendering context.
2356 for (nsDisplayItem* item : *this) {
2357 auto itemType = item->GetType();
2358 if (itemType != DisplayItemType::TYPE_TRANSFORM ||
2359 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2360 item->HitTest(aBuilder, aRect, aState, aOutFrames);
2361 } else {
2362 // One of leaves in the current 3D rendering context.
2363 aState->mItemBuffer.AppendElement(item);
2366 return;
2369 int32_t itemBufferStart = aState->mItemBuffer.Length();
2370 for (nsDisplayItem* item : *this) {
2371 aState->mItemBuffer.AppendElement(item);
2374 AutoTArray<FramesWithDepth, 16> temp;
2375 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart;
2376 --i) {
2377 // Pop element off the end of the buffer. We want to shorten the buffer
2378 // so that recursive calls to HitTest have more buffer space.
2379 item = aState->mItemBuffer[i];
2380 aState->mItemBuffer.SetLength(i);
2382 bool snap;
2383 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
2384 auto itemType = item->GetType();
2385 bool same3DContext =
2386 (itemType == DisplayItemType::TYPE_TRANSFORM &&
2387 static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
2388 (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
2389 item->Frame()->Extend3DContext());
2390 if (same3DContext &&
2391 (itemType != DisplayItemType::TYPE_TRANSFORM ||
2392 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
2393 if (!item->GetClip().MayIntersect(aRect)) {
2394 continue;
2396 AutoTArray<nsIFrame*, 1> neverUsed;
2397 // Start gathering leaves of the 3D rendering context, and
2398 // append leaves at the end of mItemBuffer. Leaves are
2399 // processed at following iterations.
2400 aState->mInPreserves3D = true;
2401 item->HitTest(aBuilder, aRect, aState, &neverUsed);
2402 aState->mInPreserves3D = false;
2403 i = aState->mItemBuffer.Length();
2404 continue;
2406 if (same3DContext || item->GetClip().MayIntersect(r)) {
2407 AutoTArray<nsIFrame*, 16> outFrames;
2408 item->HitTest(aBuilder, aRect, aState, &outFrames);
2410 // For 3d transforms with preserve-3d we add hit frames into the temp list
2411 // so we can sort them later, otherwise we add them directly to the output
2412 // list.
2413 nsTArray<nsIFrame*>* writeFrames = aOutFrames;
2414 if (item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
2415 static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2416 if (outFrames.Length()) {
2417 nsDisplayTransform* transform =
2418 static_cast<nsDisplayTransform*>(item);
2419 nsPoint point = aRect.TopLeft();
2420 // A 1x1 rect means a point, otherwise use the center of the rect
2421 if (aRect.width != 1 || aRect.height != 1) {
2422 point = aRect.Center();
2424 temp.AppendElement(
2425 FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
2426 writeFrames = &temp[temp.Length() - 1].mFrames;
2428 } else {
2429 // We may have just finished a run of consecutive preserve-3d
2430 // transforms, so flush these into the destination array before
2431 // processing our frame list.
2432 FlushFramesArray(temp, aOutFrames);
2435 for (uint32_t j = 0; j < outFrames.Length(); j++) {
2436 nsIFrame* f = outFrames.ElementAt(j);
2437 // Filter out some frames depending on the type of hittest
2438 // we are doing. For visibility tests, pass through all frames.
2439 // For pointer tests, only pass through frames that are styled
2440 // to receive pointer events.
2441 if (aBuilder->HitTestIsForVisibility() ||
2442 IsFrameReceivingPointerEvents(f)) {
2443 writeFrames->AppendElement(f);
2447 if (aBuilder->HitTestIsForVisibility()) {
2448 aState->mHitOccludingItem = [&] {
2449 if (aState->mHitOccludingItem) {
2450 // We already hit something before.
2451 return true;
2453 if (aState->mCurrentOpacity == 1.0f &&
2454 item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) {
2455 // An opaque item always occludes everything. Note that we need to
2456 // check wrapping opacity and such as well.
2457 return true;
2459 float threshold = aBuilder->VisibilityThreshold();
2460 if (threshold == 1.0f) {
2461 return false;
2463 float itemOpacity = [&] {
2464 switch (item->GetType()) {
2465 case DisplayItemType::TYPE_OPACITY:
2466 return static_cast<nsDisplayOpacity*>(item)->GetOpacity();
2467 case DisplayItemType::TYPE_BACKGROUND_COLOR:
2468 return static_cast<nsDisplayBackgroundColor*>(item)
2469 ->GetOpacity();
2470 default:
2471 // Be conservative and assume it won't occlude other items.
2472 return 0.0f;
2474 }();
2475 return itemOpacity * aState->mCurrentOpacity >= threshold;
2476 }();
2478 if (aState->mHitOccludingItem) {
2479 // We're exiting early, so pop the remaining items off the buffer.
2480 aState->mItemBuffer.TruncateLength(itemBufferStart);
2481 break;
2486 // Clear any remaining preserve-3d transforms.
2487 FlushFramesArray(temp, aOutFrames);
2488 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
2489 "How did we forget to pop some elements?");
2492 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, Document* aDoc) {
2493 nsIFrame* f = aItem->Frame();
2494 while (f) {
2495 nsPresContext* pc = f->PresContext();
2496 if (pc->Document() == aDoc) {
2497 return f->GetContent();
2499 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(
2500 pc->PresShell()->GetRootFrame());
2502 return nullptr;
2505 struct ZSortItem {
2506 nsDisplayItem* item;
2507 int32_t zIndex;
2509 explicit ZSortItem(nsDisplayItem* aItem)
2510 : item(aItem), zIndex(aItem->ZIndex()) {}
2512 operator nsDisplayItem*() { return item; }
2515 struct ZOrderComparator {
2516 bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const {
2517 // Note that we can't just take the difference of the two
2518 // z-indices here, because that might overflow a 32-bit int.
2519 return aLeft.zIndex < aRight.zIndex;
2523 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); }
2525 struct ContentComparator {
2526 nsIContent* mCommonAncestor;
2528 explicit ContentComparator(nsIContent* aCommonAncestor)
2529 : mCommonAncestor(aCommonAncestor) {}
2531 bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const {
2532 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2533 // subdocument of commonAncestor, because display items for subdocuments
2534 // have been mixed into the same list. Ensure that we're looking at content
2535 // in commonAncestor's document.
2536 Document* commonAncestorDoc = mCommonAncestor->OwnerDoc();
2537 nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc);
2538 nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc);
2539 if (!content1 || !content2) {
2540 NS_ERROR("Document trees are mixed up!");
2541 // Something weird going on
2542 return true;
2544 return nsContentUtils::CompareTreePosition<TreeKind::Flat>(
2545 content1, content2, mCommonAncestor) < 0;
2549 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) {
2550 Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor));
2553 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2554 static_assert(sizeof(nsDisplayItem) <= 176, "nsDisplayItem has grown");
2555 #endif
2557 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2558 : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
2560 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2561 const ActiveScrolledRoot* aActiveScrolledRoot)
2562 : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) {
2563 MOZ_COUNT_CTOR(nsDisplayItem);
2564 MOZ_ASSERT(mFrame);
2565 if (aBuilder->IsRetainingDisplayList()) {
2566 mFrame->AddDisplayItem(this);
2569 aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
2570 NS_ASSERTION(
2571 aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
2572 "visible rect not set");
2574 mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2576 // The visible rect is for mCurrentFrame, so we have to use
2577 // mCurrentOffsetToReferenceFrame
2578 nsRect visible = aBuilder->GetVisibleRect() +
2579 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
2580 SetBuildingRect(visible);
2582 const nsStyleDisplay* disp = mFrame->StyleDisplay();
2583 if (mFrame->BackfaceIsHidden(disp)) {
2584 mItemFlags += ItemFlag::BackfaceHidden;
2586 if (mFrame->Combines3DTransformWithAncestors()) {
2587 mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
2591 void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
2593 bool nsDisplayItem::HasDeletedFrame() const {
2594 bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) ||
2595 (GetType() == DisplayItemType::TYPE_REMOTE &&
2596 !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader());
2597 MOZ_ASSERT(retval || mFrame);
2598 return retval;
2601 /* static */
2602 bool nsDisplayItem::ForceActiveLayers() {
2603 return StaticPrefs::layers_force_active();
2606 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); }
2608 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
2609 bool aStore) {
2610 mClipChain = aClipChain;
2613 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
2614 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2615 if (const DisplayItemClip* clip =
2616 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
2617 return Some(clip->GetClipRect());
2619 #ifdef DEBUG
2620 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
2621 #endif
2622 return Nothing();
2625 const DisplayItemClip& nsDisplayItem::GetClip() const {
2626 const DisplayItemClip* clip =
2627 DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot);
2628 return clip ? *clip : DisplayItemClip::NoClip();
2631 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
2632 const DisplayItemClipChain* aOther,
2633 bool aStore) {
2634 if (!aOther || mClipChain == aOther) {
2635 return;
2638 // aOther might be a reference to a clip on the stack. We need to make sure
2639 // that CreateClipChainIntersection will allocate the actual intersected
2640 // clip in the builder's arena, so for the mClipChain == nullptr case,
2641 // we supply nullptr as the common ancestor so that
2642 // CreateClipChainIntersection clones the whole chain.
2643 const DisplayItemClipChain* ancestorClip =
2644 mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther)
2645 : nullptr;
2647 SetClipChain(
2648 aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther),
2649 aStore);
2652 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2653 bool snap;
2654 nsRect r = GetBounds(aBuilder, &snap);
2655 return GetClip().ApplyNonRoundedIntersection(r);
2658 nsDisplayContainer::nsDisplayContainer(
2659 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2660 const ActiveScrolledRoot* aActiveScrolledRoot, nsDisplayList* aList)
2661 : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
2662 mChildren(aBuilder) {
2663 MOZ_COUNT_CTOR(nsDisplayContainer);
2664 mChildren.AppendToTop(aList);
2665 UpdateBounds(aBuilder);
2667 // Clear and store the clip chain set by nsDisplayItem constructor.
2668 nsDisplayItem::SetClipChain(nullptr, true);
2671 nsRect nsDisplayItem::GetPaintRect(nsDisplayListBuilder* aBuilder,
2672 gfxContext* aCtx) {
2673 bool dummy;
2674 nsRect result = GetBounds(aBuilder, &dummy);
2675 if (aCtx) {
2676 result.IntersectRect(result,
2677 nsLayoutUtils::RoundGfxRectToAppRect(
2678 aCtx->GetClipExtents(),
2679 mFrame->PresContext()->AppUnitsPerDevPixel()));
2681 return result;
2684 bool nsDisplayContainer::CreateWebRenderCommands(
2685 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2686 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2687 nsDisplayListBuilder* aDisplayListBuilder) {
2688 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2689 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
2690 false);
2691 return true;
2694 nsRect nsDisplayContainer::GetBounds(nsDisplayListBuilder* aBuilder,
2695 bool* aSnap) const {
2696 *aSnap = false;
2697 return mBounds;
2700 nsRect nsDisplayContainer::GetComponentAlphaBounds(
2701 nsDisplayListBuilder* aBuilder) const {
2702 return mChildren.GetComponentAlphaBounds(aBuilder);
2705 static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2706 nsDisplayList* aList,
2707 const nsRect& aListBounds) {
2708 return aList->GetOpaqueRegion(aBuilder);
2711 nsRegion nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2712 bool* aSnap) const {
2713 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
2714 GetBounds(aBuilder, aSnap));
2717 Maybe<nsRect> nsDisplayContainer::GetClipWithRespectToASR(
2718 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2719 // Our children should have finite bounds with respect to |aASR|.
2720 if (aASR == mActiveScrolledRoot) {
2721 return Some(mBounds);
2724 return Some(mChildren.GetClippedBoundsWithRespectToASR(aBuilder, aASR));
2727 void nsDisplayContainer::HitTest(nsDisplayListBuilder* aBuilder,
2728 const nsRect& aRect, HitTestState* aState,
2729 nsTArray<nsIFrame*>* aOutFrames) {
2730 mChildren.HitTest(aBuilder, aRect, aState, aOutFrames);
2733 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder* aBuilder) {
2734 // Container item bounds are expected to be clipped.
2735 mBounds =
2736 mChildren.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
2739 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder,
2740 bool* aSnap) const {
2741 *aSnap = true;
2742 return mBounds;
2745 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
2746 gfxContext* aCtx) {
2747 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2748 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2749 Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx),
2750 appUnitsPerDevPixel, *drawTarget);
2751 drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
2754 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) {
2755 aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
2756 << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
2757 << ")";
2760 bool nsDisplaySolidColor::CreateWebRenderCommands(
2761 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2762 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2763 nsDisplayListBuilder* aDisplayListBuilder) {
2764 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
2765 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
2766 wr::LayoutRect r = wr::ToLayoutRect(bounds);
2767 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, mIsCheckerboardBackground,
2768 wr::ToColorF(ToDeviceColor(mColor)));
2770 return true;
2773 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
2774 bool* aSnap) const {
2775 *aSnap = true;
2776 return mRegion.GetBounds();
2779 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
2780 gfxContext* aCtx) {
2781 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2782 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2783 ColorPattern color(ToDeviceColor(mColor));
2784 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2785 Rect rect =
2786 NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
2787 drawTarget->FillRect(rect, color);
2791 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) {
2792 aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255)
2793 << "," << int(mColor.b * 255) << "," << mColor.a << ")";
2796 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2797 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2798 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2799 nsDisplayListBuilder* aDisplayListBuilder) {
2800 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2801 nsRect rect = iter.Get();
2802 LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
2803 rect, mFrame->PresContext()->AppUnitsPerDevPixel());
2804 wr::LayoutRect r = wr::ToLayoutRect(layerRects);
2805 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
2806 wr::ToColorF(ToDeviceColor(mColor)));
2809 return true;
2812 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder,
2813 nsDisplayItem* aItem, nsIFrame* aFrame,
2814 nsITheme::ThemeGeometryType aType) {
2815 if (aBuilder->IsInChromeDocumentOrPopup()) {
2816 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2817 bool preservesAxisAlignedRectangles = false;
2818 nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(
2819 aFrame, aFrame->GetRectRelativeToSelf(), displayRoot,
2820 &preservesAxisAlignedRectangles);
2821 if (preservesAxisAlignedRectangles) {
2822 aBuilder->RegisterThemeGeometry(
2823 aType, aItem,
2824 LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels(
2825 aFrame->PresContext()->AppUnitsPerDevPixel())));
2830 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2831 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
2832 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
2833 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
2834 nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
2835 nsRect rootRect = rootFrame->GetRectRelativeToSelf();
2836 if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) ==
2837 nsLayoutUtils::TRANSFORM_SUCCEEDED) {
2838 return Some(rootRect + aBuilder->ToReferenceFrame(aFrame));
2840 return Nothing();
2843 /* static */ nsDisplayBackgroundImage::InitData
2844 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder,
2845 nsIFrame* aFrame, uint16_t aLayer,
2846 const nsRect& aBackgroundRect,
2847 const ComputedStyle* aBackgroundStyle) {
2848 nsPresContext* presContext = aFrame->PresContext();
2849 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2850 const nsStyleImageLayers::Layer& layer =
2851 aBackgroundStyle->StyleBackground()->mImage.mLayers[aLayer];
2853 bool isTransformedFixed;
2854 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
2855 presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer,
2856 &isTransformedFixed);
2858 // background-attachment:fixed is treated as background-attachment:scroll
2859 // if it's affected by a transform.
2860 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2861 bool shouldTreatAsFixed =
2862 layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2863 !isTransformedFixed;
2865 bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsNone();
2866 bool isRasterImage = state.mImageRenderer.IsRasterImage();
2867 nsCOMPtr<imgIContainer> image;
2868 if (isRasterImage) {
2869 image = state.mImageRenderer.GetImage();
2871 return InitData{aBuilder, aBackgroundStyle, image,
2872 aBackgroundRect, state.mFillArea, state.mDestArea,
2873 aLayer, isRasterImage, shouldFixToViewport};
2876 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2877 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aInitData,
2878 nsIFrame* aFrameForBounds)
2879 : nsPaintedDisplayItem(aBuilder, aFrame),
2880 mBackgroundStyle(aInitData.backgroundStyle),
2881 mImage(aInitData.image),
2882 mDependentFrame(nullptr),
2883 mBackgroundRect(aInitData.backgroundRect),
2884 mFillRect(aInitData.fillArea),
2885 mDestRect(aInitData.destArea),
2886 mLayer(aInitData.layer),
2887 mIsRasterImage(aInitData.isRasterImage),
2888 mShouldFixToViewport(aInitData.shouldFixToViewport) {
2889 MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
2890 #ifdef DEBUG
2891 if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) {
2892 // If this changes, then you also need to adjust css::ImageLoader to
2893 // invalidate mFrame as needed.
2894 MOZ_ASSERT(mFrame->IsCanvasFrame() || mFrame->IsTablePart());
2896 #endif
2898 mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
2899 if (mShouldFixToViewport) {
2900 // Expand the item's visible rect to cover the entire bounds, limited to the
2901 // viewport rect. This is necessary because the background's clip can move
2902 // asynchronously.
2903 if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
2904 aInitData.builder, mFrame)) {
2905 SetBuildingRect(mBounds.Intersect(*viewportRect));
2910 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2911 MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
2912 if (mDependentFrame) {
2913 mDependentFrame->RemoveDisplayItem(this);
2917 static void SetBackgroundClipRegion(
2918 DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame,
2919 const nsStyleImageLayers::Layer& aLayer, const nsRect& aBackgroundRect,
2920 bool aWillPaintBorder) {
2921 nsCSSRendering::ImageLayerClipState clip;
2922 nsCSSRendering::GetImageLayerClip(
2923 aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect,
2924 aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
2926 if (clip.mHasAdditionalBGClipArea) {
2927 aClipState.ClipContentDescendants(
2928 clip.mAdditionalBGClipArea, clip.mBGClipArea,
2929 clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2930 } else {
2931 aClipState.ClipContentDescendants(
2932 clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2937 * This is used for the find bar highlighter overlay. It's only accessible
2938 * through the AnonymousContent API, so it's not exposed to general web pages.
2940 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
2941 nsIFrame* aFrame,
2942 const nsRect& aBackgroundRect,
2943 nsDisplayList* aList, nscolor aColor) {
2944 nsIContent* content = aFrame->GetContent();
2945 if (!content) {
2946 return false;
2949 void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
2950 if (!cutoutRegion) {
2951 return false;
2954 if (NS_GET_A(aColor) == 0) {
2955 return true;
2958 nsRegion region;
2959 region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
2960 region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
2961 aList->AppendNewToTop<nsDisplaySolidColorRegion>(aBuilder, aFrame, region,
2962 aColor);
2964 return true;
2967 enum class TableType : uint8_t {
2968 Table,
2969 TableCol,
2970 TableColGroup,
2971 TableRow,
2972 TableRowGroup,
2973 TableCell,
2975 MAX,
2978 enum class TableTypeBits : uint8_t { Count = 3 };
2980 static_assert(static_cast<uint8_t>(TableType::MAX) <
2981 (1 << (static_cast<uint8_t>(TableTypeBits::Count) + 1)),
2982 "TableType cannot fit with TableTypeBits::Count");
2983 TableType GetTableTypeFromFrame(nsIFrame* aFrame);
2985 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex,
2986 const TableType aType) {
2987 const uint32_t key = (aIndex << static_cast<uint8_t>(TableTypeBits::Count)) |
2988 static_cast<uint8_t>(aType);
2990 return static_cast<uint16_t>(key);
2993 static nsDisplayBackgroundImage* CreateBackgroundImage(
2994 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
2995 const nsDisplayBackgroundImage::InitData& aBgData) {
2996 const auto index = aBgData.layer;
2998 if (aSecondaryFrame) {
2999 const auto tableType = GetTableTypeFromFrame(aFrame);
3000 const uint16_t tableItemIndex = CalculateTablePerFrameKey(index, tableType);
3002 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundImage>(
3003 aBuilder, aSecondaryFrame, tableItemIndex, aBgData, aFrame);
3006 return MakeDisplayItemWithIndex<nsDisplayBackgroundImage>(aBuilder, aFrame,
3007 index, aBgData);
3010 static nsDisplayThemedBackground* CreateThemedBackground(
3011 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3012 const nsRect& aBgRect) {
3013 if (aSecondaryFrame) {
3014 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3015 return MakeDisplayItemWithIndex<nsDisplayTableThemedBackground>(
3016 aBuilder, aSecondaryFrame, index, aBgRect, aFrame);
3019 return MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, aBgRect);
3022 static nsDisplayBackgroundColor* CreateBackgroundColor(
3023 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3024 nsRect& aBgRect, const ComputedStyle* aBgSC, nscolor aColor) {
3025 if (aSecondaryFrame) {
3026 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3027 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>(
3028 aBuilder, aSecondaryFrame, index, aBgRect, aBgSC, aColor, aFrame);
3031 return MakeDisplayItem<nsDisplayBackgroundColor>(aBuilder, aFrame, aBgRect,
3032 aBgSC, aColor);
3035 static void DealWithWindowsAppearanceHacks(nsIFrame* aFrame,
3036 nsDisplayListBuilder* aBuilder) {
3037 const auto& disp = *aFrame->StyleDisplay();
3039 // We use default appearance rather than effective appearance because we want
3040 // to handle when titlebar buttons that have appearance: none.
3041 const auto defaultAppearance = disp.mDefaultAppearance;
3042 if (MOZ_LIKELY(defaultAppearance == StyleAppearance::None)) {
3043 return;
3046 if (auto type = disp.GetWindowButtonType()) {
3047 if (auto* widget = aFrame->GetNearestWidget()) {
3048 auto rect = LayoutDevicePixel::FromAppUnitsToNearest(
3049 nsRect(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize()),
3050 aFrame->PresContext()->AppUnitsPerDevPixel());
3051 widget->SetWindowButtonRect(*type, rect);
3056 /*static*/
3057 AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3058 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3059 const nsRect& aBackgroundRect, nsDisplayList* aList,
3060 bool aAllowWillPaintBorderOptimization, const nsRect& aBackgroundOriginRect,
3061 nsIFrame* aSecondaryReferenceFrame,
3062 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
3063 aAutoBuildingDisplayList) {
3064 MOZ_ASSERT(!aFrame->IsCanvasFrame(),
3065 "We don't expect propagated canvas backgrounds here");
3066 #ifdef DEBUG
3068 nsIFrame* bgFrame = nsCSSRendering::FindBackgroundFrame(aFrame);
3069 MOZ_ASSERT(
3070 !bgFrame || bgFrame == aFrame,
3071 "Should only suppress backgrounds, never propagate to another frame");
3073 #endif
3075 DealWithWindowsAppearanceHacks(aFrame, aBuilder);
3077 const bool isThemed = aFrame->IsThemed();
3079 const ComputedStyle* bgSC = aFrame->Style();
3080 const nsStyleBackground* bg = bgSC->StyleBackground();
3081 const bool needsBackgroundColor =
3082 aBuilder->IsForEventDelivery() ||
3083 (EffectCompositor::HasAnimationsForCompositor(
3084 aFrame, DisplayItemType::TYPE_BACKGROUND_COLOR) &&
3085 !isThemed);
3086 if (!needsBackgroundColor && !isThemed && bg->IsTransparent(bgSC)) {
3087 return AppendedBackgroundType::None;
3090 bool drawBackgroundColor = false;
3091 bool drawBackgroundImage = false;
3092 nscolor color = NS_RGBA(0, 0, 0, 0);
3093 // Don't get background color / images if we propagated our background to the
3094 // canvas (that is, if FindBackgroundFrame is null). But don't early return
3095 // yet, since we might still need a background-color item for hit-testing.
3096 if (!isThemed && nsCSSRendering::FindBackgroundFrame(aFrame)) {
3097 color = nsCSSRendering::DetermineBackgroundColor(
3098 aFrame->PresContext(), bgSC, aFrame, drawBackgroundImage,
3099 drawBackgroundColor);
3102 if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
3103 color)) {
3104 return AppendedBackgroundType::None;
3107 const nsStyleBorder& border = *aFrame->StyleBorder();
3108 const bool willPaintBorder =
3109 aAllowWillPaintBorderOptimization && !isThemed &&
3110 !aFrame->StyleEffects()->HasBoxShadowWithInset(true) &&
3111 border.HasBorder();
3113 auto EnsureBuildingDisplayList = [&] {
3114 if (!aAutoBuildingDisplayList || *aAutoBuildingDisplayList) {
3115 return;
3117 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3118 aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3119 aBuilder->GetVisibleRect() + offset,
3120 aBuilder->GetDirtyRect() + offset);
3123 // An auxiliary list is necessary in case we have background blending; if that
3124 // is the case, background items need to be wrapped by a blend container to
3125 // isolate blending to the background
3126 nsDisplayList bgItemList(aBuilder);
3127 // Even if we don't actually have a background color to paint, we may still
3128 // need to create an item for hit testing and we still need to create an item
3129 // for background-color animations.
3130 if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) ||
3131 needsBackgroundColor) {
3132 EnsureBuildingDisplayList();
3133 Maybe<DisplayListClipState::AutoSaveRestore> clipState;
3134 nsRect bgColorRect = aBackgroundRect;
3135 if (!isThemed && !aBuilder->IsForEventDelivery()) {
3136 // Disable the will-paint-border optimization for background
3137 // colors with no border-radius. Enabling it for background colors
3138 // doesn't help much (there are no tiling issues) and clipping the
3139 // background breaks detection of the element's border-box being
3140 // opaque. For nonzero border-radius we still need it because we
3141 // want to inset the background if possible to avoid antialiasing
3142 // artifacts along the rounded corners.
3143 const bool useWillPaintBorderOptimization =
3144 willPaintBorder &&
3145 nsLayoutUtils::HasNonZeroCorner(border.mBorderRadius);
3147 nsCSSRendering::ImageLayerClipState clip;
3148 nsCSSRendering::GetImageLayerClip(
3149 bg->BottomLayer(), aFrame, border, aBackgroundRect, aBackgroundRect,
3150 useWillPaintBorderOptimization,
3151 aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3153 bgColorRect = bgColorRect.Intersect(clip.mBGClipArea);
3154 if (clip.mHasAdditionalBGClipArea) {
3155 bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea);
3157 if (clip.mHasRoundedCorners) {
3158 clipState.emplace(aBuilder);
3159 clipState->ClipContentDescendants(clip.mBGClipArea, clip.mRadii);
3163 nsDisplayBackgroundColor* bgItem = CreateBackgroundColor(
3164 aBuilder, aFrame, aSecondaryReferenceFrame, bgColorRect, bgSC,
3165 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0));
3167 if (bgItem) {
3168 bgItemList.AppendToTop(bgItem);
3172 if (isThemed) {
3173 nsDisplayThemedBackground* bgItem = CreateThemedBackground(
3174 aBuilder, aFrame, aSecondaryReferenceFrame, aBackgroundRect);
3176 if (bgItem) {
3177 bgItem->Init(aBuilder);
3178 bgItemList.AppendToTop(bgItem);
3181 if (!bgItemList.IsEmpty()) {
3182 aList->AppendToTop(&bgItemList);
3183 return AppendedBackgroundType::ThemedBackground;
3186 return AppendedBackgroundType::None;
3189 if (!drawBackgroundImage) {
3190 if (!bgItemList.IsEmpty()) {
3191 aList->AppendToTop(&bgItemList);
3192 return AppendedBackgroundType::Background;
3195 return AppendedBackgroundType::None;
3198 const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
3200 bool needBlendContainer = false;
3201 const nsRect& bgOriginRect =
3202 aBackgroundOriginRect.IsEmpty() ? aBackgroundRect : aBackgroundOriginRect;
3204 // Passing bg == nullptr in this macro will result in one iteration with
3205 // i = 0.
3206 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
3207 if (bg->mImage.mLayers[i].mImage.IsNone()) {
3208 continue;
3211 EnsureBuildingDisplayList();
3213 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3214 needBlendContainer = true;
3217 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3218 if (!aBuilder->IsForEventDelivery()) {
3219 const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
3220 SetBackgroundClipRegion(clipState, aFrame, layer, aBackgroundRect,
3221 willPaintBorder);
3224 nsDisplayList thisItemList(aBuilder);
3225 nsDisplayBackgroundImage::InitData bgData =
3226 nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect,
3227 bgSC);
3229 if (bgData.shouldFixToViewport) {
3230 auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
3231 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3232 aBuilder, aFrame, aBuilder->GetVisibleRect(),
3233 aBuilder->GetDirtyRect());
3235 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
3236 aBuilder);
3237 if (displayData) {
3238 asrSetter.SetCurrentActiveScrolledRoot(
3239 displayData->mContainingBlockActiveScrolledRoot);
3240 asrSetter.SetCurrentScrollParentId(displayData->mScrollParentId);
3241 if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
3242 // Override the dirty rect on the builder to be the dirty rect of
3243 // the viewport.
3244 // displayData->mDirtyRect is relative to the presshell's viewport
3245 // frame (the root frame), and we need it to be relative to aFrame.
3246 nsIFrame* rootFrame =
3247 aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
3248 // There cannot be any transforms between aFrame and rootFrame
3249 // because then bgData.shouldFixToViewport would have been false.
3250 nsRect visibleRect =
3251 displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame);
3252 aBuilder->SetVisibleRect(visibleRect);
3253 nsRect dirtyRect =
3254 displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
3255 aBuilder->SetDirtyRect(dirtyRect);
3259 nsDisplayBackgroundImage* bgItem = nullptr;
3261 // The clip is captured by the nsDisplayFixedPosition, so clear the
3262 // clip for the nsDisplayBackgroundImage inside.
3263 DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
3264 bgImageClip.Clear();
3265 bgItem = CreateBackgroundImage(aBuilder, aFrame,
3266 aSecondaryReferenceFrame, bgData);
3268 if (bgItem) {
3269 thisItemList.AppendToTop(
3270 nsDisplayFixedPosition::CreateForFixedBackground(
3271 aBuilder, aFrame, aSecondaryReferenceFrame, bgItem, i, asr));
3273 } else { // bgData.shouldFixToViewport == false
3274 nsDisplayBackgroundImage* bgItem = CreateBackgroundImage(
3275 aBuilder, aFrame, aSecondaryReferenceFrame, bgData);
3276 if (bgItem) {
3277 thisItemList.AppendToTop(bgItem);
3281 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3282 // asr is scrolled. Even if we wrap a fixed background layer, that's
3283 // fine, because the item will have a scrolled clip that limits the
3284 // item with respect to asr.
3285 if (aSecondaryReferenceFrame) {
3286 const auto tableType = GetTableTypeFromFrame(aFrame);
3287 const uint16_t index = CalculateTablePerFrameKey(i + 1, tableType);
3289 thisItemList.AppendNewToTopWithIndex<nsDisplayTableBlendMode>(
3290 aBuilder, aSecondaryReferenceFrame, index, &thisItemList,
3291 bg->mImage.mLayers[i].mBlendMode, asr, aFrame, true);
3292 } else {
3293 thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
3294 aBuilder, aFrame, i + 1, &thisItemList,
3295 bg->mImage.mLayers[i].mBlendMode, asr, true);
3298 bgItemList.AppendToTop(&thisItemList);
3301 if (needBlendContainer) {
3302 bgItemList.AppendToTop(
3303 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3304 aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr));
3307 if (!bgItemList.IsEmpty()) {
3308 aList->AppendToTop(&bgItemList);
3309 return AppendedBackgroundType::Background;
3312 return AppendedBackgroundType::None;
3315 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3316 // intersects aRect. Assumes that the unrounded border has already
3317 // been checked for intersection.
3318 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame,
3319 const nsPoint& aFrameToReferenceFrame,
3320 const nsRect& aTestRect) {
3321 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize())
3322 .Intersects(aTestRect)) {
3323 return false;
3326 nscoord radii[8];
3327 return !aFrame->GetBorderRadii(radii) ||
3328 nsLayoutUtils::RoundedRectIntersectsRect(
3329 nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii,
3330 aTestRect);
3333 // Returns TRUE if aContainedRect is guaranteed to be contained in
3334 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3335 // handled conservatively by returning FALSE in some situations where
3336 // a more thorough analysis could return TRUE.
3338 // See also RoundedRectIntersectsRect.
3339 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
3340 const nscoord aRadii[8],
3341 const nsRect& aContainedRect) {
3342 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii,
3343 aContainedRect);
3344 return rgn.Contains(aContainedRect);
3347 bool nsDisplayBackgroundImage::CanApplyOpacity(
3348 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3349 return CanBuildWebRenderDisplayItems(aManager, aBuilder);
3352 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3353 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3354 return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip !=
3355 StyleGeometryBox::Text &&
3356 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3357 aManager, *StyleFrame()->PresContext(), StyleFrame(),
3358 mBackgroundStyle->StyleBackground(), mLayer,
3359 aBuilder->GetBackgroundPaintFlags());
3362 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3363 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3364 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3365 nsDisplayListBuilder* aDisplayListBuilder) {
3366 if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(),
3367 aDisplayListBuilder)) {
3368 return false;
3371 uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
3372 bool dummy;
3373 nsCSSRendering::PaintBGParams params =
3374 nsCSSRendering::PaintBGParams::ForSingleLayer(
3375 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy),
3376 mBackgroundRect, StyleFrame(), paintFlags, mLayer,
3377 CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity());
3378 params.bgClipRect = &mBounds;
3379 ImgDrawResult result =
3380 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3381 params, aBuilder, aResources, aSc, aManager, this);
3382 if (result == ImgDrawResult::NOT_SUPPORTED) {
3383 return false;
3386 if (nsIContent* content = StyleFrame()->GetContent()) {
3387 if (imgRequestProxy* requestProxy = mBackgroundStyle->StyleBackground()
3388 ->mImage.mLayers[mLayer]
3389 .mImage.GetImageRequest()) {
3390 // LCP don't consider gradient backgrounds.
3391 LCPHelpers::FinalizeLCPEntryForImage(content->AsElement(), requestProxy,
3392 mBounds - ToReferenceFrame());
3396 return true;
3399 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
3400 const nsRect& aRect,
3401 HitTestState* aState,
3402 nsTArray<nsIFrame*>* aOutFrames) {
3403 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3404 aOutFrames->AppendElement(mFrame);
3408 static nsRect GetInsideClipRect(const nsDisplayItem* aItem,
3409 StyleGeometryBox aClip, const nsRect& aRect,
3410 const nsRect& aBackgroundRect) {
3411 if (aRect.IsEmpty()) {
3412 return {};
3415 nsIFrame* frame = aItem->Frame();
3417 nsRect clipRect = aBackgroundRect;
3418 if (frame->IsCanvasFrame()) {
3419 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3420 clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
3421 } else if (aClip == StyleGeometryBox::PaddingBox ||
3422 aClip == StyleGeometryBox::ContentBox) {
3423 nsMargin border = frame->GetUsedBorder();
3424 if (aClip == StyleGeometryBox::ContentBox) {
3425 border += frame->GetUsedPadding();
3427 border.ApplySkipSides(frame->GetSkipSides());
3428 clipRect.Deflate(border);
3431 return clipRect.Intersect(aRect);
3434 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
3435 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3436 nsRegion result;
3437 *aSnap = false;
3439 if (!mBackgroundStyle) {
3440 return result;
3443 *aSnap = true;
3445 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3446 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3447 // which expects frames to be sent to it in content order, not reverse
3448 // content order which we'll produce here.
3449 // Of course, if there's only one frame in the flow, it doesn't matter.
3450 if (mFrame->StyleBorder()->mBoxDecorationBreak ==
3451 StyleBoxDecorationBreak::Clone ||
3452 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
3453 const nsStyleImageLayers::Layer& layer =
3454 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3455 if (layer.mImage.IsOpaque() && layer.mBlendMode == StyleBlend::Normal &&
3456 layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
3457 layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
3458 layer.mClip != StyleGeometryBox::Text) {
3459 result = GetInsideClipRect(this, layer.mClip, mBounds, mBackgroundRect);
3463 return result;
3466 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform(
3467 nsDisplayListBuilder* aBuilder) const {
3468 if (!mBackgroundStyle) {
3469 return Some(NS_RGBA(0, 0, 0, 0));
3471 return Nothing();
3474 nsRect nsDisplayBackgroundImage::GetPositioningArea() const {
3475 if (!mBackgroundStyle) {
3476 return nsRect();
3478 nsIFrame* attachedToFrame;
3479 bool transformedFixed;
3480 return nsCSSRendering::ComputeImageLayerPositioningArea(
3481 mFrame->PresContext(), mFrame, mBackgroundRect,
3482 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer],
3483 &attachedToFrame, &transformedFixed) +
3484 ToReferenceFrame();
3487 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3488 const {
3489 if (!mBackgroundStyle) {
3490 return false;
3493 nscoord radii[8];
3494 if (mFrame->GetBorderRadii(radii)) {
3495 // A change in the size of the positioning area might change the position
3496 // of the rounded corners.
3497 return true;
3500 const nsStyleImageLayers::Layer& layer =
3501 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3502 return layer.RenderingMightDependOnPositioningAreaSizeChange();
3505 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
3506 gfxContext* aCtx) {
3507 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &mBounds);
3510 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
3511 gfxContext* aCtx,
3512 const nsRect& aBounds,
3513 nsRect* aClipRect) {
3514 gfxContext* ctx = aCtx;
3515 StyleGeometryBox clip =
3516 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip;
3518 if (clip == StyleGeometryBox::Text) {
3519 if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect,
3520 aBuilder)) {
3521 return;
3525 nsCSSRendering::PaintBGParams params =
3526 nsCSSRendering::PaintBGParams::ForSingleLayer(
3527 *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
3528 aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER,
3529 1.0f);
3530 params.bgClipRect = aClipRect;
3531 Unused << nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
3533 if (clip == StyleGeometryBox::Text) {
3534 ctx->PopGroupAndBlend();
3538 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3539 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3540 nsRegion* aInvalidRegion) const {
3541 if (!mBackgroundStyle) {
3542 return;
3545 const auto* geometry =
3546 static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
3548 bool snap;
3549 nsRect bounds = GetBounds(aBuilder, &snap);
3550 nsRect positioningArea = GetPositioningArea();
3551 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
3552 (positioningArea.Size() != geometry->mPositioningArea.Size() &&
3553 RenderingMightDependOnPositioningAreaSizeChange())) {
3554 // Positioning area changed in a way that could cause everything to change,
3555 // so invalidate everything (both old and new painting areas).
3556 aInvalidRegion->Or(bounds, geometry->mBounds);
3557 return;
3559 if (!mDestRect.IsEqualInterior(geometry->mDestRect)) {
3560 // Dest area changed in a way that could cause everything to change,
3561 // so invalidate everything (both old and new painting areas).
3562 aInvalidRegion->Or(bounds, geometry->mBounds);
3563 return;
3565 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3566 // Positioning area is unchanged, so invalidate just the change in the
3567 // painting area.
3568 aInvalidRegion->Xor(bounds, geometry->mBounds);
3572 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder,
3573 bool* aSnap) const {
3574 *aSnap = true;
3575 return mBounds;
3578 nsRect nsDisplayBackgroundImage::GetBoundsInternal(
3579 nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) {
3580 // This allows nsDisplayTableBackgroundImage to change the frame used for
3581 // bounds calculation.
3582 nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame;
3584 nsPresContext* presContext = frame->PresContext();
3586 if (!mBackgroundStyle) {
3587 return nsRect();
3590 nsRect clipRect = mBackgroundRect;
3591 if (frame->IsCanvasFrame()) {
3592 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3593 clipRect = canvasFrame->CanvasArea() + ToReferenceFrame();
3595 const nsStyleImageLayers::Layer& layer =
3596 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3597 return nsCSSRendering::GetBackgroundLayerRect(
3598 presContext, frame, mBackgroundRect, clipRect, layer,
3599 aBuilder->GetBackgroundPaintFlags());
3602 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3603 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aData,
3604 nsIFrame* aCellFrame)
3605 : nsDisplayBackgroundImage(aBuilder, aFrame, aData, aCellFrame),
3606 mStyleFrame(aCellFrame) {
3607 if (aBuilder->IsRetainingDisplayList()) {
3608 mStyleFrame->AddDisplayItem(this);
3612 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3613 if (mStyleFrame) {
3614 mStyleFrame->RemoveDisplayItem(this);
3618 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const {
3619 bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
3620 aRect += ToReferenceFrame();
3621 return result;
3624 nsDisplayThemedBackground::nsDisplayThemedBackground(
3625 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3626 const nsRect& aBackgroundRect)
3627 : nsPaintedDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) {
3628 MOZ_COUNT_CTOR(nsDisplayThemedBackground);
3631 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) {
3632 const nsStyleDisplay* disp = StyleFrame()->StyleDisplay();
3633 mAppearance = disp->EffectiveAppearance();
3634 StyleFrame()->IsThemed(disp, &mThemeTransparency);
3636 // Perform necessary RegisterThemeGeometry
3637 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3638 nsITheme::ThemeGeometryType type =
3639 theme->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance);
3640 if (type != nsITheme::eThemeGeometryTypeUnknown) {
3641 RegisterThemeGeometry(aBuilder, this, StyleFrame(), type);
3644 mBounds = GetBoundsInternal();
3647 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) {
3648 aStream << " (themed, appearance:" << (int)mAppearance << ")";
3651 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
3652 const nsRect& aRect,
3653 HitTestState* aState,
3654 nsTArray<nsIFrame*>* aOutFrames) {
3655 // Assume that any point in our background rect is a hit.
3656 if (mBackgroundRect.Intersects(aRect)) {
3657 aOutFrames->AppendElement(mFrame);
3661 nsRegion nsDisplayThemedBackground::GetOpaqueRegion(
3662 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3663 nsRegion result;
3664 *aSnap = false;
3666 if (mThemeTransparency == nsITheme::eOpaque) {
3667 *aSnap = true;
3668 result = mBackgroundRect;
3670 return result;
3673 Maybe<nscolor> nsDisplayThemedBackground::IsUniform(
3674 nsDisplayListBuilder* aBuilder) const {
3675 return Nothing();
3678 nsRect nsDisplayThemedBackground::GetPositioningArea() const {
3679 return mBackgroundRect;
3682 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
3683 gfxContext* aCtx) {
3684 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), nullptr);
3687 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
3688 gfxContext* aCtx,
3689 const nsRect& aBounds,
3690 nsRect* aClipRect) {
3691 // XXXzw this ignores aClipRect.
3692 nsPresContext* presContext = StyleFrame()->PresContext();
3693 nsITheme* theme = presContext->Theme();
3694 nsRect drawing(mBackgroundRect);
3695 theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(),
3696 mAppearance, &drawing);
3697 drawing.IntersectRect(drawing, aBounds);
3698 theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect,
3699 drawing);
3702 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3703 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3704 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3705 nsDisplayListBuilder* aDisplayListBuilder) {
3706 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3707 return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc,
3708 aManager, StyleFrame(),
3709 mAppearance, mBackgroundRect);
3712 bool nsDisplayThemedBackground::IsWindowActive() const {
3713 return !mFrame->PresContext()->Document()->IsTopLevelWindowInactive();
3716 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3717 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3718 nsRegion* aInvalidRegion) const {
3719 const auto* geometry =
3720 static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
3722 bool snap;
3723 nsRect bounds = GetBounds(aBuilder, &snap);
3724 nsRect positioningArea = GetPositioningArea();
3725 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
3726 // Invalidate everything (both old and new painting areas).
3727 aInvalidRegion->Or(bounds, geometry->mBounds);
3728 return;
3730 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3731 // Positioning area is unchanged, so invalidate just the change in the
3732 // painting area.
3733 aInvalidRegion->Xor(bounds, geometry->mBounds);
3735 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3736 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
3737 IsWindowActive() != geometry->mWindowIsActive) {
3738 aInvalidRegion->Or(*aInvalidRegion, bounds);
3742 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder,
3743 bool* aSnap) const {
3744 *aSnap = true;
3745 return mBounds;
3748 nsRect nsDisplayThemedBackground::GetBoundsInternal() {
3749 nsPresContext* presContext = mFrame->PresContext();
3751 nsRect r = mBackgroundRect - ToReferenceFrame();
3752 presContext->Theme()->GetWidgetOverflow(
3753 presContext->DeviceContext(), mFrame,
3754 mFrame->StyleDisplay()->EffectiveAppearance(), &r);
3755 return r + ToReferenceFrame();
3758 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
3759 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder,
3760 gfxContext* aCtx) {
3761 mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(),
3762 mFrame, ToReferenceFrame(), mColor);
3764 #endif
3766 bool nsDisplayBackgroundColor::CanApplyOpacity(
3767 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3768 // Don't apply opacity if the background color is animated since the color is
3769 // going to be changed on the compositor.
3770 return !EffectCompositor::HasAnimationsForCompositor(
3771 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR);
3774 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3775 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3776 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3777 nsDisplayListBuilder* aDisplayListBuilder) {
3778 gfx::sRGBColor color = mColor;
3779 color.a *= aBuilder.GetInheritedOpacity();
3781 if (color == sRGBColor() &&
3782 !EffectCompositor::HasAnimationsForCompositor(
3783 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3784 return true;
3787 if (HasBackgroundClipText()) {
3788 return false;
3791 uint64_t animationsId = 0;
3792 // We don't support background-color animations on table elements yet.
3793 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
3794 animationsId =
3795 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
3798 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
3799 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3800 wr::LayoutRect r = wr::ToLayoutRect(bounds);
3802 if (animationsId) {
3803 wr::WrAnimationProperty prop{
3804 wr::WrAnimationType::BackgroundColor,
3805 animationsId,
3807 aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(),
3808 wr::ToColorF(ToDeviceColor(color)), &prop);
3809 } else {
3810 aBuilder.StartGroup(this);
3811 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
3812 wr::ToColorF(ToDeviceColor(color)));
3813 aBuilder.FinishGroup();
3816 return true;
3819 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder,
3820 gfxContext* aCtx,
3821 const DisplayItemClip& aClip) {
3822 MOZ_ASSERT(!HasBackgroundClipText());
3824 if (mColor == sRGBColor()) {
3825 return;
3828 nsRect fillRect = mBackgroundRect;
3829 if (aClip.HasClip()) {
3830 fillRect.IntersectRect(fillRect, aClip.GetClipRect());
3833 DrawTarget* dt = aCtx->GetDrawTarget();
3834 int32_t A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
3835 Rect bounds = ToRect(nsLayoutUtils::RectToGfxRect(fillRect, A2D));
3836 MaybeSnapToDevicePixels(bounds, *dt);
3837 ColorPattern fill(ToDeviceColor(mColor));
3839 if (aClip.GetRoundedRectCount()) {
3840 MOZ_ASSERT(aClip.GetRoundedRectCount() == 1);
3842 AutoTArray<DisplayItemClip::RoundedRect, 1> roundedRect;
3843 aClip.AppendRoundedRects(&roundedRect);
3845 bool pushedClip = false;
3846 if (!fillRect.Contains(roundedRect[0].mRect)) {
3847 dt->PushClipRect(bounds);
3848 pushedClip = true;
3851 RectCornerRadii pixelRadii;
3852 nsCSSRendering::ComputePixelRadii(roundedRect[0].mRadii, A2D, &pixelRadii);
3853 dt->FillRoundedRect(
3854 RoundedRect(NSRectToSnappedRect(roundedRect[0].mRect, A2D, *dt),
3855 pixelRadii),
3856 fill);
3857 if (pushedClip) {
3858 dt->PopClip();
3860 } else {
3861 dt->FillRect(bounds, fill);
3865 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
3866 gfxContext* aCtx) {
3867 if (mColor == sRGBColor()) {
3868 return;
3871 #if 0
3872 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3873 // results in a precision induced rounding issue that makes the rect one
3874 // pixel shorter in rare cases. Disabled in favor of the old code for now.
3875 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3876 // reproduce the bug.
3878 // TODO:
3879 // This new path does not include support for background-clip:text; need to
3880 // be fixed if/when we switch to this new code path.
3882 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
3884 Rect rect = NSRectToSnappedRect(mBackgroundRect,
3885 mFrame->PresContext()->AppUnitsPerDevPixel(),
3886 aDrawTarget);
3887 ColorPattern color(ToDeviceColor(mColor));
3888 aDrawTarget.FillRect(rect, color);
3889 #else
3890 gfxContext* ctx = aCtx;
3891 gfxRect bounds = nsLayoutUtils::RectToGfxRect(
3892 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3894 if (HasBackgroundClipText()) {
3895 if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
3896 return;
3899 ctx->SetColor(mColor);
3900 ctx->NewPath();
3901 ctx->SnappedRectangle(bounds);
3902 ctx->Fill();
3903 ctx->PopGroupAndBlend();
3904 return;
3907 ctx->SetColor(mColor);
3908 ctx->NewPath();
3909 ctx->SnappedRectangle(bounds);
3910 ctx->Fill();
3911 #endif
3914 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion(
3915 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3916 *aSnap = false;
3918 if (mColor.a != 1 ||
3919 // Even if the current alpha channel is 1, we treat this item as if it's
3920 // non-opaque if there is a background-color animation since the animation
3921 // might change the alpha channel.
3922 EffectCompositor::HasAnimationsForCompositor(
3923 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3924 return nsRegion();
3927 if (!mHasStyle || HasBackgroundClipText()) {
3928 return nsRegion();
3931 *aSnap = true;
3932 return GetInsideClipRect(this, mBottomLayerClip, mBackgroundRect,
3933 mBackgroundRect);
3936 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform(
3937 nsDisplayListBuilder* aBuilder) const {
3938 return Some(mColor.ToABGR());
3941 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
3942 const nsRect& aRect,
3943 HitTestState* aState,
3944 nsTArray<nsIFrame*>* aOutFrames) {
3945 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3946 // aRect doesn't intersect our border-radius curve.
3947 return;
3950 aOutFrames->AppendElement(mFrame);
3953 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) {
3954 aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << ","
3955 << mColor.a << ")";
3956 aStream << " backgroundRect" << mBackgroundRect;
3959 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
3960 bool* aSnap) const {
3961 *aSnap = false;
3962 return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
3965 nsRect nsDisplayOutline::GetInnerRect() const {
3966 if (nsRect* savedOutlineInnerRect =
3967 mFrame->GetProperty(nsIFrame::OutlineInnerRectProperty())) {
3968 return *savedOutlineInnerRect;
3970 return mFrame->GetRectRelativeToSelf();
3973 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
3974 // TODO join outlines together
3975 MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(),
3976 "Should have not created a nsDisplayOutline!");
3978 nsRect rect = GetInnerRect() + ToReferenceFrame();
3979 nsPresContext* pc = mFrame->PresContext();
3980 if (IsThemedOutline()) {
3981 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
3982 pc->Theme()->DrawWidgetBackground(aCtx, mFrame,
3983 StyleAppearance::FocusOutline, rect,
3984 GetPaintRect(aBuilder, aCtx));
3985 return;
3988 nsCSSRendering::PaintNonThemedOutline(
3989 pc, *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), rect, mFrame->Style());
3992 bool nsDisplayOutline::IsThemedOutline() const {
3993 #ifdef DEBUG
3994 nsPresContext* pc = mFrame->PresContext();
3995 MOZ_ASSERT(
3996 pc->Theme()->ThemeSupportsWidget(pc, mFrame,
3997 StyleAppearance::FocusOutline),
3998 "All of our supported platforms have support for themed focus-outlines");
3999 #endif
4000 return mFrame->StyleOutline()->mOutlineStyle.IsAuto();
4003 bool nsDisplayOutline::CreateWebRenderCommands(
4004 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4005 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4006 nsDisplayListBuilder* aDisplayListBuilder) {
4007 nsPresContext* pc = mFrame->PresContext();
4008 nsRect rect = GetInnerRect() + ToReferenceFrame();
4009 if (IsThemedOutline()) {
4010 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
4011 return pc->Theme()->CreateWebRenderCommandsForWidget(
4012 aBuilder, aResources, aSc, aManager, mFrame,
4013 StyleAppearance::FocusOutline, rect);
4016 bool dummy;
4017 Maybe<nsCSSBorderRenderer> borderRenderer =
4018 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4019 pc, /* aDrawTarget = */ nullptr, mFrame,
4020 GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style());
4022 if (!borderRenderer) {
4023 // No border renderer means "there is no outline".
4024 // Paint nothing and return success.
4025 return true;
4028 borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
4029 return true;
4032 bool nsDisplayOutline::HasRadius() const {
4033 const auto& radius = mFrame->StyleBorder()->mBorderRadius;
4034 return !nsLayoutUtils::HasNonZeroCorner(radius);
4037 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const {
4038 const nsStyleOutline* outline = mFrame->StyleOutline();
4039 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
4040 if (borderBox.Contains(aRect) && !HasRadius() &&
4041 outline->mOutlineOffset.ToCSSPixels() >= 0.0f) {
4042 // aRect is entirely inside the border-rect, and the outline isn't rendered
4043 // inside the border-rect, so the outline is not visible.
4044 return true;
4046 return false;
4049 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
4050 const nsRect& aRect, HitTestState* aState,
4051 nsTArray<nsIFrame*>* aOutFrames) {
4052 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4053 // aRect doesn't intersect our border-radius curve.
4054 return;
4057 aOutFrames->AppendElement(mFrame);
4060 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4061 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4062 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4063 nsDisplayListBuilder* aDisplayListBuilder) {
4064 return true;
4067 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4068 return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
4071 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) {
4072 mOverrideZIndex = Some(aZIndex);
4075 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
4076 nsIFrame* aCaretFrame)
4077 : nsPaintedDisplayItem(aBuilder, aCaretFrame),
4078 mCaret(aBuilder->GetCaret()),
4079 mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) {
4080 MOZ_COUNT_CTOR(nsDisplayCaret);
4081 // The presence of a caret doesn't change the overflow rect
4082 // of the owning frame, so the normal building rect might not
4083 // include the caret at all. We use MarkFrameForDisplay to ensure
4084 // we build this item, and here we override the building rect
4085 // to cover the pixels we're going to draw.
4086 SetBuildingRect(mBounds);
4089 #ifdef NS_BUILD_REFCNT_LOGGING
4090 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret); }
4091 #endif
4093 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder,
4094 bool* aSnap) const {
4095 *aSnap = true;
4096 // The caret returns a rect in the coordinates of mFrame.
4097 return mBounds;
4100 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4101 // Note: Because we exist, we know that the caret is visible, so we don't
4102 // need to check for the caret's visibility.
4103 mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
4106 bool nsDisplayCaret::CreateWebRenderCommands(
4107 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4108 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4109 nsDisplayListBuilder* aDisplayListBuilder) {
4110 using namespace layers;
4111 nsRect caretRect;
4112 nsRect hookRect;
4113 nscolor caretColor;
4114 nsIFrame* frame =
4115 mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor);
4116 if (NS_WARN_IF(!frame) || NS_WARN_IF(frame != mFrame)) {
4117 NS_ASSERTION(false, "Caret invalidation bug");
4118 return true;
4121 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
4122 gfx::DeviceColor color = ToDeviceColor(caretColor);
4123 LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
4124 caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
4125 LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
4126 hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
4128 wr::LayoutRect caret = wr::ToLayoutRect(devCaretRect);
4129 wr::LayoutRect hook = wr::ToLayoutRect(devHookRect);
4131 // Note, WR will pixel snap anything that is layout aligned.
4132 aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), false, false,
4133 wr::ToColorF(color));
4135 if (!devHookRect.IsEmpty()) {
4136 aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), false, false,
4137 wr::ToColorF(color));
4139 return true;
4142 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder,
4143 nsIFrame* aFrame)
4144 : nsPaintedDisplayItem(aBuilder, aFrame) {
4145 MOZ_COUNT_CTOR(nsDisplayBorder);
4147 mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
4150 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const {
4151 nsRect paddingRect = GetPaddingRect();
4152 const nsStyleBorder* styleBorder;
4153 if (paddingRect.Contains(aRect) &&
4154 !(styleBorder = mFrame->StyleBorder())->IsBorderImageSizeAvailable() &&
4155 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
4156 // aRect is entirely inside the content rect, and no part
4157 // of the border is rendered inside the content rect, so we are not
4158 // visible
4159 // Skip this if there's a border-image (which draws a background
4160 // too) or if there is a border-radius (which makes the border draw
4161 // further in).
4162 return true;
4165 return false;
4168 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry(
4169 nsDisplayListBuilder* aBuilder) {
4170 return new nsDisplayBorderGeometry(this, aBuilder);
4173 void nsDisplayBorder::ComputeInvalidationRegion(
4174 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4175 nsRegion* aInvalidRegion) const {
4176 const auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
4177 bool snap;
4179 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
4180 // We can probably get away with only invalidating the difference
4181 // between the border and padding rects, but the XUL ui at least
4182 // is apparently painting a background with this?
4183 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4187 bool nsDisplayBorder::CreateWebRenderCommands(
4188 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4189 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4190 nsDisplayListBuilder* aDisplayListBuilder) {
4191 nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
4193 ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder(
4194 this, mFrame, rect, aBuilder, aResources, aSc, aManager,
4195 aDisplayListBuilder);
4197 if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
4198 return false;
4200 return true;
4203 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4204 nsPoint offset = ToReferenceFrame();
4206 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
4207 ? PaintBorderFlags::SyncDecodeImages
4208 : PaintBorderFlags();
4210 Unused << nsCSSRendering::PaintBorder(
4211 mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
4212 nsRect(offset, mFrame->GetSize()), mFrame->Style(), flags,
4213 mFrame->GetSkipSides());
4216 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder,
4217 bool* aSnap) const {
4218 *aSnap = true;
4219 return mBounds;
4222 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
4223 gfxContext* aCtx) {
4224 nsPoint offset = ToReferenceFrame();
4225 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4226 nsPresContext* presContext = mFrame->PresContext();
4228 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
4230 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
4231 GetPaintRect(aBuilder, aCtx), 1.0f);
4234 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
4235 bool* aSnap) const {
4236 *aSnap = false;
4237 return mBounds;
4240 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() {
4241 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
4242 ToReferenceFrame();
4245 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const {
4246 nsPoint origin = ToReferenceFrame();
4247 nsRect frameRect(origin, mFrame->GetSize());
4248 if (!frameRect.Contains(aRect)) {
4249 return false;
4252 // the visible region is entirely inside the border-rect, and box shadows
4253 // never render within the border-rect (unless there's a border radius).
4254 nscoord twipsRadii[8];
4255 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
4256 if (!hasBorderRadii) {
4257 return true;
4260 return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
4263 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4264 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4265 if (shadows.IsEmpty()) {
4266 return false;
4269 bool hasBorderRadius;
4270 // We don't support native themed things yet like box shadows around
4271 // input buttons.
4273 // TODO(emilio): The non-native theme could provide the right rect+radius
4274 // instead relatively painlessly, if we find this causes performance issues or
4275 // what not.
4276 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4279 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4280 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4281 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4282 nsDisplayListBuilder* aDisplayListBuilder) {
4283 if (!CanBuildWebRenderDisplayItems()) {
4284 return false;
4287 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4288 nsPoint offset = ToReferenceFrame();
4289 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4290 bool snap;
4291 nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
4293 bool hasBorderRadius;
4294 bool nativeTheme =
4295 nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4297 // Don't need the full size of the shadow rect like we do in
4298 // nsCSSRendering since WR takes care of calculations for blur
4299 // and spread radius.
4300 nsRect frameRect =
4301 nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
4303 RectCornerRadii borderRadii;
4304 if (hasBorderRadius) {
4305 hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
4306 mFrame, borderRadii);
4309 // Everything here is in app units, change to device units.
4310 LayoutDeviceRect clipRect =
4311 LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel);
4312 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4313 MOZ_ASSERT(!shadows.IsEmpty());
4315 for (const auto& shadow : Reversed(shadows)) {
4316 if (shadow.inset) {
4317 continue;
4320 float blurRadius =
4321 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4322 gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor(
4323 shadow.base, mFrame, aBuilder.GetInheritedOpacity());
4325 // We don't move the shadow rect here since WR does it for us
4326 // Now translate everything to device pixels.
4327 const nsRect& shadowRect = frameRect;
4328 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4329 nsPoint(shadow.base.horizontal.ToAppUnits(),
4330 shadow.base.vertical.ToAppUnits()),
4331 appUnitsPerDevPixel);
4333 LayoutDeviceRect deviceBox =
4334 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4335 wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
4336 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4338 LayoutDeviceSize zeroSize;
4339 wr::BorderRadius borderRadius =
4340 wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
4341 if (hasBorderRadius) {
4342 borderRadius = wr::ToBorderRadius(
4343 LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
4344 LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
4345 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
4346 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
4349 float spreadRadius =
4350 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4352 aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
4353 deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
4354 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius,
4355 spreadRadius, borderRadius,
4356 wr::BoxShadowClipMode::Outset);
4359 return true;
4362 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4363 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4364 nsRegion* aInvalidRegion) const {
4365 const auto* geometry =
4366 static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
4367 bool snap;
4368 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
4369 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
4370 nsRegion oldShadow, newShadow;
4371 nscoord dontCare[8];
4372 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
4373 if (hasBorderRadius) {
4374 // If we have rounded corners then we need to invalidate the frame area
4375 // too since we paint into it.
4376 oldShadow = geometry->mBounds;
4377 newShadow = GetBounds(aBuilder, &snap);
4378 } else {
4379 oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
4380 newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
4382 aInvalidRegion->Or(oldShadow, newShadow);
4386 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
4387 gfxContext* aCtx) {
4388 nsPoint offset = ToReferenceFrame();
4389 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4390 nsPresContext* presContext = mFrame->PresContext();
4392 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS);
4394 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
4397 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4398 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4399 const nsPoint& aReferenceOffset) {
4400 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4401 if (shadows.IsEmpty()) {
4402 // Means we don't have to paint anything
4403 return true;
4406 bool hasBorderRadius;
4407 bool nativeTheme =
4408 nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
4410 // We don't support native themed things yet like box shadows around
4411 // input buttons.
4412 return !nativeTheme;
4415 /* static */
4416 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4417 wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
4418 nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect) {
4419 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) {
4420 return;
4423 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
4425 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4427 LayoutDeviceRect clipRect =
4428 LayoutDeviceRect::FromAppUnits(aVisibleRect, appUnitsPerDevPixel);
4430 for (const auto& shadow : Reversed(shadows)) {
4431 if (!shadow.inset) {
4432 continue;
4435 nsRect shadowRect =
4436 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
4437 RectCornerRadii innerRadii;
4438 nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
4440 // Now translate everything to device pixels.
4441 LayoutDeviceRect deviceBoxRect =
4442 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4443 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4444 sRGBColor shadowColor =
4445 nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0);
4447 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4448 nsPoint(shadow.base.horizontal.ToAppUnits(),
4449 shadow.base.vertical.ToAppUnits()),
4450 appUnitsPerDevPixel);
4452 float blurRadius =
4453 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4455 wr::BorderRadius borderRadius = wr::ToBorderRadius(
4456 LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
4457 LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
4458 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
4459 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
4460 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4461 float spreadRadius =
4462 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4464 aBuilder.PushBoxShadow(
4465 wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
4466 !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
4467 wr::ToLayoutVector2D(shadowOffset),
4468 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, spreadRadius,
4469 borderRadius, wr::BoxShadowClipMode::Inset);
4473 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4474 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4475 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4476 nsDisplayListBuilder* aDisplayListBuilder) {
4477 if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame,
4478 ToReferenceFrame())) {
4479 return false;
4482 bool snap;
4483 nsRect visible = GetBounds(aDisplayListBuilder, &snap);
4484 nsPoint offset = ToReferenceFrame();
4485 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4486 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4487 aBuilder, aSc, visible, mFrame, borderRect);
4489 return true;
4492 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4493 nsIFrame* aFrame, nsDisplayList* aList)
4494 : nsDisplayWrapList(aBuilder, aFrame, aList,
4495 aBuilder->CurrentActiveScrolledRoot(), false) {}
4497 nsDisplayWrapList::nsDisplayWrapList(
4498 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4499 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
4500 : nsPaintedDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
4501 mList(aBuilder),
4502 mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
4503 mOverrideZIndex(0),
4504 mHasZIndexOverride(false),
4505 mClearingClipChain(aClearClipChain) {
4506 MOZ_COUNT_CTOR(nsDisplayWrapList);
4508 mBaseBuildingRect = GetBuildingRect();
4510 mListPtr = &mList;
4511 mListPtr->AppendToTop(aList);
4512 mOriginalClipChain = mClipChain;
4513 nsDisplayWrapList::UpdateBounds(aBuilder);
4516 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4517 nsIFrame* aFrame, nsDisplayItem* aItem)
4518 : nsPaintedDisplayItem(aBuilder, aFrame,
4519 aBuilder->CurrentActiveScrolledRoot()),
4520 mList(aBuilder),
4521 mOverrideZIndex(0),
4522 mHasZIndexOverride(false) {
4523 MOZ_COUNT_CTOR(nsDisplayWrapList);
4525 mBaseBuildingRect = GetBuildingRect();
4527 mListPtr = &mList;
4528 mListPtr->AppendToTop(aItem);
4529 mOriginalClipChain = mClipChain;
4530 nsDisplayWrapList::UpdateBounds(aBuilder);
4532 if (!aFrame || !aFrame->IsTransformed()) {
4533 return;
4536 // See the previous nsDisplayWrapList constructor
4537 if (aItem->Frame() == aFrame) {
4538 mToReferenceFrame = aItem->ToReferenceFrame();
4541 nsRect visible = aBuilder->GetVisibleRect() +
4542 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
4544 SetBuildingRect(visible);
4547 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList); }
4549 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder,
4550 const nsRect& aRect, HitTestState* aState,
4551 nsTArray<nsIFrame*>* aOutFrames) {
4552 mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames);
4555 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder,
4556 bool* aSnap) const {
4557 *aSnap = false;
4558 return mBounds;
4561 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4562 bool* aSnap) const {
4563 *aSnap = false;
4564 bool snap;
4565 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
4566 GetBounds(aBuilder, &snap));
4569 Maybe<nscolor> nsDisplayWrapList::IsUniform(
4570 nsDisplayListBuilder* aBuilder) const {
4571 // We could try to do something but let's conservatively just return Nothing.
4572 return Nothing();
4575 void nsDisplayWrapper::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4576 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4579 nsRect nsDisplayWrapList::GetComponentAlphaBounds(
4580 nsDisplayListBuilder* aBuilder) const {
4581 return mListPtr->GetComponentAlphaBounds(aBuilder);
4584 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4585 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4586 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4587 nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList) {
4588 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4589 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
4590 aNewClipList);
4591 return true;
4594 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder,
4595 nsIFrame* aFrame, nsDisplayList* aList,
4596 nsDisplayItemWrapper* aWrapper) {
4597 if (!aList->GetTop()) {
4598 return NS_OK;
4600 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
4601 if (!item) {
4602 return NS_ERROR_OUT_OF_MEMORY;
4604 // aList was emptied
4605 aList->AppendToTop(item);
4606 return NS_OK;
4609 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
4610 nsDisplayList* aList,
4611 nsDisplayItemWrapper* aWrapper) {
4612 for (nsDisplayItem* item : aList->TakeItems()) {
4613 item = aWrapper->WrapItem(aBuilder, item);
4614 if (!item) {
4615 return NS_ERROR_OUT_OF_MEMORY;
4617 aList->AppendToTop(item);
4619 // aList was emptied
4620 return NS_OK;
4623 nsresult nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
4624 nsIFrame* aFrame,
4625 const nsDisplayListSet& aIn,
4626 const nsDisplayListSet& aOut) {
4627 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
4628 NS_ENSURE_SUCCESS(rv, rv);
4630 if (&aOut == &aIn) {
4631 return NS_OK;
4633 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
4634 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
4635 aOut.Floats()->AppendToTop(aIn.Floats());
4636 aOut.Content()->AppendToTop(aIn.Content());
4637 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
4638 aOut.Outlines()->AppendToTop(aIn.Outlines());
4639 return NS_OK;
4642 nsresult nsDisplayItemWrapper::WrapListsInPlace(
4643 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4644 const nsDisplayListSet& aLists) {
4645 nsresult rv;
4646 if (WrapBorderBackground()) {
4647 // Our border-backgrounds are in-flow
4648 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
4649 NS_ENSURE_SUCCESS(rv, rv);
4651 // Our block border-backgrounds are in-flow
4652 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
4653 NS_ENSURE_SUCCESS(rv, rv);
4654 // The floats are not in flow
4655 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
4656 NS_ENSURE_SUCCESS(rv, rv);
4657 // Our child content is in flow
4658 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
4659 NS_ENSURE_SUCCESS(rv, rv);
4660 // The positioned descendants may not be in-flow
4661 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
4662 NS_ENSURE_SUCCESS(rv, rv);
4663 // The outlines may not be in-flow
4664 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
4667 nsDisplayOpacity::nsDisplayOpacity(
4668 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4669 const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsOnly,
4670 bool aNeedsActiveLayer, bool aWrapsBackdropFilter)
4671 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4672 mOpacity(aFrame->StyleEffects()->mOpacity),
4673 mForEventsOnly(aForEventsOnly),
4674 mNeedsActiveLayer(aNeedsActiveLayer),
4675 mChildOpacityState(ChildOpacityState::Unknown),
4676 mWrapsBackdropFilter(aWrapsBackdropFilter) {
4677 MOZ_COUNT_CTOR(nsDisplayOpacity);
4680 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder,
4681 const nsRect& aRect,
4682 nsDisplayItem::HitTestState* aState,
4683 nsTArray<nsIFrame*>* aOutFrames) {
4684 AutoRestore<float> opacity(aState->mCurrentOpacity);
4685 aState->mCurrentOpacity *= mOpacity;
4687 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4688 // only consider fully opaque items? Or make this configurable somehow?
4689 if (aBuilder->HitTestIsForVisibility() && mOpacity == 0.0f) {
4690 return;
4692 nsDisplayWrapList::HitTest(aBuilder, aRect, aState, aOutFrames);
4695 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4696 bool* aSnap) const {
4697 *aSnap = false;
4698 // The only time where mOpacity == 1.0 should be when we have will-change.
4699 // We could report this as opaque then but when the will-change value starts
4700 // animating the element would become non opaque and could cause repaints.
4701 return nsRegion();
4704 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4705 if (GetOpacity() == 0.0f) {
4706 return;
4709 if (GetOpacity() == 1.0f) {
4710 GetChildren()->Paint(aBuilder, aCtx,
4711 mFrame->PresContext()->AppUnitsPerDevPixel());
4712 return;
4715 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4716 // allocation.
4717 aCtx->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4718 GetChildren()->Paint(aBuilder, aCtx,
4719 mFrame->PresContext()->AppUnitsPerDevPixel());
4720 aCtx->GetDrawTarget()->PopLayer();
4723 /* static */
4724 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
4725 nsIFrame* aFrame) {
4726 return EffectCompositor::HasAnimationsForCompositor(
4727 aFrame, DisplayItemType::TYPE_OPACITY) ||
4728 (ActiveLayerTracker::IsStyleAnimated(
4729 aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()));
4732 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager,
4733 nsDisplayListBuilder* aBuilder) const {
4734 return !EffectCompositor::HasAnimationsForCompositor(
4735 mFrame, DisplayItemType::TYPE_OPACITY);
4738 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4739 // children that don't overlap and can all apply the opacity to themselves.
4740 static const size_t kOpacityMaxChildCount = 3;
4742 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4743 // are likely have more child items than |kOpacityMaxChildCount|.
4744 static const size_t kOpacityMaxListSize = kOpacityMaxChildCount * 2;
4747 * Recursively iterates through |aList| and collects at most
4748 * |kOpacityMaxChildCount| display item pointers to items that return true for
4749 * CanApplyOpacity(). The item pointers are added to |aArray|.
4751 * LayerEventRegions and WrapList items are ignored.
4753 * We need to do this recursively, because the child display items might contain
4754 * nested nsDisplayWrapLists.
4756 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4757 * item that returns false for CanApplyOpacity() is encountered.
4758 * Otherwise returns true.
4760 static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager,
4761 nsDisplayListBuilder* aBuilder,
4762 nsDisplayList* aList,
4763 nsTArray<nsPaintedDisplayItem*>& aArray) {
4764 if (aList->Length() > kOpacityMaxListSize) {
4765 // Exit early, since |aList| will likely contain more than
4766 // |kOpacityMaxChildCount| items.
4767 return false;
4770 for (nsDisplayItem* i : *aList) {
4771 const DisplayItemType type = i->GetType();
4773 if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
4774 continue;
4777 // Descend only into wraplists.
4778 if (type == DisplayItemType::TYPE_WRAP_LIST ||
4779 type == DisplayItemType::TYPE_CONTAINER) {
4780 // The current display item has children, process them first.
4781 if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(),
4782 aArray)) {
4783 return false;
4786 continue;
4789 if (aArray.Length() == kOpacityMaxChildCount) {
4790 return false;
4793 auto* item = i->AsPaintedDisplayItem();
4794 if (!item || !item->CanApplyOpacity(aManager, aBuilder)) {
4795 return false;
4798 aArray.AppendElement(item);
4801 return true;
4804 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager,
4805 nsDisplayListBuilder* aBuilder) {
4806 if (mChildOpacityState == ChildOpacityState::Deferred) {
4807 return false;
4810 // Iterate through the child display list and copy at most
4811 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4812 AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items;
4813 if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) {
4814 mChildOpacityState = ChildOpacityState::Deferred;
4815 return false;
4818 struct {
4819 nsPaintedDisplayItem* item{};
4820 nsRect bounds;
4821 } children[kOpacityMaxChildCount];
4823 bool snap;
4824 size_t childCount = 0;
4825 for (nsPaintedDisplayItem* item : items) {
4826 children[childCount].item = item;
4827 children[childCount].bounds = item->GetBounds(aBuilder, &snap);
4828 childCount++;
4831 for (size_t i = 0; i < childCount; i++) {
4832 for (size_t j = i + 1; j < childCount; j++) {
4833 if (children[i].bounds.Intersects(children[j].bounds)) {
4834 mChildOpacityState = ChildOpacityState::Deferred;
4835 return false;
4840 mChildOpacityState = ChildOpacityState::Applied;
4841 return true;
4845 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4846 * that has the same frame as the opacity item, and that supports painting with
4847 * opacity. In this case the opacity item can be optimized away.
4849 bool nsDisplayOpacity::ApplyToMask() {
4850 if (mList.Length() != 1) {
4851 return false;
4854 nsDisplayItem* item = mList.GetBottom();
4855 if (item->Frame() != mFrame) {
4856 // The effect item needs to have the same frame as the opacity item.
4857 return false;
4860 const DisplayItemType type = item->GetType();
4861 if (type == DisplayItemType::TYPE_MASK) {
4862 return true;
4865 return false;
4868 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4869 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder,
4870 float aInheritedOpacity) {
4871 if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
4872 mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
4873 // If we've been split, then we might need to merge, so
4874 // don't flatten us away.
4875 return false;
4878 if (mNeedsActiveLayer || mOpacity == 0.0) {
4879 // If our opacity is zero then we'll discard all descendant display items
4880 // except for layer event regions, so there's no point in doing this
4881 // optimization (and if we do do it, then invalidations of those descendants
4882 // might trigger repainting).
4883 return false;
4886 if (mList.IsEmpty()) {
4887 return false;
4890 // We can only flatten opacity items into a mask if we haven't
4891 // already flattened an earlier ancestor, since the SVG code pulls the opacity
4892 // from style directly, and won't know about the outer opacity value.
4893 if (aInheritedOpacity == 1.0f && ApplyToMask()) {
4894 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame));
4895 mChildOpacityState = ChildOpacityState::Applied;
4896 return true;
4899 // Return true if we successfully applied opacity to child items.
4900 return CanApplyToChildren(aManager, aBuilder);
4903 void nsDisplayOpacity::ComputeInvalidationRegion(
4904 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4905 nsRegion* aInvalidRegion) const {
4906 const auto* geometry =
4907 static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
4909 bool snap;
4910 if (mOpacity != geometry->mOpacity) {
4911 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4915 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) {
4916 aStream << " (opacity " << mOpacity << ", mChildOpacityState: ";
4917 switch (mChildOpacityState) {
4918 case ChildOpacityState::Unknown:
4919 aStream << "Unknown";
4920 break;
4921 case ChildOpacityState::Applied:
4922 aStream << "Applied";
4923 break;
4924 case ChildOpacityState::Deferred:
4925 aStream << "Deferred";
4926 break;
4927 default:
4928 break;
4931 aStream << ")";
4934 bool nsDisplayOpacity::CreateWebRenderCommands(
4935 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4936 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4937 nsDisplayListBuilder* aDisplayListBuilder) {
4938 MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied);
4939 float oldOpacity = aBuilder.GetInheritedOpacity();
4940 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
4941 aBuilder.SetInheritedOpacity(1.0f);
4942 aBuilder.SetInheritedClipChain(nullptr);
4943 float opacity = mOpacity * oldOpacity;
4944 float* opacityForSC = &opacity;
4946 uint64_t animationsId =
4947 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
4948 wr::WrAnimationProperty prop{
4949 wr::WrAnimationType::Opacity,
4950 animationsId,
4953 wr::StackingContextParams params;
4954 params.animation = animationsId ? &prop : nullptr;
4955 params.opacity = opacityForSC;
4956 params.clip =
4957 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
4958 if (mWrapsBackdropFilter) {
4959 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
4961 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
4962 params);
4964 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4965 &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
4966 aBuilder.SetInheritedOpacity(oldOpacity);
4967 aBuilder.SetInheritedClipChain(oldClipChain);
4968 return true;
4971 nsDisplayBlendMode::nsDisplayBlendMode(
4972 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4973 StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
4974 const bool aIsForBackground)
4975 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4976 mBlendMode(aBlendMode),
4977 mIsForBackground(aIsForBackground) {
4978 MOZ_COUNT_CTOR(nsDisplayBlendMode);
4981 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4982 bool* aSnap) const {
4983 *aSnap = false;
4984 // We are never considered opaque
4985 return nsRegion();
4988 bool nsDisplayBlendMode::CreateWebRenderCommands(
4989 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4990 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4991 nsDisplayListBuilder* aDisplayListBuilder) {
4992 wr::StackingContextParams params;
4993 params.mix_blend_mode =
4994 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
4995 params.clip =
4996 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
4997 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
4998 params);
5000 return nsDisplayWrapList::CreateWebRenderCommands(
5001 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5004 void nsDisplayBlendMode::Paint(nsDisplayListBuilder* aBuilder,
5005 gfxContext* aCtx) {
5006 // This should be switched to use PushLayerWithBlend, once it's
5007 // been implemented for all DrawTarget backends.
5008 DrawTarget* dt = aCtx->GetDrawTarget();
5009 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5010 Rect rect = NSRectToRect(GetPaintRect(aBuilder, aCtx), appUnitsPerDevPixel);
5011 rect.RoundOut();
5013 // Create a temporary DrawTarget that is clipped to the area that
5014 // we're going to draw to. This will include the same transform as
5015 // is currently on |dt|.
5016 RefPtr<DrawTarget> temp =
5017 dt->CreateClippedDrawTarget(rect, SurfaceFormat::B8G8R8A8);
5018 if (!temp) {
5019 return;
5022 gfxContext ctx(temp, /* aPreserveTransform */ true);
5024 GetChildren()->Paint(aBuilder, &ctx,
5025 mFrame->PresContext()->AppUnitsPerDevPixel());
5027 // Draw the temporary DT to the real destination, applying the blend mode, but
5028 // no transform.
5029 temp->Flush();
5030 RefPtr<SourceSurface> surface = temp->Snapshot();
5031 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
5032 dt->SetTransform(Matrix());
5033 dt->DrawSurface(
5034 surface, Rect(surface->GetRect()), Rect(surface->GetRect()),
5035 DrawSurfaceOptions(),
5036 DrawOptions(1.0f, nsCSSRendering::GetGFXBlendMode(mBlendMode)));
5039 gfx::CompositionOp nsDisplayBlendMode::BlendMode() {
5040 return nsCSSRendering::GetGFXBlendMode(mBlendMode);
5043 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const {
5044 // Items for the same content element should be merged into a single
5045 // compositing group.
5046 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
5047 !HasSameContent(aItem)) {
5048 return false;
5051 const auto* item = static_cast<const nsDisplayBlendMode*>(aItem);
5052 if (mIsForBackground || item->mIsForBackground) {
5053 // Don't merge background-blend-mode items
5054 return false;
5057 return true;
5060 /* static */
5061 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForMixBlendMode(
5062 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5063 const ActiveScrolledRoot* aActiveScrolledRoot) {
5064 return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
5065 aActiveScrolledRoot, false);
5068 /* static */
5069 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5070 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5071 nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot) {
5072 if (aSecondaryFrame) {
5073 auto type = GetTableTypeFromFrame(aFrame);
5074 auto index = static_cast<uint16_t>(type);
5076 return MakeDisplayItemWithIndex<nsDisplayTableBlendContainer>(
5077 aBuilder, aSecondaryFrame, index, aList, aActiveScrolledRoot, true,
5078 aFrame);
5081 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>(
5082 aBuilder, aFrame, 1, aList, aActiveScrolledRoot, true);
5085 nsDisplayBlendContainer::nsDisplayBlendContainer(
5086 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5087 const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground)
5088 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5089 mIsForBackground(aIsForBackground) {
5090 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
5093 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder* aBuilder,
5094 gfxContext* aCtx) {
5095 aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5096 GetChildren()->Paint(aBuilder, aCtx,
5097 mFrame->PresContext()->AppUnitsPerDevPixel());
5098 aCtx->GetDrawTarget()->PopLayer();
5101 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5102 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5103 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5104 nsDisplayListBuilder* aDisplayListBuilder) {
5105 wr::StackingContextParams params;
5106 params.flags |= wr::StackingContextFlags::IS_BLEND_CONTAINER;
5107 params.clip =
5108 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5109 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5110 params);
5112 return nsDisplayWrapList::CreateWebRenderCommands(
5113 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5116 nsDisplayOwnLayer::nsDisplayOwnLayer(
5117 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5118 const ActiveScrolledRoot* aActiveScrolledRoot,
5119 nsDisplayOwnLayerFlags aFlags, const ScrollbarData& aScrollbarData,
5120 bool aForceActive, bool aClearClipChain)
5121 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
5122 aClearClipChain),
5123 mFlags(aFlags),
5124 mScrollbarData(aScrollbarData),
5125 mForceActive(aForceActive),
5126 mWrAnimationId(0) {
5127 MOZ_COUNT_CTOR(nsDisplayOwnLayer);
5130 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5131 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb;
5134 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5135 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container;
5138 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5139 return IsScrollbarContainer() && IsScrollbarLayerForRoot();
5142 bool nsDisplayOwnLayer::IsScrollbarLayerForRoot() const {
5143 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5144 mScrollbarData.mTargetViewId ==
5145 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5148 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5149 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM;
5152 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5153 return GetType() == DisplayItemType::TYPE_FIXED_POSITION ||
5154 GetType() == DisplayItemType::TYPE_TABLE_FIXED_POSITION;
5157 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5158 return GetType() == DisplayItemType::TYPE_STICKY_POSITION;
5161 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5162 if (!mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
5163 return false;
5165 return mFrame->PresContext()->HasDynamicToolbar() ||
5166 // For tests on Android, this pref is set to simulate the dynamic
5167 // toolbar
5168 StaticPrefs::apz_fixed_margin_override_enabled();
5171 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5172 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5173 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5174 nsDisplayListBuilder* aDisplayListBuilder) {
5175 Maybe<wr::WrAnimationProperty> prop;
5176 bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() &&
5177 (IsScrollThumbLayer() || IsZoomingLayer() ||
5178 ShouldGetFixedOrStickyAnimationId() ||
5179 (IsRootScrollbarContainer() && HasDynamicToolbar()));
5181 if (needsProp) {
5182 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5183 // to create and set an animation id. That way APZ can adjust the position/
5184 // zoom of this content asynchronously as needed.
5185 RefPtr<WebRenderAPZAnimationData> animationData =
5186 aManager->CommandBuilder()
5187 .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(this);
5188 mWrAnimationId = animationData->GetAnimationId();
5190 prop.emplace();
5191 prop->id = mWrAnimationId;
5192 prop->key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5193 wr::SpatialKeyKind::APZ);
5194 prop->effect_type = wr::WrAnimationType::Transform;
5197 wr::StackingContextParams params;
5198 params.animation = prop.ptrOr(nullptr);
5199 params.clip =
5200 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5201 if (IsScrollbarContainer() && IsRootScrollbarContainer()) {
5202 params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER;
5204 if (IsZoomingLayer() ||
5205 (ShouldGetFixedOrStickyAnimationId() ||
5206 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5207 params.is_2d_scale_translation = true;
5208 params.should_snap = true;
5211 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5212 params);
5214 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
5215 aDisplayListBuilder);
5216 return true;
5219 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData,
5220 WebRenderLayerScrollData* aLayerData) {
5221 bool isRelevantToApz =
5222 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5223 ShouldGetFixedOrStickyAnimationId());
5225 if (!isRelevantToApz) {
5226 return false;
5229 if (!aLayerData) {
5230 return true;
5233 if (IsZoomingLayer()) {
5234 aLayerData->SetZoomAnimationId(mWrAnimationId);
5235 return true;
5238 if (IsFixedPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5239 aLayerData->SetFixedPositionAnimationId(mWrAnimationId);
5240 return true;
5243 if (IsStickyPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5244 aLayerData->SetStickyPositionAnimationId(mWrAnimationId);
5245 return true;
5248 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5250 aLayerData->SetScrollbarData(mScrollbarData);
5252 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5253 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5254 return true;
5257 if (IsScrollThumbLayer()) {
5258 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5259 LayoutDeviceRect bounds = LayoutDeviceIntRect::FromAppUnits(
5260 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
5261 // Subframe scrollbars are subject to the pinch-zoom scale,
5262 // but root scrollbars are not because they are outside of the
5263 // region that is zoomed.
5264 const float resolution =
5265 IsScrollbarLayerForRoot()
5266 ? 1.0f
5267 : mFrame->PresShell()->GetCumulativeResolution();
5268 LayerIntRect layerBounds =
5269 RoundedOut(bounds * LayoutDeviceToLayerScale(resolution));
5270 aLayerData->SetVisibleRect(layerBounds);
5272 return true;
5275 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) {
5276 aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
5277 (int)mFlags, mScrollbarData.mTargetViewId)
5278 .get();
5281 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
5282 nsIFrame* aFrame,
5283 nsSubDocumentFrame* aSubDocFrame,
5284 nsDisplayList* aList,
5285 nsDisplayOwnLayerFlags aFlags)
5286 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5287 aBuilder->CurrentActiveScrolledRoot(), aFlags),
5288 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5289 mShouldFlatten(false),
5290 mSubDocFrame(aSubDocFrame) {
5291 MOZ_COUNT_CTOR(nsDisplaySubDocument);
5293 if (mSubDocFrame && mSubDocFrame != mFrame) {
5294 mSubDocFrame->AddDisplayItem(this);
5298 nsDisplaySubDocument::~nsDisplaySubDocument() {
5299 MOZ_COUNT_DTOR(nsDisplaySubDocument);
5300 if (mSubDocFrame) {
5301 mSubDocFrame->RemoveDisplayItem(this);
5305 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const {
5306 return mSubDocFrame ? mSubDocFrame : mFrame;
5309 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) {
5310 if (aFrame == mSubDocFrame) {
5311 mSubDocFrame = nullptr;
5312 SetDeletedFrame();
5314 nsDisplayOwnLayer::RemoveFrame(aFrame);
5317 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder,
5318 nsIFrame* aFrame) {
5319 return aBuilder->IsPaintingToWindow() &&
5320 DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext());
5323 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder,
5324 bool* aSnap) const {
5325 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5327 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5328 usingDisplayPort) {
5329 *aSnap = false;
5330 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
5333 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
5336 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5337 bool* aSnap) const {
5338 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5340 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5341 usingDisplayPort) {
5342 *aSnap = false;
5343 return nsRegion();
5346 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
5349 /* static */
5350 nsDisplayFixedPosition* nsDisplayFixedPosition::CreateForFixedBackground(
5351 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5352 nsDisplayBackgroundImage* aImage, const uint16_t aIndex,
5353 const ActiveScrolledRoot* aScrollTargetASR) {
5354 nsDisplayList temp(aBuilder);
5355 temp.AppendToTop(aImage);
5357 if (aSecondaryFrame) {
5358 auto tableType = GetTableTypeFromFrame(aFrame);
5359 const uint16_t index = CalculateTablePerFrameKey(aIndex + 1, tableType);
5360 return MakeDisplayItemWithIndex<nsDisplayTableFixedPosition>(
5361 aBuilder, aSecondaryFrame, index, &temp, aFrame, aScrollTargetASR);
5364 return MakeDisplayItemWithIndex<nsDisplayFixedPosition>(
5365 aBuilder, aFrame, aIndex + 1, &temp, aScrollTargetASR);
5368 nsDisplayFixedPosition::nsDisplayFixedPosition(
5369 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5370 const ActiveScrolledRoot* aActiveScrolledRoot,
5371 const ActiveScrolledRoot* aScrollTargetASR)
5372 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5373 mScrollTargetASR(aScrollTargetASR),
5374 mIsFixedBackground(false) {
5375 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5378 nsDisplayFixedPosition::nsDisplayFixedPosition(
5379 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5380 const ActiveScrolledRoot* aScrollTargetASR)
5381 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5382 aBuilder->CurrentActiveScrolledRoot()),
5383 // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5384 mScrollTargetASR(aScrollTargetASR),
5385 mIsFixedBackground(true) {
5386 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5389 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() const {
5390 if (mScrollTargetASR &&
5391 (mIsFixedBackground || !nsLayoutUtils::IsReallyFixedPos(mFrame))) {
5392 return mScrollTargetASR->GetViewId();
5394 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5397 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5398 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5399 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5400 nsDisplayListBuilder* aDisplayListBuilder) {
5401 SideBits sides = SideBits::eNone;
5402 if (!mIsFixedBackground) {
5403 sides = nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5406 // We install this RAII scrolltarget tracker so that any
5407 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5408 // share the same ASR as this item) use the correct scroll target. That way
5409 // attempts to scroll on those items will scroll the root scroll frame.
5410 wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker(
5411 aBuilder, GetActiveScrolledRoot(), GetScrollTargetId(), sides);
5412 return nsDisplayOwnLayer::CreateWebRenderCommands(
5413 aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
5416 bool nsDisplayFixedPosition::UpdateScrollData(
5417 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5418 if (aLayerData) {
5419 if (!mIsFixedBackground) {
5420 aLayerData->SetFixedPositionSides(
5421 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame));
5423 aLayerData->SetFixedPositionScrollContainerId(GetScrollTargetId());
5425 nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5426 return true;
5429 bool nsDisplayFixedPosition::ShouldGetFixedOrStickyAnimationId() {
5430 #if defined(MOZ_WIDGET_ANDROID)
5431 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5432 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) ==
5433 GetScrollTargetId();
5434 #else
5435 return false;
5436 #endif
5439 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream& aStream) {
5440 aStream << nsPrintfCString(
5441 " (containerASR %s) (scrolltarget %" PRIu64 ")",
5442 ActiveScrolledRoot::ToString(mScrollTargetASR).get(),
5443 GetScrollTargetId())
5444 .get();
5447 TableType GetTableTypeFromFrame(nsIFrame* aFrame) {
5448 if (aFrame->IsTableFrame()) {
5449 return TableType::Table;
5452 if (aFrame->IsTableColFrame()) {
5453 return TableType::TableCol;
5456 if (aFrame->IsTableColGroupFrame()) {
5457 return TableType::TableColGroup;
5460 if (aFrame->IsTableRowFrame()) {
5461 return TableType::TableRow;
5464 if (aFrame->IsTableRowGroupFrame()) {
5465 return TableType::TableRowGroup;
5468 if (aFrame->IsTableCellFrame()) {
5469 return TableType::TableCell;
5472 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5473 return TableType::Table;
5476 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5477 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5478 nsIFrame* aAncestorFrame, const ActiveScrolledRoot* aScrollTargetASR)
5479 : nsDisplayFixedPosition(aBuilder, aFrame, aList, aScrollTargetASR),
5480 mAncestorFrame(aAncestorFrame) {
5481 if (aBuilder->IsRetainingDisplayList()) {
5482 mAncestorFrame->AddDisplayItem(this);
5486 nsDisplayStickyPosition::nsDisplayStickyPosition(
5487 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5488 const ActiveScrolledRoot* aActiveScrolledRoot,
5489 const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
5490 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5491 mContainerASR(aContainerASR),
5492 mClippedToDisplayPort(aClippedToDisplayPort),
5493 mShouldFlatten(false) {
5494 MOZ_COUNT_CTOR(nsDisplayStickyPosition);
5497 // Returns the smallest distance from "0" to the range [min, max] where
5498 // min <= max. Despite the name, the return value is actually a 1-D vector,
5499 // and so may be negative if max < 0.
5500 static nscoord DistanceToRange(nscoord min, nscoord max) {
5501 MOZ_ASSERT(min <= max);
5502 if (max < 0) {
5503 return max;
5505 if (min > 0) {
5506 return min;
5508 MOZ_ASSERT(min <= 0 && max >= 0);
5509 return 0;
5512 // Returns the magnitude of the part of the range [min, max] that is greater
5513 // than zero. The return value is always non-negative.
5514 static nscoord PositivePart(nscoord min, nscoord max) {
5515 MOZ_ASSERT(min <= max);
5516 if (min >= 0) {
5517 return max - min;
5519 if (max > 0) {
5520 return max;
5522 return 0;
5525 // Returns the magnitude of the part of the range [min, max] that is less
5526 // than zero. The return value is always non-negative.
5527 static nscoord NegativePart(nscoord min, nscoord max) {
5528 MOZ_ASSERT(min <= max);
5529 if (max <= 0) {
5530 return max - min;
5532 if (min < 0) {
5533 return 0 - min;
5535 return 0;
5538 StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() {
5539 StickyScrollContainer* stickyScrollContainer =
5540 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
5541 if (stickyScrollContainer) {
5542 // If there's no ASR for the scrollframe that this sticky item is attached
5543 // to, then don't create a WR sticky item for it either. Trying to do so
5544 // will end in sadness because WR will interpret some coordinates as
5545 // relative to the nearest enclosing scrollframe, which will correspond
5546 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5547 // same as the scrollframe this sticky item is actually supposed to be
5548 // attached to, thus the sadness.
5549 // Not sending WR the sticky item is ok, because the enclosing scrollframe
5550 // will never be asynchronously scrolled. Instead we will always position
5551 // the sticky items correctly on the gecko side and WR will never need to
5552 // adjust their position itself.
5553 MOZ_ASSERT(
5554 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5555 if (!stickyScrollContainer->ScrollFrame()
5556 ->IsMaybeAsynchronouslyScrolled()) {
5557 stickyScrollContainer = nullptr;
5560 return stickyScrollContainer;
5563 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5564 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5565 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5566 nsDisplayListBuilder* aDisplayListBuilder) {
5567 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5569 Maybe<wr::SpaceAndClipChainHelper> saccHelper;
5571 if (stickyScrollContainer) {
5572 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5574 bool snap;
5575 nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
5577 Maybe<float> topMargin;
5578 Maybe<float> rightMargin;
5579 Maybe<float> bottomMargin;
5580 Maybe<float> leftMargin;
5581 wr::StickyOffsetBounds vBounds = {0.0, 0.0};
5582 wr::StickyOffsetBounds hBounds = {0.0, 0.0};
5583 nsPoint appliedOffset;
5585 nsRectAbsolute outer;
5586 nsRectAbsolute inner;
5587 stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5589 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
5590 nsPoint offset =
5591 scrollFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5593 // Adjust the scrollPort coordinates to be relative to the reference frame,
5594 // so that it is in the same space as everything else.
5595 nsRect scrollPort =
5596 stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
5597 scrollPort += offset;
5599 // The following computations make more sense upon understanding the
5600 // semantics of "inner" and "outer", which is explained in the comment on
5601 // SetStickyPositionData in Layers.h.
5603 if (outer.YMost() != inner.YMost()) {
5604 // Question: How far will itemBounds.y be from the top of the scrollport
5605 // when we have scrolled from the current scroll position of "0" to
5606 // reach the range [inner.YMost(), outer.YMost()] where the item gets
5607 // stuck?
5608 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5609 // needs to be adjusted by the distance to the range, less any other
5610 // sticky ranges that fall between 0 and the range. If the distance is
5611 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5612 // scrolling upwards (decreasing scroll offset) to reach that range,
5613 // which would increase itemBounds.y and make it farther away from the
5614 // top of the scrollport. So in that case the adjustment is -distance.
5615 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5616 // we would be scrolling downwards, itemBounds.y would decrease, and we
5617 // again need to adjust by -distance. If we are already in the range
5618 // then no adjustment is needed and distance is 0 so again using
5619 // -distance works. If the distance is positive, and the item has both
5620 // top and bottom sticky ranges, then the bottom sticky range may fall
5621 // (entirely[1] or partly[2]) between the current scroll position.
5622 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5623 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5624 // In these cases, the item doesn't actually move for that part of the
5625 // distance, so we need to subtract out that bit, which can be computed
5626 // as the positive portion of the range [outer.Y(), inner.Y()].
5627 nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
5628 if (distance > 0) {
5629 distance -= PositivePart(outer.Y(), inner.Y());
5631 topMargin = Some(NSAppUnitsToFloatPixels(
5632 itemBounds.y - scrollPort.y - distance, auPerDevPixel));
5633 // Question: What is the maximum positive ("downward") offset that WR
5634 // will have to apply to this item in order to prevent the item from
5635 // visually moving?
5636 // Answer: Since the item is "sticky" in the range [inner.YMost(),
5637 // outer.YMost()], the maximum offset will be the size of the range, which
5638 // is outer.YMost() - inner.YMost().
5639 vBounds.max =
5640 NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
5641 // Question: how much of an offset has layout already applied to the item?
5642 // Answer: if we are
5643 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5644 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5645 // then layout has already applied some offset to the position of the
5646 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5647 // and |outer.YMost() - inner.YMost()| in case (b).
5648 if (inner.YMost() < 0) {
5649 appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
5650 MOZ_ASSERT(appliedOffset.y > 0);
5653 if (outer.Y() != inner.Y()) {
5654 // Similar logic as in the previous section, but this time we care about
5655 // the distance from itemBounds.YMost() to scrollPort.YMost().
5656 nscoord distance = DistanceToRange(outer.Y(), inner.Y());
5657 if (distance < 0) {
5658 distance += NegativePart(inner.YMost(), outer.YMost());
5660 bottomMargin = Some(NSAppUnitsToFloatPixels(
5661 scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
5662 // And here WR will be moving the item upwards rather than downwards so
5663 // again things are inverted from the previous block.
5664 vBounds.min =
5665 NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
5666 // We can't have appliedOffset be both positive and negative, and the top
5667 // adjustment takes priority. So here we only update appliedOffset.y if
5668 // it wasn't set by the top-sticky case above.
5669 if (appliedOffset.y == 0 && inner.Y() > 0) {
5670 appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
5671 MOZ_ASSERT(appliedOffset.y < 0);
5674 // Same as above, but for the x-axis
5675 if (outer.XMost() != inner.XMost()) {
5676 nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
5677 if (distance > 0) {
5678 distance -= PositivePart(outer.X(), inner.X());
5680 leftMargin = Some(NSAppUnitsToFloatPixels(
5681 itemBounds.x - scrollPort.x - distance, auPerDevPixel));
5682 hBounds.max =
5683 NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
5684 if (inner.XMost() < 0) {
5685 appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
5686 MOZ_ASSERT(appliedOffset.x > 0);
5689 if (outer.X() != inner.X()) {
5690 nscoord distance = DistanceToRange(outer.X(), inner.X());
5691 if (distance < 0) {
5692 distance += NegativePart(inner.XMost(), outer.XMost());
5694 rightMargin = Some(NSAppUnitsToFloatPixels(
5695 scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
5696 hBounds.min =
5697 NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
5698 if (appliedOffset.x == 0 && inner.X() > 0) {
5699 appliedOffset.x = std::max(0, outer.X()) - inner.X();
5700 MOZ_ASSERT(appliedOffset.x < 0);
5704 LayoutDeviceRect bounds =
5705 LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
5706 wr::LayoutVector2D applied = {
5707 NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
5708 NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
5709 wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame(
5710 wr::ToLayoutRect(bounds), topMargin.ptrOr(nullptr),
5711 rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
5712 leftMargin.ptrOr(nullptr), vBounds, hBounds, applied,
5713 wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5714 wr::SpatialKeyKind::Sticky));
5716 saccHelper.emplace(aBuilder, spatialId);
5717 aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId);
5721 wr::StackingContextParams params;
5722 params.clip =
5723 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5724 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
5725 aBuilder, params);
5726 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc,
5727 aManager, aDisplayListBuilder);
5730 if (stickyScrollContainer) {
5731 aManager->CommandBuilder().PopOverrideForASR(mContainerASR);
5734 return true;
5737 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5738 StickyScrollContainer* aStickyScrollContainer, float aAppUnitsPerDevPixel,
5739 float aScaleX, float aScaleY, LayerRectAbsolute& aStickyOuter,
5740 LayerRectAbsolute& aStickyInner) {
5741 nsRectAbsolute outer;
5742 nsRectAbsolute inner;
5743 aStickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5744 aStickyOuter.SetBox(
5745 NSAppUnitsToFloatPixels(outer.X(), aAppUnitsPerDevPixel) * aScaleX,
5746 NSAppUnitsToFloatPixels(outer.Y(), aAppUnitsPerDevPixel) * aScaleY,
5747 NSAppUnitsToFloatPixels(outer.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5748 NSAppUnitsToFloatPixels(outer.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5749 aStickyInner.SetBox(
5750 NSAppUnitsToFloatPixels(inner.X(), aAppUnitsPerDevPixel) * aScaleX,
5751 NSAppUnitsToFloatPixels(inner.Y(), aAppUnitsPerDevPixel) * aScaleY,
5752 NSAppUnitsToFloatPixels(inner.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5753 NSAppUnitsToFloatPixels(inner.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5756 bool nsDisplayStickyPosition::UpdateScrollData(
5757 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5758 bool hasDynamicToolbar = HasDynamicToolbar();
5759 if (aLayerData && hasDynamicToolbar) {
5760 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5761 if (stickyScrollContainer) {
5762 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5763 float cumulativeResolution =
5764 mFrame->PresShell()->GetCumulativeResolution();
5765 LayerRectAbsolute stickyOuter;
5766 LayerRectAbsolute stickyInner;
5767 CalculateLayerScrollRanges(stickyScrollContainer, auPerDevPixel,
5768 cumulativeResolution, cumulativeResolution,
5769 stickyOuter, stickyInner);
5770 aLayerData->SetStickyScrollRangeOuter(stickyOuter);
5771 aLayerData->SetStickyScrollRangeInner(stickyInner);
5773 SideBits sides =
5774 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5775 aLayerData->SetFixedPositionSides(sides);
5777 ScrollableLayerGuid::ViewID scrollId =
5778 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5779 ->GetScrolledFrame()
5780 ->GetContent());
5781 aLayerData->SetStickyPositionScrollContainerId(scrollId);
5784 // Return true if either there is a dynamic toolbar affecting this sticky
5785 // item or the OwnLayer base implementation returns true for some other
5786 // reason.
5787 bool ret = hasDynamicToolbar;
5788 ret |= nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5789 return ret;
5792 bool nsDisplayStickyPosition::ShouldGetFixedOrStickyAnimationId() {
5793 #if defined(MOZ_WIDGET_ANDROID)
5794 if (HasDynamicToolbar()) { // also implies being in the cross-process RCD
5795 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5796 if (stickyScrollContainer) {
5797 ScrollableLayerGuid::ViewID scrollId =
5798 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5799 ->GetScrolledFrame()
5800 ->GetContent());
5801 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) ==
5802 scrollId;
5805 #endif
5806 return false;
5809 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5810 nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
5811 nsIFrame* aScrollFrame, const CompositorHitTestInfo& aHitInfo,
5812 const nsRect& aHitArea)
5813 : nsDisplayWrapList(aBuilder, aScrollFrame),
5814 mScrollFrame(aScrollFrame),
5815 mScrolledFrame(aScrolledFrame),
5816 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5817 mHitInfo(aHitInfo),
5818 mHitArea(aHitArea) {
5819 #ifdef NS_BUILD_REFCNT_LOGGING
5820 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
5821 #endif
5824 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5825 nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) {
5826 ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
5827 mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(),
5828 ToReferenceFrame(), aLayerManager, mScrollParentId,
5829 mScrollFrame->GetSize(), false);
5830 metadata.GetMetrics().SetIsScrollInfoLayer(true);
5831 nsIScrollableFrame* scrollableFrame = mScrollFrame->GetScrollTargetFrame();
5832 if (scrollableFrame) {
5833 aBuilder->AddScrollFrameToNotify(scrollableFrame);
5836 return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
5839 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5840 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5841 if (aLayerData) {
5842 UniquePtr<ScrollMetadata> metadata =
5843 ComputeScrollMetadata(aData->GetBuilder(), aData->GetManager());
5844 MOZ_ASSERT(aData);
5845 MOZ_ASSERT(metadata);
5846 aLayerData->AppendScrollMetadata(*aData, *metadata);
5848 return true;
5851 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5852 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5853 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5854 nsDisplayListBuilder* aDisplayListBuilder) {
5855 ScrollableLayerGuid::ViewID scrollId =
5856 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame->GetContent());
5858 const LayoutDeviceRect devRect = LayoutDeviceRect::FromAppUnits(
5859 mHitArea, mScrollFrame->PresContext()->AppUnitsPerDevPixel());
5861 const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
5863 aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, mHitInfo,
5864 SideBits::eNone);
5866 return true;
5869 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) {
5870 aStream << " (scrollframe " << mScrollFrame << " scrolledFrame "
5871 << mScrolledFrame << ")";
5874 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5875 nsSubDocumentFrame* aSubDocFrame,
5876 nsDisplayList* aList, int32_t aAPD,
5877 int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags)
5878 : nsDisplaySubDocument(aBuilder, aFrame, aSubDocFrame, aList, aFlags),
5879 mAPD(aAPD),
5880 mParentAPD(aParentAPD) {
5881 MOZ_COUNT_CTOR(nsDisplayZoom);
5884 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder,
5885 bool* aSnap) const {
5886 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
5887 *aSnap = false;
5888 return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD);
5891 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5892 HitTestState* aState,
5893 nsTArray<nsIFrame*>* aOutFrames) {
5894 nsRect rect;
5895 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5896 // rect as well instead of possibly rounding the width or height to zero.
5897 if (aRect.width == 1 && aRect.height == 1) {
5898 rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD));
5899 rect.width = rect.height = 1;
5900 } else {
5901 rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
5903 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5906 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5907 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5908 const ActiveScrolledRoot* aActiveScrolledRoot, FrameMetrics::ViewID aViewID)
5909 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5910 mViewID(aViewID) {
5911 MOZ_COUNT_CTOR(nsDisplayAsyncZoom);
5914 #ifdef NS_BUILD_REFCNT_LOGGING
5915 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5916 MOZ_COUNT_DTOR(nsDisplayAsyncZoom);
5918 #endif
5920 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder* aBuilder,
5921 const nsRect& aRect, HitTestState* aState,
5922 nsTArray<nsIFrame*>* aOutFrames) {
5923 #ifdef DEBUG
5924 nsIScrollableFrame* scrollFrame = do_QueryFrame(mFrame);
5925 MOZ_ASSERT(scrollFrame && ViewportUtils::IsZoomedContentRoot(
5926 scrollFrame->GetScrolledFrame()));
5927 #endif
5928 nsRect rect = ViewportUtils::VisualToLayout(aRect, mFrame->PresShell());
5929 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5932 bool nsDisplayAsyncZoom::UpdateScrollData(
5933 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5934 bool ret = nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5935 MOZ_ASSERT(ret);
5936 if (aLayerData) {
5937 aLayerData->SetAsyncZoomContainerId(mViewID);
5939 return ret;
5942 ///////////////////////////////////////////////////
5943 // nsDisplayTransform Implementation
5946 #ifndef DEBUG
5947 static_assert(sizeof(nsDisplayTransform) <= 512,
5948 "nsDisplayTransform has grown");
5949 #endif
5951 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5952 nsIFrame* aFrame, nsDisplayList* aList,
5953 const nsRect& aChildrenBuildingRect)
5954 : nsPaintedDisplayItem(aBuilder, aFrame),
5955 mChildren(aBuilder),
5956 mTransform(Some(Matrix4x4())),
5957 mChildrenBuildingRect(aChildrenBuildingRect),
5958 mPrerenderDecision(PrerenderDecision::No),
5959 mIsTransformSeparator(true),
5960 mHasTransformGetter(false),
5961 mHasAssociatedPerspective(false),
5962 mContainsASRs(false) {
5963 MOZ_COUNT_CTOR(nsDisplayTransform);
5964 MOZ_ASSERT(aFrame, "Must have a frame!");
5965 Init(aBuilder, aList);
5968 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5969 nsIFrame* aFrame, nsDisplayList* aList,
5970 const nsRect& aChildrenBuildingRect,
5971 PrerenderDecision aPrerenderDecision)
5972 : nsPaintedDisplayItem(aBuilder, aFrame),
5973 mChildren(aBuilder),
5974 mChildrenBuildingRect(aChildrenBuildingRect),
5975 mPrerenderDecision(aPrerenderDecision),
5976 mIsTransformSeparator(false),
5977 mHasTransformGetter(false),
5978 mHasAssociatedPerspective(false),
5979 mContainsASRs(false) {
5980 MOZ_COUNT_CTOR(nsDisplayTransform);
5981 MOZ_ASSERT(aFrame, "Must have a frame!");
5982 SetReferenceFrameToAncestor(aBuilder);
5983 Init(aBuilder, aList);
5986 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5987 nsIFrame* aFrame, nsDisplayList* aList,
5988 const nsRect& aChildrenBuildingRect,
5989 decltype(WithTransformGetter))
5990 : nsPaintedDisplayItem(aBuilder, aFrame),
5991 mChildren(aBuilder),
5992 mChildrenBuildingRect(aChildrenBuildingRect),
5993 mPrerenderDecision(PrerenderDecision::No),
5994 mIsTransformSeparator(false),
5995 mHasTransformGetter(true),
5996 mHasAssociatedPerspective(false),
5997 mContainsASRs(false) {
5998 MOZ_COUNT_CTOR(nsDisplayTransform);
5999 MOZ_ASSERT(aFrame, "Must have a frame!");
6000 MOZ_ASSERT(aFrame->GetTransformGetter());
6001 Init(aBuilder, aList);
6004 void nsDisplayTransform::SetReferenceFrameToAncestor(
6005 nsDisplayListBuilder* aBuilder) {
6006 if (mFrame == aBuilder->RootReferenceFrame()) {
6007 return;
6009 // We manually recompute mToReferenceFrame without going through the
6010 // builder, since this won't apply the 'additional offset'. Our
6011 // children will already be painting with that applied, and we don't
6012 // want to include it a second time in our transform. We don't recompute
6013 // our visible/building rects, since those should still include the additional
6014 // offset.
6015 // TODO: Are there are things computed using our ToReferenceFrame that should
6016 // have the additional offset applied? Should we instead just manually remove
6017 // the offset from our transform instead of this more general value?
6018 // Can we instead apply the additional offset to us and not our children, like
6019 // we do for all other offsets (and how reference frames are supposed to
6020 // work)?
6021 nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame);
6022 const nsIFrame* referenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
6023 mToReferenceFrame = mFrame->GetOffsetToCrossDoc(referenceFrame);
6026 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
6027 nsDisplayList* aChildren) {
6028 mChildren.AppendToTop(aChildren);
6029 UpdateBounds(aBuilder);
6032 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
6033 return false;
6036 /* Returns the delta specified by the transform-origin property.
6037 * This is a positive delta, meaning that it indicates the direction to move
6038 * to get from (0, 0) of the frame to the transform origin. This function is
6039 * called off the main thread.
6041 /* static */
6042 Point3D nsDisplayTransform::GetDeltaToTransformOrigin(
6043 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6044 float aAppUnitsPerPixel) {
6045 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6046 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6047 aFrame->Combines3DTransformWithAncestors(),
6048 "Shouldn't get a delta for an untransformed frame!");
6050 if (!aFrame->IsTransformed()) {
6051 return Point3D();
6054 /* For both of the coordinates, if the value of transform is a
6055 * percentage, it's relative to the size of the frame. Otherwise, if it's
6056 * a distance, it's already computed for us!
6058 const nsStyleDisplay* display = aFrame->StyleDisplay();
6060 const StyleTransformOrigin& transformOrigin = display->mTransformOrigin;
6061 CSSPoint origin = nsStyleTransformMatrix::Convert2DPosition(
6062 transformOrigin.horizontal, transformOrigin.vertical, aRefBox);
6064 // Note:
6065 // 1. SVG frames have a reference box that can be (and typically is) offset
6066 // from the TopLeft() of the frame. We need to account for that here.
6067 // 2. If we are using transform-box:content-box in CSS layout, we have the
6068 // offset from TopLeft() of the frame as well.
6069 origin.x += CSSPixel::FromAppUnits(aRefBox.X());
6070 origin.y += CSSPixel::FromAppUnits(aRefBox.Y());
6072 float scale = AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel);
6073 float z = transformOrigin.depth._0;
6074 return Point3D(origin.x * scale, origin.y * scale, z * scale);
6077 /* static */
6078 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
6079 float aAppUnitsPerPixel,
6080 Matrix4x4& aOutMatrix) {
6081 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6082 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6083 aFrame->Combines3DTransformWithAncestors(),
6084 "Shouldn't get a delta for an untransformed frame!");
6085 MOZ_ASSERT(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
6087 if (!aFrame->IsTransformed()) {
6088 return false;
6091 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6092 // correctly yet (similar to the aBoundsOverride case for
6093 // GetResultingTransformMatrix)?
6094 nsIFrame* perspectiveFrame =
6095 aFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6096 if (!perspectiveFrame) {
6097 return false;
6100 /* Grab the values for perspective and perspective-origin (if present) */
6101 const nsStyleDisplay* perspectiveDisplay = perspectiveFrame->StyleDisplay();
6102 if (perspectiveDisplay->mChildPerspective.IsNone()) {
6103 return false;
6106 MOZ_ASSERT(perspectiveDisplay->mChildPerspective.IsLength());
6107 float perspective =
6108 perspectiveDisplay->mChildPerspective.AsLength().ToCSSPixels();
6109 perspective = std::max(1.0f, perspective);
6110 if (perspective < std::numeric_limits<Float>::epsilon()) {
6111 return true;
6114 TransformReferenceBox refBox(perspectiveFrame);
6116 Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition(
6117 perspectiveDisplay->mPerspectiveOrigin.horizontal,
6118 perspectiveDisplay->mPerspectiveOrigin.vertical, refBox,
6119 aAppUnitsPerPixel);
6121 /* GetOffsetTo computes the offset required to move from 0,0 in
6122 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6123 * this, it's faster to compute this way.
6125 nsPoint frameToPerspectiveOffset = -aFrame->GetOffsetTo(perspectiveFrame);
6126 Point frameToPerspectiveGfxOffset(
6127 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.x, aAppUnitsPerPixel),
6128 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.y, aAppUnitsPerPixel));
6130 /* Move the perspective origin to be relative to aFrame, instead of relative
6131 * to the containing block which is how it was specified in the style system.
6133 perspectiveOrigin += frameToPerspectiveGfxOffset;
6135 aOutMatrix._34 =
6136 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective),
6137 aAppUnitsPerPixel);
6139 aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
6140 return true;
6143 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6144 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6145 float aAppUnitsPerPixel)
6146 : mFrame(aFrame),
6147 mTranslate(aFrame->StyleDisplay()->mTranslate),
6148 mRotate(aFrame->StyleDisplay()->mRotate),
6149 mScale(aFrame->StyleDisplay()->mScale),
6150 mTransform(aFrame->StyleDisplay()->mTransform),
6151 mMotion(aFrame->StyleDisplay()->mOffsetPath.IsNone()
6152 ? Nothing()
6153 : MotionPathUtils::ResolveMotionPath(aFrame, aRefBox)),
6154 mToTransformOrigin(
6155 GetDeltaToTransformOrigin(aFrame, aRefBox, aAppUnitsPerPixel)) {}
6157 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6158 * translates from local coordinate space to transform coordinate space, then
6159 * hands it back.
6161 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6162 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6163 float aAppUnitsPerPixel) {
6164 return GetResultingTransformMatrixInternal(aProperties, aRefBox, nsPoint(),
6165 aAppUnitsPerPixel, 0);
6168 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6169 const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel,
6170 uint32_t aFlags) {
6171 TransformReferenceBox refBox(aFrame);
6172 FrameTransformProperties props(aFrame, refBox, aAppUnitsPerPixel);
6173 return GetResultingTransformMatrixInternal(props, refBox, aOrigin,
6174 aAppUnitsPerPixel, aFlags);
6177 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal(
6178 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6179 const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags) {
6180 const nsIFrame* frame = aProperties.mFrame;
6181 NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE),
6182 "Must have a frame to compute perspective!");
6184 // IncrementScaleRestyleCountIfNeeded in ActiveLayerTracker.cpp is a
6185 // simplified copy of this function.
6187 // Get the underlying transform matrix:
6189 /* Get the matrix, then change its basis to factor in the origin. */
6190 Matrix4x4 result;
6191 // Call IsSVGTransformed() regardless of the value of
6192 // aProperties.HasTransform(), since we still need any
6193 // potential parentsChildrenOnlyTransform.
6194 Matrix svgTransform, parentsChildrenOnlyTransform;
6195 const bool hasSVGTransforms =
6196 frame && frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
6197 frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform);
6198 bool shouldRound = nsLayoutUtils::ShouldSnapToGrid(frame);
6200 /* Transformed frames always have a transform, or are preserving 3d (and might
6201 * still have perspective!) */
6202 if (aProperties.HasTransform()) {
6203 result = nsStyleTransformMatrix::ReadTransforms(
6204 aProperties.mTranslate, aProperties.mRotate, aProperties.mScale,
6205 aProperties.mMotion.ptrOr(nullptr), aProperties.mTransform, aRefBox,
6206 aAppUnitsPerPixel);
6207 } else if (hasSVGTransforms) {
6208 // Correct the translation components for zoom:
6209 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6210 svgTransform._31 *= pixelsPerCSSPx;
6211 svgTransform._32 *= pixelsPerCSSPx;
6212 result = Matrix4x4::From2D(svgTransform);
6215 // Apply any translation due to 'transform-origin' and/or 'transform-box':
6216 result.ChangeBasis(aProperties.mToTransformOrigin);
6218 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
6219 // an explanation of what children-only transforms are.
6220 const bool parentHasChildrenOnlyTransform =
6221 hasSVGTransforms && !parentsChildrenOnlyTransform.IsIdentity();
6223 if (parentHasChildrenOnlyTransform) {
6224 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6225 parentsChildrenOnlyTransform._31 *= pixelsPerCSSPx;
6226 parentsChildrenOnlyTransform._32 *= pixelsPerCSSPx;
6228 Point3D frameOffset(
6229 NSAppUnitsToFloatPixels(-frame->GetPosition().x, aAppUnitsPerPixel),
6230 NSAppUnitsToFloatPixels(-frame->GetPosition().y, aAppUnitsPerPixel), 0);
6231 Matrix4x4 parentsChildrenOnlyTransform3D =
6232 Matrix4x4::From2D(parentsChildrenOnlyTransform)
6233 .ChangeBasis(frameOffset);
6235 result *= parentsChildrenOnlyTransform3D;
6238 Matrix4x4 perspectiveMatrix;
6239 bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE;
6240 if (hasPerspective) {
6241 if (ComputePerspectiveMatrix(frame, aAppUnitsPerPixel, perspectiveMatrix)) {
6242 result *= perspectiveMatrix;
6246 if ((aFlags & INCLUDE_PRESERVE3D_ANCESTORS) && frame &&
6247 frame->Combines3DTransformWithAncestors()) {
6248 // Include the transform set on our parent
6249 nsIFrame* parentFrame =
6250 frame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6251 NS_ASSERTION(parentFrame && parentFrame->IsTransformed() &&
6252 parentFrame->Extend3DContext(),
6253 "Preserve3D mismatch!");
6254 TransformReferenceBox refBox(parentFrame);
6255 FrameTransformProperties props(parentFrame, refBox, aAppUnitsPerPixel);
6257 uint32_t flags =
6258 aFlags & (INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE);
6260 // If this frame isn't transformed (but we exist for backface-visibility),
6261 // then we're not a reference frame so no offset to origin will be added.
6262 // Otherwise we need to manually translate into our parent's coordinate
6263 // space.
6264 if (frame->IsTransformed()) {
6265 nsLayoutUtils::PostTranslate(result, frame->GetPosition(),
6266 aAppUnitsPerPixel, shouldRound);
6268 Matrix4x4 parent = GetResultingTransformMatrixInternal(
6269 props, refBox, nsPoint(0, 0), aAppUnitsPerPixel, flags);
6270 result = result * parent;
6273 if (aFlags & OFFSET_BY_ORIGIN) {
6274 nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel,
6275 shouldRound);
6278 return result;
6281 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6282 static constexpr nsCSSPropertyIDSet opacitySet =
6283 nsCSSPropertyIDSet::OpacityProperties();
6284 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, opacitySet)) {
6285 return true;
6288 EffectCompositor::SetPerformanceWarning(
6289 mFrame, opacitySet,
6290 AnimationPerformanceWarning(
6291 AnimationPerformanceWarning::Type::OpacityFrameInactive));
6293 return false;
6296 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6297 return mPrerenderDecision != PrerenderDecision::No;
6300 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
6301 nsDisplayListBuilder* aBuilder) {
6302 return StaticPrefs::gfx_omta_background_color();
6305 static bool IsInStickyPositionedSubtree(const nsIFrame* aFrame) {
6306 for (const nsIFrame* frame = aFrame; frame;
6307 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame)) {
6308 if (frame->IsStickyPositioned()) {
6309 return true;
6312 return false;
6315 static bool ShouldUsePartialPrerender(const nsIFrame* aFrame) {
6316 return StaticPrefs::layout_animation_prerender_partial() &&
6317 // Bug 1642547: Support partial prerender for position:sticky elements.
6318 !IsInStickyPositionedSubtree(aFrame);
6321 /* static */
6322 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
6323 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect)
6324 -> PrerenderInfo {
6325 PrerenderInfo result;
6326 // If we are in a preserve-3d tree, and we've disallowed async animations, we
6327 // return No prerender decision directly.
6328 if ((aFrame->Extend3DContext() ||
6329 aFrame->Combines3DTransformWithAncestors()) &&
6330 !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
6331 return result;
6334 // Elements whose transform has been modified recently, or which
6335 // have a compositor-animated transform, can be prerendered. An element
6336 // might have only just had its transform animated in which case
6337 // the ActiveLayerManager may not have been notified yet.
6338 static constexpr nsCSSPropertyIDSet transformSet =
6339 nsCSSPropertyIDSet::TransformLikeProperties();
6340 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame) &&
6341 !EffectCompositor::HasAnimationsForCompositor(
6342 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
6343 EffectCompositor::SetPerformanceWarning(
6344 aFrame, transformSet,
6345 AnimationPerformanceWarning(
6346 AnimationPerformanceWarning::Type::TransformFrameInactive));
6348 // This case happens when we're sure that the frame is not animated and its
6349 // preserve-3d ancestors are not, either. So we don't need to pre-render.
6350 // However, this decision shouldn't affect the decisions for other frames in
6351 // the preserve-3d context. We need this flag to determine whether we should
6352 // block async animations on other frames in the current preserve-3d tree.
6353 result.mHasAnimations = false;
6354 return result;
6357 // We should not allow prerender if any ancestor container element has
6358 // mask/clip-path effects.
6360 // With prerender and async transform animation, we do not need to restyle an
6361 // animated element to respect position changes, since that transform is done
6362 // by layer animation. As a result, the container element is not aware of
6363 // position change of that containing element and loses the chance to update
6364 // the content of mask/clip-path.
6366 // Why do we need to update a mask? This is relative to how we generate a
6367 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
6368 // mask layer, to reduce memory usage, we did not choose the size of the
6369 // masked element as mask size. Instead, we read the union of bounds of all
6370 // children display items by nsDisplayWrapList::GetBounds, which is smaller
6371 // than or equal to the masked element's boundary, and use it as the position
6372 // size of the mask layer. That union bounds is actually affected by the
6373 // geometry of the animated element. To keep the content of mask up to date,
6374 // forbidding of prerender is required.
6375 for (nsIFrame* container =
6376 nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
6377 container;
6378 container = nsLayoutUtils::GetCrossDocParentFrameInProcess(container)) {
6379 const nsStyleSVGReset* svgReset = container->StyleSVGReset();
6380 if (svgReset->HasMask() || svgReset->HasClipPath()) {
6381 return result;
6385 // If the incoming dirty rect already contains the entire overflow area,
6386 // we are already rendering the entire content.
6387 nsRect overflow = aFrame->InkOverflowRectRelativeToSelf();
6388 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
6389 // in cases of non-invertible transforms, so we set `untransformedRect` to
6390 // `aDirtyRect` as an initial value for such cases.
6391 nsRect untransformedDirtyRect = *aDirtyRect;
6392 UntransformRect(*aDirtyRect, overflow, aFrame, &untransformedDirtyRect);
6393 if (untransformedDirtyRect.Contains(overflow)) {
6394 *aDirtyRect = untransformedDirtyRect;
6395 result.mDecision = PrerenderDecision::Full;
6396 return result;
6399 float viewportRatio =
6400 StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
6401 uint32_t absoluteLimitX =
6402 StaticPrefs::layout_animation_prerender_absolute_limit_x();
6403 uint32_t absoluteLimitY =
6404 StaticPrefs::layout_animation_prerender_absolute_limit_y();
6405 nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
6407 float resolution = aFrame->PresShell()->GetCumulativeResolution();
6408 if (resolution < 1.0f) {
6409 refSize.SizeTo(
6410 NSCoordSaturatingNonnegativeMultiply(refSize.width, 1.0f / resolution),
6411 NSCoordSaturatingNonnegativeMultiply(refSize.height,
6412 1.0f / resolution));
6415 // Only prerender if the transformed frame's size is <= a multiple of the
6416 // reference frame size (~viewport), and less than an absolute limit.
6417 // Both the ratio and the absolute limit are configurable.
6418 nscoord maxLength = std::max(nscoord(refSize.width * viewportRatio),
6419 nscoord(refSize.height * viewportRatio));
6420 nsSize relativeLimit(maxLength, maxLength);
6421 nsSize absoluteLimit(
6422 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
6423 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
6424 nsSize maxSize = Min(relativeLimit, absoluteLimit);
6426 const auto transform = nsLayoutUtils::GetTransformToAncestor(
6427 RelativeTo{aFrame},
6428 RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});
6429 const gfxRect transformedBounds = transform.TransformAndClipBounds(
6430 gfxRect(overflow.x, overflow.y, overflow.width, overflow.height),
6431 gfxRect::MaxIntRect());
6432 const nsSize frameSize =
6433 nsSize(transformedBounds.width, transformedBounds.height);
6435 uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
6436 uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
6437 if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
6438 *aDirtyRect = overflow;
6439 result.mDecision = PrerenderDecision::Full;
6440 return result;
6443 if (ShouldUsePartialPrerender(aFrame)) {
6444 *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(
6445 aFrame, untransformedDirtyRect, overflow, maxSize);
6446 result.mDecision = PrerenderDecision::Partial;
6447 return result;
6450 if (frameArea > maxLimitArea) {
6451 uint64_t appUnitsPerPixel = AppUnitsPerCSSPixel();
6452 EffectCompositor::SetPerformanceWarning(
6453 aFrame, transformSet,
6454 AnimationPerformanceWarning(
6455 AnimationPerformanceWarning::Type::ContentTooLargeArea,
6457 int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
6458 int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
6459 }));
6460 } else {
6461 EffectCompositor::SetPerformanceWarning(
6462 aFrame, transformSet,
6463 AnimationPerformanceWarning(
6464 AnimationPerformanceWarning::Type::ContentTooLarge,
6466 nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
6467 nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
6468 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
6469 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
6470 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
6471 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
6472 }));
6475 return result;
6478 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
6479 * visible or hit. */
6480 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix) {
6481 if (aMatrix.IsSingular()) {
6482 return false;
6484 if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) {
6485 return false;
6487 return true;
6490 const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const {
6491 if (mTransform) {
6492 return *mTransform;
6495 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6497 if (mHasTransformGetter) {
6498 mTransform.emplace((mFrame->GetTransformGetter())(mFrame, scale));
6499 Point3D newOrigin =
6500 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
6501 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f);
6502 mTransform->ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
6503 } else if (!mIsTransformSeparator) {
6504 DebugOnly<bool> isReference = mFrame->IsTransformed() ||
6505 mFrame->Combines3DTransformWithAncestors() ||
6506 mFrame->Extend3DContext();
6507 MOZ_ASSERT(isReference);
6508 mTransform.emplace(
6509 GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale,
6510 INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN));
6511 } else {
6512 // Use identity matrix
6513 mTransform.emplace();
6516 return *mTransform;
6519 const Matrix4x4Flagged& nsDisplayTransform::GetInverseTransform() const {
6520 if (mInverseTransform) {
6521 return *mInverseTransform;
6524 MOZ_ASSERT(!GetTransform().IsSingular());
6526 mInverseTransform.emplace(GetTransform().Inverse());
6528 return *mInverseTransform;
6531 Matrix4x4 nsDisplayTransform::GetTransformForRendering(
6532 LayoutDevicePoint* aOutOrigin) const {
6533 if (!mFrame->HasPerspective() || mHasTransformGetter ||
6534 mIsTransformSeparator) {
6535 if (!mHasTransformGetter && !mIsTransformSeparator && aOutOrigin) {
6536 // If aOutOrigin is provided, put the offset to origin into it, because
6537 // we need to keep it separate for webrender. The combination of
6538 // *aOutOrigin and the returned matrix here should always be equivalent
6539 // to what GetTransform() would have returned.
6540 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6541 *aOutOrigin = LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale);
6543 // The rounding behavior should also be the same as GetTransform().
6544 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6545 aOutOrigin->Round();
6547 return GetResultingTransformMatrix(mFrame, nsPoint(0, 0), scale,
6548 INCLUDE_PERSPECTIVE);
6550 return GetTransform().GetMatrix();
6552 MOZ_ASSERT(!mHasTransformGetter);
6554 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6555 // Don't include perspective transform, or the offset to origin, since
6556 // nsDisplayPerspective will handle both of those.
6557 return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
6560 const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
6561 nsDisplayListBuilder* aBuilder) {
6562 MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
6564 if (!IsLeafOf3DContext()) {
6565 return GetTransform().GetMatrix();
6568 if (!mTransformPreserves3D) {
6569 const nsIFrame* establisher; // Establisher of the 3D rendering context.
6570 for (establisher = mFrame;
6571 establisher && establisher->Combines3DTransformWithAncestors();
6572 establisher =
6573 establisher->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
6575 const nsIFrame* establisherReference = aBuilder->FindReferenceFrameFor(
6576 nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher));
6578 nsPoint offset = establisher->GetOffsetToCrossDoc(establisherReference);
6579 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6580 uint32_t flags =
6581 INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN;
6582 mTransformPreserves3D = MakeUnique<Matrix4x4>(
6583 GetResultingTransformMatrix(mFrame, offset, scale, flags));
6586 return *mTransformPreserves3D;
6589 bool nsDisplayTransform::CreateWebRenderCommands(
6590 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
6591 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
6592 nsDisplayListBuilder* aDisplayListBuilder) {
6593 // We want to make sure we don't pollute the transform property in the WR
6594 // stacking context by including the position of this frame (relative to the
6595 // parent reference frame). We need to keep those separate; the position of
6596 // this frame goes into the stacking context bounds while the transform goes
6597 // into the transform.
6598 LayoutDevicePoint position;
6599 Matrix4x4 newTransformMatrix = GetTransformForRendering(&position);
6601 gfx::Matrix4x4* transformForSC = &newTransformMatrix;
6602 if (newTransformMatrix.IsIdentity()) {
6603 // If the transform is an identity transform, strip it out so that WR
6604 // doesn't turn this stacking context into a reference frame, as it
6605 // affects positioning. Bug 1345577 tracks a better fix.
6606 transformForSC = nullptr;
6608 // In ChooseScaleAndSetTransform, we round the offset from the reference
6609 // frame used to adjust the transform, if there is no transform, or it
6610 // is just a translation. We need to do the same here.
6611 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6612 position.Round();
6616 auto key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
6617 wr::SpatialKeyKind::Transform);
6619 // We don't send animations for transform separator display items.
6620 uint64_t animationsId =
6621 mIsTransformSeparator
6623 : AddAnimationsForWebRender(
6624 this, aManager, aDisplayListBuilder,
6625 IsPartialPrerender() ? Some(position) : Nothing());
6626 wr::WrAnimationProperty prop{wr::WrAnimationType::Transform, animationsId,
6627 key};
6629 nsDisplayTransform* deferredTransformItem = nullptr;
6630 if (ShouldDeferTransform()) {
6631 // If it has perspective, we create a new scroll data via the
6632 // UpdateScrollData call because that scenario is more complex. Otherwise,
6633 // if we don't contain any ASRs then just stash the transform on the
6634 // StackingContextHelper and apply it to any scroll data that are created
6635 // inside this nsDisplayTransform.
6636 deferredTransformItem = this;
6639 // Determine if we're possibly animated (= would need an active layer in FLB).
6640 bool animated = !mIsTransformSeparator &&
6641 ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
6643 wr::StackingContextParams params;
6644 params.mBoundTransform = &newTransformMatrix;
6645 params.animation = animationsId ? &prop : nullptr;
6647 wr::WrTransformInfo transform_info;
6648 if (transformForSC) {
6649 transform_info.transform = wr::ToLayoutTransform(newTransformMatrix);
6650 transform_info.key = key;
6651 params.mTransformPtr = &transform_info;
6652 } else {
6653 params.mTransformPtr = nullptr;
6656 params.prim_flags = !BackfaceIsHidden()
6657 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
6658 : wr::PrimitiveFlags{0};
6659 params.paired_with_perspective = mHasAssociatedPerspective;
6660 params.mDeferredTransformItem = deferredTransformItem;
6661 params.mAnimated = animated;
6662 // Determine if we would have to rasterize any items in local raster space
6663 // (i.e. disable subpixel AA). We don't always need to rasterize locally even
6664 // if the stacking context is possibly animated (at the cost of potentially
6665 // some false negatives with respect to will-change handling), so we pass in
6666 // this determination separately to accurately match with when FLB would
6667 // normally disable subpixel AA.
6668 params.mRasterizeLocally = animated && Frame()->HasAnimationOfTransform();
6669 params.SetPreserve3D(mFrame->Extend3DContext() && !mIsTransformSeparator);
6670 params.clip =
6671 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
6673 LayoutDeviceSize boundsSize = LayoutDeviceSize::FromAppUnits(
6674 mChildBounds.Size(), mFrame->PresContext()->AppUnitsPerDevPixel());
6676 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
6677 params, LayoutDeviceRect(position, boundsSize));
6679 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6680 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
6681 return true;
6684 bool nsDisplayTransform::UpdateScrollData(
6685 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
6686 if (ShouldDeferTransform()) {
6687 // This case is handled in CreateWebRenderCommands by stashing the transform
6688 // on the stacking context.
6689 return false;
6691 if (aLayerData) {
6692 aLayerData->SetTransform(GetTransform().GetMatrix());
6693 aLayerData->SetTransformIsPerspective(mFrame->ChildrenHavePerspective());
6695 return true;
6698 bool nsDisplayTransform::ShouldSkipTransform(
6699 nsDisplayListBuilder* aBuilder) const {
6700 return (aBuilder->RootReferenceFrame() == mFrame) &&
6701 aBuilder->IsForGenerateGlyphMask();
6704 void nsDisplayTransform::Collect3DTransformLeaves(
6705 nsDisplayListBuilder* aBuilder, nsTArray<nsDisplayTransform*>& aLeaves) {
6706 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6707 aLeaves.AppendElement(this);
6708 return;
6711 FlattenedDisplayListIterator iter(aBuilder, &mChildren);
6712 while (iter.HasNext()) {
6713 nsDisplayItem* item = iter.GetNextItem();
6714 if (item->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
6715 auto* perspective = static_cast<nsDisplayPerspective*>(item);
6716 if (!perspective->GetChildren()->GetTop()) {
6717 continue;
6719 item = perspective->GetChildren()->GetTop();
6721 if (item->GetType() != DisplayItemType::TYPE_TRANSFORM) {
6722 gfxCriticalError() << "Invalid child item within 3D transform of type: "
6723 << item->Name();
6724 continue;
6726 static_cast<nsDisplayTransform*>(item)->Collect3DTransformLeaves(aBuilder,
6727 aLeaves);
6731 static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
6732 const gfx::Polygon& aPolygon) {
6733 MOZ_ASSERT(!aPolygon.IsEmpty());
6735 RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
6736 const nsTArray<Point4D>& points = aPolygon.GetPoints();
6738 pathBuilder->MoveTo(points[0].As2DPoint());
6740 for (size_t i = 1; i < points.Length(); ++i) {
6741 pathBuilder->LineTo(points[i].As2DPoint());
6744 pathBuilder->Close();
6745 return pathBuilder->Finish();
6748 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6749 nsDisplayListBuilder* aBuilder, nsTArray<TransformPolygon>& aLeaves) {
6750 std::list<TransformPolygon> inputLayers;
6752 nsTArray<nsDisplayTransform*> leaves;
6753 Collect3DTransformLeaves(aBuilder, leaves);
6754 for (nsDisplayTransform* item : leaves) {
6755 auto bounds = LayoutDeviceRect::FromAppUnits(
6756 item->mChildBounds, item->mFrame->PresContext()->AppUnitsPerDevPixel());
6757 Matrix4x4 transform = item->GetAccumulatedPreserved3DTransform(aBuilder);
6759 if (!IsFrameVisible(item->mFrame, transform)) {
6760 continue;
6762 gfx::Polygon polygon =
6763 gfx::Polygon::FromRect(gfx::Rect(bounds.ToUnknownRect()));
6765 polygon.TransformToScreenSpace(transform);
6767 if (polygon.GetPoints().Length() >= 3) {
6768 inputLayers.push_back(TransformPolygon(item, std::move(polygon)));
6772 if (inputLayers.empty()) {
6773 return;
6776 BSPTree<nsDisplayTransform> tree(inputLayers);
6777 nsTArray<TransformPolygon> orderedLayers(tree.GetDrawOrder());
6779 for (TransformPolygon& polygon : orderedLayers) {
6780 Matrix4x4 inverse =
6781 polygon.data->GetAccumulatedPreserved3DTransform(aBuilder).Inverse();
6783 MOZ_ASSERT(polygon.geometry);
6784 polygon.geometry->TransformToLayerSpace(inverse);
6787 aLeaves = std::move(orderedLayers);
6790 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder,
6791 gfxContext* aCtx) {
6792 Paint(aBuilder, aCtx, Nothing());
6795 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
6796 const Maybe<gfx::Polygon>& aPolygon) {
6797 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6798 MOZ_ASSERT(!aPolygon);
6799 nsTArray<TransformPolygon> leaves;
6800 CollectSorted3DTransformLeaves(aBuilder, leaves);
6801 for (TransformPolygon& item : leaves) {
6802 item.data->Paint(aBuilder, aCtx, item.geometry);
6804 return;
6807 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
6808 Matrix4x4 trans = ShouldSkipTransform(aBuilder)
6809 ? Matrix4x4()
6810 : GetAccumulatedPreserved3DTransform(aBuilder);
6811 if (!IsFrameVisible(mFrame, trans)) {
6812 return;
6815 Matrix trans2d;
6816 if (trans.CanDraw2D(&trans2d)) {
6817 aCtx->Multiply(ThebesMatrix(trans2d));
6819 if (aPolygon) {
6820 RefPtr<gfx::Path> path =
6821 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6822 aCtx->GetDrawTarget()->PushClip(path);
6825 GetChildren()->Paint(aBuilder, aCtx,
6826 mFrame->PresContext()->AppUnitsPerDevPixel());
6828 if (aPolygon) {
6829 aCtx->GetDrawTarget()->PopClip();
6831 return;
6834 // TODO: Implement 3d transform handling, including plane splitting and
6835 // sorting. See BasicCompositor.
6836 auto pixelBounds = LayoutDeviceRect::FromAppUnitsToOutside(
6837 mChildBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
6838 RefPtr<DrawTarget> untransformedDT =
6839 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6840 IntSize(pixelBounds.Width(), pixelBounds.Height()),
6841 SurfaceFormat::B8G8R8A8, true);
6842 if (!untransformedDT || !untransformedDT->IsValid()) {
6843 return;
6845 untransformedDT->SetTransform(
6846 Matrix::Translation(-Point(pixelBounds.X(), pixelBounds.Y())));
6848 gfxContext groupTarget(untransformedDT, /* aPreserveTransform */ true);
6850 if (aPolygon) {
6851 RefPtr<gfx::Path> path =
6852 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6853 aCtx->GetDrawTarget()->PushClip(path);
6856 GetChildren()->Paint(aBuilder, &groupTarget,
6857 mFrame->PresContext()->AppUnitsPerDevPixel());
6859 if (aPolygon) {
6860 aCtx->GetDrawTarget()->PopClip();
6863 RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
6865 trans.PreTranslate(pixelBounds.X(), pixelBounds.Y(), 0);
6866 aCtx->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf, trans);
6869 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
6870 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6871 // completely bypass the main thread for this animation, so it is always
6872 // worthwhile.
6873 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6874 // already involved so there is less to be gained.
6875 // Therefore we check that the *post-transform* bounds of this item are
6876 // big enough to justify an active layer.
6877 return EffectCompositor::HasAnimationsForCompositor(
6878 mFrame, DisplayItemType::TYPE_TRANSFORM) ||
6879 (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame));
6882 nsRect nsDisplayTransform::TransformUntransformedBounds(
6883 nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
6884 bool snap;
6885 const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
6886 // GetTransform always operates in dev pixels.
6887 const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
6888 return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
6889 factor);
6893 * Returns the bounds for this transform. The bounds are calculated during
6894 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6896 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
6897 bool* aSnap) const {
6898 *aSnap = false;
6899 return mBounds;
6902 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
6903 MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
6905 /* Some transforms can get empty bounds in 2D, but might get transformed again
6906 * and get non-empty bounds. A simple example of this would be a 180 degree
6907 * rotation getting applied twice.
6908 * We should not depend on transforming bounds level by level.
6910 * This function collects the bounds of this transform and stores it in
6911 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6912 * down and include the bounds of the child transforms.
6913 * The bounds are transformed with the accumulated transformation matrix up to
6914 * the 3D context root coordinate space.
6916 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6917 accTransform.Accumulate(GetTransform().GetMatrix());
6919 // Do not dive into another 3D context.
6920 if (!IsLeafOf3DContext()) {
6921 for (nsDisplayItem* i : *GetChildren()) {
6922 i->DoUpdateBoundsPreserves3D(aBuilder);
6926 /* The child transforms that extend 3D context further will have empty bounds,
6927 * so the untransformed bounds here is the bounds of all the non-preserve-3d
6928 * content under this transform.
6930 const nsRect rect = TransformUntransformedBounds(
6931 aBuilder, accTransform.GetCurrentTransform());
6932 aBuilder->AccumulateRect(rect);
6935 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6936 nsDisplayListBuilder* aBuilder) {
6937 MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
6938 IsTransformSeparator());
6939 // Updating is not going through to child 3D context.
6940 ComputeBounds(aBuilder);
6943 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
6944 UpdateUntransformedBounds(aBuilder);
6946 if (IsTransformSeparator()) {
6947 MOZ_ASSERT(GetTransform().IsIdentity());
6948 mBounds = mChildBounds;
6949 return;
6952 if (mFrame->Extend3DContext()) {
6953 if (!Combines3DTransformWithAncestors()) {
6954 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6955 // collect the bounds from the child transforms.
6956 UpdateBoundsFor3D(aBuilder);
6957 } else {
6958 // With nested 3D transforms, the 2D bounds might not be useful.
6959 mBounds = nsRect();
6962 return;
6965 MOZ_ASSERT(!mFrame->Extend3DContext());
6967 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
6968 // but mix-blend-mode relies on having bounds set. See bug 1556956.
6970 // A stand-alone transform.
6971 mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
6974 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
6975 MOZ_ASSERT(mFrame->Extend3DContext() &&
6976 !mFrame->Combines3DTransformWithAncestors() &&
6977 !IsTransformSeparator());
6979 // Always start updating from an establisher of a 3D rendering context.
6980 nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
6981 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6982 accTransform.StartRoot();
6983 ComputeBounds(aBuilder);
6984 mBounds = aBuilder->GetAccumulatedRect();
6987 void nsDisplayTransform::UpdateUntransformedBounds(
6988 nsDisplayListBuilder* aBuilder) {
6989 mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
6990 aBuilder, mActiveScrolledRoot);
6993 #ifdef DEBUG_HIT
6994 # include <time.h>
6995 #endif
6997 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
6998 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
6999 const nsRect& aRect, HitTestState* aState,
7000 nsTArray<nsIFrame*>* aOutFrames) {
7001 if (aState->mInPreserves3D) {
7002 GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
7003 return;
7006 /* Here's how this works:
7007 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
7008 * anything).
7009 * 2. Invert the matrix.
7010 * 3. Use it to transform the rect into the correct space.
7011 * 4. Pass that rect down through to the list's version of HitTest.
7013 // GetTransform always operates in dev pixels.
7014 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7015 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7017 if (!IsFrameVisible(mFrame, matrix)) {
7018 return;
7021 const bool oldHitOccludingItem = aState->mHitOccludingItem;
7023 /* We want to go from transformed-space to regular space.
7024 * Thus we have to invert the matrix, which normally does
7025 * the reverse operation (e.g. regular->transformed)
7028 /* Now, apply the transform and pass it down the channel. */
7029 matrix.Invert();
7030 nsRect resultingRect;
7031 // Magic width/height indicating we're hit testing a point, not a rect
7032 const bool testingPoint = aRect.width == 1 && aRect.height == 1;
7033 if (testingPoint) {
7034 Point4D point =
7035 matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
7036 NSAppUnitsToFloatPixels(aRect.y, factor)));
7037 if (!point.HasPositiveWCoord()) {
7038 return;
7041 Point point2d = point.As2DPoint();
7043 resultingRect =
7044 nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
7045 NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1);
7047 } else {
7048 Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
7049 NSAppUnitsToFloatPixels(aRect.y, factor),
7050 NSAppUnitsToFloatPixels(aRect.width, factor),
7051 NSAppUnitsToFloatPixels(aRect.height, factor));
7053 Rect childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds.x, factor),
7054 NSAppUnitsToFloatPixels(mChildBounds.y, factor),
7055 NSAppUnitsToFloatPixels(mChildBounds.width, factor),
7056 NSAppUnitsToFloatPixels(mChildBounds.height, factor));
7058 Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
7060 resultingRect =
7061 nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
7062 NSFloatPixelsToAppUnits(float(rect.Y()), factor),
7063 NSFloatPixelsToAppUnits(float(rect.Width()), factor),
7064 NSFloatPixelsToAppUnits(float(rect.Height()), factor));
7067 if (resultingRect.IsEmpty()) {
7068 return;
7071 #ifdef DEBUG_HIT
7072 printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
7073 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(),
7074 resultingRect.Y());
7075 uint32_t originalFrameCount = aOutFrames.Length();
7076 #endif
7078 GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
7080 if (aState->mHitOccludingItem && !testingPoint &&
7081 !mChildBounds.Contains(aRect)) {
7082 MOZ_ASSERT(aBuilder->HitTestIsForVisibility());
7083 // We're hit-testing a rect that's bigger than our child bounds, but
7084 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so
7085 // we can't stop hit-testing altogether.
7087 // FIXME(emilio): I think this means that theoretically we might include
7088 // some frames fully behind other transformed-but-opaque frames? Then again
7089 // that's our pre-existing behavior for other untransformed content that
7090 // doesn't fill the whole rect. To be fully correct I think we'd need proper
7091 // "known occluded region" tracking, but that might be overkill for our
7092 // purposes here.
7093 aState->mHitOccludingItem = oldHitOccludingItem;
7096 #ifdef DEBUG_HIT
7097 if (originalFrameCount != aOutFrames.Length())
7098 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7099 dynamic_cast<void*>(aOutFrames.ElementAt(0)));
7100 printf("=== end of hit test ===\n");
7101 #endif
7104 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
7105 const nsPoint& aPoint) {
7106 // GetTransform always operates in dev pixels.
7107 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7108 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7110 NS_ASSERTION(IsFrameVisible(mFrame, matrix),
7111 "We can't have hit a frame that isn't visible!");
7113 Matrix4x4 inverse = matrix;
7114 inverse.Invert();
7115 Point4D point =
7116 inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
7117 NSAppUnitsToFloatPixels(aPoint.y, factor)));
7119 Point point2d = point.As2DPoint();
7121 Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
7122 return transformed.z;
7125 /* The transform is opaque iff the transform consists solely of scales and
7126 * translations and if the underlying content is opaque. Thus if the transform
7127 * is of the form
7129 * |a c e|
7130 * |b d f|
7131 * |0 0 1|
7133 * We need b and c to be zero.
7135 * We also need to check whether the underlying opaque content completely fills
7136 * our visible rect. We use UntransformRect which expands to the axis-aligned
7137 * bounding rect, but that's OK since if
7138 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7139 * certainly contains the actual (non-axis-aligned) untransformed rect.
7141 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7142 bool* aSnap) const {
7143 *aSnap = false;
7145 nsRect untransformedVisible;
7146 if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
7147 return nsRegion();
7150 const Matrix4x4Flagged& matrix = GetTransform();
7151 Matrix matrix2d;
7152 if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
7153 return nsRegion();
7156 nsRegion result;
7158 bool tmpSnap;
7159 const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
7160 const nsRegion opaque =
7161 ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
7163 if (opaque.Contains(untransformedVisible)) {
7164 result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
7166 return result;
7169 nsRect nsDisplayTransform::GetComponentAlphaBounds(
7170 nsDisplayListBuilder* aBuilder) const {
7171 if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
7172 return nsRect();
7175 bool snap;
7176 return GetBounds(aBuilder, &snap);
7179 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7180 * the smallest rectangle (in app space) containing the transformed image of
7181 * that rectangle. That is, it takes the four corners of the rectangle,
7182 * transforms them according to the matrix associated with the specified frame,
7183 * then returns the smallest rectangle containing the four transformed points.
7185 * @param aUntransformedBounds The rectangle (in app units) to transform.
7186 * @param aFrame The frame whose transformation should be applied.
7187 * @param aOrigin The delta from the frame origin to the coordinate space origin
7188 * @return The smallest rectangle containing the image of the transformed
7189 * rectangle.
7191 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds,
7192 const nsIFrame* aFrame,
7193 TransformReferenceBox& aRefBox) {
7194 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7196 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7198 FrameTransformProperties props(aFrame, aRefBox, factor);
7199 return nsLayoutUtils::MatrixTransformRect(
7200 aUntransformedBounds,
7201 GetResultingTransformMatrixInternal(props, aRefBox, nsPoint(), factor,
7202 kTransformRectFlags),
7203 factor);
7206 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7207 const nsRect& aChildBounds,
7208 const nsIFrame* aFrame,
7209 nsRect* aOutRect) {
7210 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7212 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7213 Matrix4x4 transform = GetResultingTransformMatrix(aFrame, nsPoint(), factor,
7214 kTransformRectFlags);
7215 return UntransformRect(aTransformedBounds, aChildBounds, transform, factor,
7216 aOutRect);
7219 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7220 const nsRect& aChildBounds,
7221 const Matrix4x4& aMatrix,
7222 float aAppUnitsPerPixel,
7223 nsRect* aOutRect) {
7224 if (aMatrix.IsSingular()) {
7225 return false;
7228 RectDouble result(
7229 NSAppUnitsToFloatPixels(aTransformedBounds.x, aAppUnitsPerPixel),
7230 NSAppUnitsToFloatPixels(aTransformedBounds.y, aAppUnitsPerPixel),
7231 NSAppUnitsToFloatPixels(aTransformedBounds.width, aAppUnitsPerPixel),
7232 NSAppUnitsToFloatPixels(aTransformedBounds.height, aAppUnitsPerPixel));
7234 RectDouble childGfxBounds(
7235 NSAppUnitsToFloatPixels(aChildBounds.x, aAppUnitsPerPixel),
7236 NSAppUnitsToFloatPixels(aChildBounds.y, aAppUnitsPerPixel),
7237 NSAppUnitsToFloatPixels(aChildBounds.width, aAppUnitsPerPixel),
7238 NSAppUnitsToFloatPixels(aChildBounds.height, aAppUnitsPerPixel));
7240 result = aMatrix.Inverse().ProjectRectBounds(result, childGfxBounds);
7241 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result),
7242 aAppUnitsPerPixel);
7243 return true;
7246 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder,
7247 const nsRect& aRect,
7248 nsRect* aOutRect) const {
7249 if (GetTransform().IsSingular()) {
7250 return false;
7253 // GetTransform always operates in dev pixels.
7254 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7255 RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor),
7256 NSAppUnitsToFloatPixels(aRect.y, factor),
7257 NSAppUnitsToFloatPixels(aRect.width, factor),
7258 NSAppUnitsToFloatPixels(aRect.height, factor));
7260 bool snap;
7261 nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
7262 RectDouble childGfxBounds(
7263 NSAppUnitsToFloatPixels(childBounds.x, factor),
7264 NSAppUnitsToFloatPixels(childBounds.y, factor),
7265 NSAppUnitsToFloatPixels(childBounds.width, factor),
7266 NSAppUnitsToFloatPixels(childBounds.height, factor));
7268 /* We want to untransform the matrix, so invert the transformation first! */
7269 result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds);
7271 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
7273 return true;
7276 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) {
7277 aStream << GetTransform().GetMatrix();
7278 if (IsTransformSeparator()) {
7279 aStream << " transform-separator";
7281 if (IsLeafOf3DContext()) {
7282 aStream << " 3d-context-leaf";
7284 if (mFrame->Extend3DContext()) {
7285 aStream << " extends-3d-context";
7287 if (mFrame->Combines3DTransformWithAncestors()) {
7288 aStream << " combines-3d-with-ancestors";
7291 aStream << " prerender(";
7292 switch (mPrerenderDecision) {
7293 case PrerenderDecision::No:
7294 aStream << "no";
7295 break;
7296 case PrerenderDecision::Partial:
7297 aStream << "partial";
7298 break;
7299 case PrerenderDecision::Full:
7300 aStream << "full";
7301 break;
7303 aStream << ")";
7304 aStream << " childrenBuildingRect" << mChildrenBuildingRect;
7307 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
7308 nsIFrame* aFrame,
7309 nsDisplayList* aList)
7310 : nsPaintedDisplayItem(aBuilder, aFrame), mList(aBuilder) {
7311 mList.AppendToTop(aList);
7312 MOZ_ASSERT(mList.Length() == 1);
7313 MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
7316 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder,
7317 gfxContext* aCtx) {
7318 // Just directly recurse into children, since we'll include the persepctive
7319 // value in any nsDisplayTransform children.
7320 GetChildren()->Paint(aBuilder, aCtx,
7321 mFrame->PresContext()->AppUnitsPerDevPixel());
7324 nsRegion nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7325 bool* aSnap) const {
7326 if (!GetChildren()->GetTop()) {
7327 *aSnap = false;
7328 return nsRegion();
7331 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder, aSnap);
7334 bool nsDisplayPerspective::CreateWebRenderCommands(
7335 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7336 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7337 nsDisplayListBuilder* aDisplayListBuilder) {
7338 float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
7339 Matrix4x4 perspectiveMatrix;
7340 DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
7341 mFrame, appUnitsPerPixel, perspectiveMatrix);
7342 MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
7345 * ClipListToRange can remove our child after we were created.
7347 if (!GetChildren()->GetTop()) {
7348 return false;
7352 * The resulting matrix is still in the coordinate space of the transformed
7353 * frame. Append a translation to the reference frame coordinates.
7355 nsDisplayTransform* transform =
7356 static_cast<nsDisplayTransform*>(GetChildren()->GetTop());
7358 Point3D newOrigin =
7359 Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
7360 appUnitsPerPixel),
7361 NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
7362 appUnitsPerPixel),
7363 0.0f);
7364 Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
7366 perspectiveMatrix.PostTranslate(roundedOrigin);
7368 nsIFrame* perspectiveFrame =
7369 mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
7371 // Passing true here is always correct, since perspective always combines
7372 // transforms with the descendants. However that'd make WR do a lot of work
7373 // that it doesn't really need to do if there aren't other transforms forming
7374 // part of the 3D context.
7376 // WR knows how to treat perspective in that case, so the only thing we need
7377 // to do is to ensure we pass true when we're involved in a 3d context in any
7378 // other way via the transform-style property on either the transformed frame
7379 // or the perspective frame in order to not confuse WR's preserve-3d code in
7380 // very awful ways.
7381 bool preserve3D =
7382 mFrame->Extend3DContext() || perspectiveFrame->Extend3DContext();
7384 wr::StackingContextParams params;
7386 wr::WrTransformInfo transform_info;
7387 transform_info.transform = wr::ToLayoutTransform(perspectiveMatrix);
7388 transform_info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
7389 wr::SpatialKeyKind::Perspective);
7390 params.mTransformPtr = &transform_info;
7392 params.reference_frame_kind = wr::WrReferenceFrameKind::Perspective;
7393 params.prim_flags = !BackfaceIsHidden()
7394 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7395 : wr::PrimitiveFlags{0};
7396 params.SetPreserve3D(preserve3D);
7397 params.clip =
7398 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
7400 Maybe<uint64_t> scrollingRelativeTo;
7401 for (const auto* asr = GetActiveScrolledRoot(); asr; asr = asr->mParent) {
7402 // In OOP documents, the root scrollable frame of the in-process root
7403 // document is always active, so using IsAncestorFrameCrossDocInProcess
7404 // should be fine here.
7405 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7406 asr->mScrollableFrame->GetScrolledFrame(), perspectiveFrame)) {
7407 scrollingRelativeTo.emplace(asr->GetViewId());
7408 break;
7412 // We put the perspective reference frame wrapping the transformed frame,
7413 // even though there may be arbitrarily nested scroll frames in between.
7415 // We need to know how many ancestor scroll-frames are we nested in, in order
7416 // for the async scrolling code in WebRender to calculate the right
7417 // transformation for the perspective contents.
7418 params.scrolling_relative_to = scrollingRelativeTo.ptrOr(nullptr);
7420 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
7421 params);
7423 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7424 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
7426 return true;
7429 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
7430 nsTextFrame* aFrame)
7431 : nsPaintedDisplayItem(aBuilder, aFrame),
7432 mVisIStartEdge(0),
7433 mVisIEndEdge(0) {
7434 MOZ_COUNT_CTOR(nsDisplayText);
7435 mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7436 // Bug 748228
7437 mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
7438 mVisibleRect = aBuilder->GetVisibleRect() +
7439 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
7442 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager,
7443 nsDisplayListBuilder* aBuilder) const {
7444 auto* f = static_cast<nsTextFrame*>(mFrame);
7446 if (f->IsSelected()) {
7447 return false;
7450 const nsStyleText* textStyle = f->StyleText();
7451 if (textStyle->HasTextShadow()) {
7452 return false;
7455 nsTextFrame::TextDecorations decorations;
7456 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7457 decorations);
7458 return !decorations.HasDecorationLines();
7461 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
7462 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
7463 // We don't pass mVisibleRect here, since this can be called from within
7464 // the WebRender fallback painting path, and we don't want to issue
7465 // recorded commands that are dependent on the visible/building rect.
7466 RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
7468 auto* textFrame = static_cast<nsTextFrame*>(mFrame);
7469 LCPTextFrameHelper::MaybeUnionTextFrame(textFrame,
7470 mBounds - ToReferenceFrame());
7473 bool nsDisplayText::CreateWebRenderCommands(
7474 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7475 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7476 nsDisplayListBuilder* aDisplayListBuilder) {
7477 auto* f = static_cast<nsTextFrame*>(mFrame);
7478 auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
7480 nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
7481 // Bug 748228
7482 bounds.Inflate(appUnitsPerDevPixel);
7484 if (bounds.IsEmpty()) {
7485 return true;
7488 // For large font sizes, punt to a blob image, to avoid the blurry rendering
7489 // that results from WR clamping the glyph size used for rasterization.
7491 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7493 // This is not strictly accurate, as final used font sizes might not be the
7494 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7495 // altering the used size of the font actually used).
7496 // It also fails to consider how transforms might affect the device-font-size
7497 // that webrender uses (and clamps).
7498 // But it should be near enough for practical purposes; the limitations just
7499 // mean we might sometimes end up with webrender still applying some bitmap
7500 // scaling, or bail out when we didn't really need to.
7501 constexpr float kWebRenderFontSizeLimit = 320.0;
7502 f->EnsureTextRun(nsTextFrame::eInflated);
7503 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7504 if (textRun &&
7505 textRun->GetFontGroup()->GetStyle()->size > kWebRenderFontSizeLimit) {
7506 return false;
7509 gfx::Point deviceOffset =
7510 LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
7511 .ToUnknownPoint();
7513 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7514 // frames) lets us early reject a bunch of things.
7515 nsRect visible = mVisibleRect;
7517 // Add the "source rect" area from which the given shadows could intersect
7518 // with mVisibleRect, and which therefore needs to included in the paint
7519 // operation, to the `visible` rect that we will use to limit the bounds of
7520 // what we send to the renderer.
7521 auto addShadowSourceToVisible = [&](Span<const StyleSimpleShadow> aShadows) {
7522 for (const auto& shadow : aShadows) {
7523 nsRect sourceRect = mVisibleRect;
7524 // Negate the offsets, because we're looking for the "source" rect that
7525 // could cast a shadow into the visible rect, rather than a "target" area
7526 // onto which the visible rect would cast a shadow.
7527 sourceRect.MoveBy(-shadow.horizontal.ToAppUnits(),
7528 -shadow.vertical.ToAppUnits());
7529 // Inflate to account for the shadow blur.
7530 sourceRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
7531 shadow.blur.ToAppUnits(), appUnitsPerDevPixel));
7532 visible.OrWith(sourceRect);
7536 // Shadows can translate things back into view, so we enlarge the notional
7537 // "visible" rect to ensure we don't skip painting relevant parts that might
7538 // cast a shadow within the visible area.
7539 addShadowSourceToVisible(f->StyleText()->mTextShadow.AsSpan());
7541 // Similarly for shadows that may be cast by ::selection.
7542 if (f->IsSelected()) {
7543 nsTextPaintStyle textPaint(f);
7544 Span<const StyleSimpleShadow> shadows;
7545 f->GetSelectionTextShadow(SelectionType::eNormal, textPaint, &shadows);
7546 addShadowSourceToVisible(shadows);
7549 // Inflate a little extra to allow for potential antialiasing "blur".
7550 visible.Inflate(3 * appUnitsPerDevPixel);
7551 bounds = bounds.Intersect(visible);
7553 gfxContext* textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager,
7554 this, bounds, deviceOffset);
7556 LCPTextFrameHelper::MaybeUnionTextFrame(f, bounds - ToReferenceFrame());
7558 aBuilder.StartGroup(this);
7560 RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
7561 aBuilder.GetInheritedOpacity(), true);
7562 const bool result = textDrawer->GetTextDrawer()->Finish();
7564 if (result) {
7565 aBuilder.FinishGroup();
7566 } else {
7567 aBuilder.CancelGroup(true);
7570 return result;
7573 void nsDisplayText::RenderToContext(gfxContext* aCtx,
7574 nsDisplayListBuilder* aBuilder,
7575 const nsRect& aVisibleRect, float aOpacity,
7576 bool aIsRecording) {
7577 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7579 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7580 // antialiased pixels beyond the measured text extents.
7581 // This is temporary until we do this in the actual calculation of text
7582 // extents.
7583 auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
7584 LayoutDeviceRect extraVisible =
7585 LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D);
7586 extraVisible.Inflate(1);
7588 gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width,
7589 extraVisible.height);
7590 pixelVisible.Inflate(2);
7591 pixelVisible.RoundOut();
7593 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7594 if (!aBuilder->IsForGenerateGlyphMask() && !aIsRecording) {
7595 autoSaveClip.Clip(pixelVisible);
7598 NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
7599 NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
7601 gfxContextMatrixAutoSaveRestore matrixSR;
7603 nsPoint framePt = ToReferenceFrame();
7604 if (f->Style()->IsTextCombined()) {
7605 float scaleFactor = nsTextFrame::GetTextCombineScaleFactor(f);
7606 if (scaleFactor != 1.0f) {
7607 if (auto* textDrawer = aCtx->GetTextDrawer()) {
7608 // WebRender doesn't support scaling text like this yet
7609 textDrawer->FoundUnsupportedFeature();
7610 return;
7612 matrixSR.SetContext(aCtx);
7613 // Setup matrix to compress text for text-combine-upright if
7614 // necessary. This is done here because we want selection be
7615 // compressed at the same time as text.
7616 gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
7617 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7618 if (textRun && textRun->IsRightToLeft()) {
7619 pt.x += gfxFloat(f->GetSize().width) / A2D;
7621 gfxMatrix mat = aCtx->CurrentMatrixDouble()
7622 .PreTranslate(pt)
7623 .PreScale(scaleFactor, 1.0)
7624 .PreTranslate(-pt);
7625 aCtx->SetMatrixDouble(mat);
7628 nsTextFrame::PaintTextParams params(aCtx);
7629 params.framePt = gfx::Point(framePt.x, framePt.y);
7630 params.dirtyRect = extraVisible;
7632 if (aBuilder->IsForGenerateGlyphMask()) {
7633 params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
7634 } else {
7635 params.state = nsTextFrame::PaintTextParams::PaintText;
7638 f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
7639 f->IsSelected(), aOpacity);
7642 // This could go to nsDisplayListInvalidation.h, but
7643 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7644 // would produce circular dependencies.
7645 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
7646 public:
7647 nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
7648 : nsDisplayItemGenericGeometry(aItem, aBuilder),
7649 mVisIStartEdge(aItem->VisIStartEdge()),
7650 mVisIEndEdge(aItem->VisIEndEdge()) {
7651 nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
7652 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7653 mDecorations);
7657 * We store the computed text decorations here since they are
7658 * computed using style data from parent frames. Any changes to these
7659 * styles will only invalidate the parent frame and not this frame.
7661 nsTextFrame::TextDecorations mDecorations;
7662 nscoord mVisIStartEdge;
7663 nscoord mVisIEndEdge;
7666 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry(
7667 nsDisplayListBuilder* aBuilder) {
7668 return new nsDisplayTextGeometry(this, aBuilder);
7671 void nsDisplayText::ComputeInvalidationRegion(
7672 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7673 nsRegion* aInvalidRegion) const {
7674 const nsDisplayTextGeometry* geometry =
7675 static_cast<const nsDisplayTextGeometry*>(aGeometry);
7676 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7678 nsTextFrame::TextDecorations decorations;
7679 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7680 decorations);
7682 bool snap;
7683 const nsRect& newRect = geometry->mBounds;
7684 nsRect oldRect = GetBounds(aBuilder, &snap);
7685 if (decorations != geometry->mDecorations ||
7686 mVisIStartEdge != geometry->mVisIStartEdge ||
7687 mVisIEndEdge != geometry->mVisIEndEdge ||
7688 !oldRect.IsEqualInterior(newRect) ||
7689 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
7690 aInvalidRegion->Or(oldRect, newRect);
7694 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) {
7695 #ifdef DEBUG
7696 aStream << " (\"";
7698 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7699 nsCString buf;
7700 f->ToCString(buf);
7702 aStream << buf.get() << "\")";
7703 #endif
7706 nsDisplayEffectsBase::nsDisplayEffectsBase(
7707 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7708 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
7709 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
7710 aClearClipChain) {
7711 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7714 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
7715 nsIFrame* aFrame,
7716 nsDisplayList* aList)
7717 : nsDisplayWrapList(aBuilder, aFrame, aList) {
7718 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7721 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7722 bool* aSnap) const {
7723 *aSnap = false;
7724 return nsRegion();
7727 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder* aBuilder,
7728 const nsRect& aRect, HitTestState* aState,
7729 nsTArray<nsIFrame*>* aOutFrames) {
7730 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
7731 if (SVGIntegrationUtils::HitTestFrameForEffects(
7732 mFrame, rectCenter - ToReferenceFrame())) {
7733 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
7737 gfxRect nsDisplayEffectsBase::BBoxInUserSpace() const {
7738 return SVGUtils::GetBBox(mFrame);
7741 gfxPoint nsDisplayEffectsBase::UserSpaceOffset() const {
7742 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
7745 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7746 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7747 nsRegion* aInvalidRegion) const {
7748 const auto* geometry =
7749 static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
7750 bool snap;
7751 nsRect bounds = GetBounds(aBuilder, &snap);
7752 if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
7753 geometry->mUserSpaceOffset != UserSpaceOffset() ||
7754 !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
7755 // Filter and mask output can depend on the location of the frame's user
7756 // space and on the frame's BBox. We need to invalidate if either of these
7757 // change relative to the reference frame.
7758 // Invalidations from our inactive layer manager are not enough to catch
7759 // some of these cases because filters can produce output even if there's
7760 // nothing in the filter input.
7761 aInvalidRegion->Or(bounds, geometry->mBounds);
7765 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7766 if (mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
7767 ISVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame);
7768 if (!svgFrame) {
7769 return false;
7771 if (auto* svgElement = SVGElement::FromNode(mFrame->GetContent())) {
7772 // The SVG spec says only to draw filters if the element
7773 // has valid dimensions.
7774 return svgElement->HasValidDimensions();
7776 return false;
7779 return true;
7782 using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams;
7784 static void ComputeMaskGeometry(PaintFramesParams& aParams) {
7785 // Properties are added lazily and may have been removed by a restyle, so
7786 // make sure all applicable ones are set again.
7787 nsIFrame* firstFrame =
7788 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
7790 const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
7792 nsTArray<SVGMaskFrame*> maskFrames;
7793 // XXX check return value?
7794 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
7796 if (maskFrames.Length() == 0) {
7797 return;
7800 gfxContext& ctx = aParams.ctx;
7801 nsIFrame* frame = aParams.frame;
7803 nsPoint offsetToUserSpace =
7804 nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame);
7806 auto cssToDevScale = frame->PresContext()->CSSToDevPixelScale();
7807 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
7809 gfxPoint devPixelOffsetToUserSpace =
7810 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, appUnitsPerDevPixel);
7812 gfxContextMatrixAutoSaveRestore matSR(&ctx);
7813 ctx.SetMatrixDouble(
7814 ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace));
7816 // Convert boaderArea and dirtyRect to user space.
7817 nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
7818 nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
7820 // Union all mask layer rectangles in user space.
7821 LayoutDeviceRect maskInUserSpace;
7822 for (size_t i = 0; i < maskFrames.Length(); i++) {
7823 SVGMaskFrame* maskFrame = maskFrames[i];
7824 LayoutDeviceRect currentMaskSurfaceRect;
7826 if (maskFrame) {
7827 auto rect = maskFrame->GetMaskArea(aParams.frame);
7828 currentMaskSurfaceRect =
7829 CSSRect::FromUnknownRect(ToRect(rect)) * cssToDevScale;
7830 } else {
7831 nsCSSRendering::ImageLayerClipState clipState;
7832 nsCSSRendering::GetImageLayerClip(
7833 svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(),
7834 userSpaceBorderArea, userSpaceDirtyRect,
7835 /* aWillPaintBorder = */ false, appUnitsPerDevPixel, &clipState);
7836 currentMaskSurfaceRect = LayoutDeviceRect::FromUnknownRect(
7837 ToRect(clipState.mDirtyRectInDevPx));
7840 maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
7843 if (!maskInUserSpace.IsEmpty()) {
7844 aParams.maskRect = Some(maskInUserSpace);
7845 } else {
7846 aParams.maskRect = Nothing();
7850 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7851 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7852 const ActiveScrolledRoot* aActiveScrolledRoot, bool aWrapsBackdropFilter)
7853 : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
7854 mWrapsBackdropFilter(aWrapsBackdropFilter) {
7855 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
7857 nsPresContext* presContext = mFrame->PresContext();
7858 uint32_t flags =
7859 aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
7860 const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
7861 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7862 const auto& layer = svgReset->mMask.mLayers[i];
7863 if (!layer.mImage.IsResolved()) {
7864 continue;
7866 const nsRect& borderArea = mFrame->GetRectRelativeToSelf();
7867 // NOTE(emilio): We only care about the dest rect so we don't bother
7868 // computing a clip.
7869 bool isTransformedFixed = false;
7870 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
7871 presContext, aFrame, flags, borderArea, borderArea, layer,
7872 &isTransformedFixed);
7873 mDestRects.AppendElement(state.mDestArea);
7877 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) {
7878 // Do not merge items for box-decoration-break:clone elements,
7879 // since each box should have its own mask in that case.
7880 if (aFrame->StyleBorder()->mBoxDecorationBreak ==
7881 StyleBoxDecorationBreak::Clone) {
7882 return false;
7885 // Do not merge if either frame has a mask. Continuation frames should apply
7886 // the mask independently (just like nsDisplayBackgroundImage).
7887 if (aFrame->StyleSVGReset()->HasMask()) {
7888 return false;
7891 return true;
7894 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem* aItem) const {
7895 // Items for the same content element should be merged into a single
7896 // compositing group.
7897 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
7898 !HasSameContent(aItem)) {
7899 return false;
7902 return CanMergeDisplayMaskFrame(mFrame) &&
7903 CanMergeDisplayMaskFrame(aItem->Frame());
7906 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7907 if (!ValidateSVGFrame()) {
7908 return false;
7911 return SVGUtils::DetermineMaskUsage(mFrame, false).UsingMaskOrClipPath();
7914 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder,
7915 gfxContext* aMaskContext,
7916 bool aHandleOpacity,
7917 bool* aMaskPainted) {
7918 MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
7920 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7921 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7922 PaintFramesParams params(*aMaskContext, mFrame, mBounds, borderArea, aBuilder,
7923 aHandleOpacity, imgParams);
7924 ComputeMaskGeometry(params);
7925 bool maskIsComplete = false;
7926 bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete);
7927 if (aMaskPainted) {
7928 *aMaskPainted = painted;
7931 return maskIsComplete &&
7932 (imgParams.result == ImgDrawResult::SUCCESS ||
7933 imgParams.result == ImgDrawResult::SUCCESS_NOT_COMPLETE ||
7934 imgParams.result == ImgDrawResult::WRONG_SIZE);
7937 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7938 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7939 nsRegion* aInvalidRegion) const {
7940 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
7941 aInvalidRegion);
7943 const auto* geometry =
7944 static_cast<const nsDisplayMasksAndClipPathsGeometry*>(aGeometry);
7945 bool snap;
7946 nsRect bounds = GetBounds(aBuilder, &snap);
7948 if (mDestRects.Length() != geometry->mDestRects.Length()) {
7949 aInvalidRegion->Or(bounds, geometry->mBounds);
7950 } else {
7951 for (size_t i = 0; i < mDestRects.Length(); i++) {
7952 if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) {
7953 aInvalidRegion->Or(bounds, geometry->mBounds);
7954 break;
7960 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7961 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
7962 const std::function<void()>& aPaintChildren) {
7963 // Clip the drawing target by mVisibleRect, which contains the visible
7964 // region of the target frame and its out-of-flow and inflow descendants.
7965 Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx),
7966 mFrame->PresContext()->AppUnitsPerDevPixel());
7967 bounds.RoundOut();
7968 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7969 autoSaveClip.Clip(bounds);
7971 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7972 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7973 PaintFramesParams params(*aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
7974 borderArea, aBuilder, false, imgParams);
7976 ComputeMaskGeometry(params);
7978 SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren);
7981 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder* aBuilder,
7982 gfxContext* aCtx) {
7983 if (!IsValidMask()) {
7984 return;
7986 PaintWithContentsPaintCallback(aBuilder, aCtx, [&] {
7987 GetChildren()->Paint(aBuilder, aCtx,
7988 mFrame->PresContext()->AppUnitsPerDevPixel());
7992 static Maybe<wr::WrClipChainId> CreateSimpleClipRegion(
7993 const nsDisplayMasksAndClipPaths& aDisplayItem,
7994 wr::DisplayListBuilder& aBuilder) {
7995 nsIFrame* frame = aDisplayItem.Frame();
7996 const auto* style = frame->StyleSVGReset();
7997 MOZ_ASSERT(style->HasClipPath() || style->HasMask());
7998 if (!SVGUtils::DetermineMaskUsage(frame, false).IsSimpleClipShape()) {
7999 return Nothing();
8002 const auto& clipPath = style->mClipPath;
8003 const auto& shape = *clipPath.AsShape()._0;
8005 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8006 const nsRect refBox =
8007 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8009 wr::WrClipId clipId{};
8011 switch (shape.tag) {
8012 case StyleBasicShape::Tag::Rect: {
8013 const nsRect rect =
8014 ShapeUtils::ComputeInsetRect(shape.AsRect().rect, refBox) +
8015 aDisplayItem.ToReferenceFrame();
8017 nscoord radii[8] = {0};
8018 if (ShapeUtils::ComputeRectRadii(shape.AsRect().round, refBox, rect,
8019 radii)) {
8020 clipId = aBuilder.DefineRoundedRectClip(
8021 Nothing(),
8022 wr::ToComplexClipRegion(rect, radii, appUnitsPerDevPixel));
8023 } else {
8024 clipId = aBuilder.DefineRectClip(
8025 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8026 rect, appUnitsPerDevPixel)));
8029 break;
8031 case StyleBasicShape::Tag::Ellipse:
8032 case StyleBasicShape::Tag::Circle: {
8033 nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox);
8035 nsSize radii;
8036 if (shape.IsEllipse()) {
8037 radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox);
8038 } else {
8039 nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox);
8040 radii = {radius, radius};
8043 nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center -
8044 nsPoint(radii.width, radii.height),
8045 radii * 2);
8047 nscoord ellipseRadii[8];
8048 for (const auto corner : AllPhysicalHalfCorners()) {
8049 ellipseRadii[corner] =
8050 HalfCornerIsX(corner) ? radii.width : radii.height;
8053 clipId = aBuilder.DefineRoundedRectClip(
8054 Nothing(), wr::ToComplexClipRegion(ellipseRect, ellipseRadii,
8055 appUnitsPerDevPixel));
8057 break;
8059 default:
8060 // Please don't add more exceptions, try to find a way to define the clip
8061 // without using a mask image.
8063 // And if you _really really_ need to add an exception, add it to
8064 // SVGUtils::DetermineMaskUsage
8065 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8066 return Nothing();
8069 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8071 return Some(clipChainId);
8074 static void FillPolygonDataForDisplayItem(
8075 const nsDisplayMasksAndClipPaths& aDisplayItem,
8076 nsTArray<wr::LayoutPoint>& aPoints, wr::FillRule& aFillRule) {
8077 nsIFrame* frame = aDisplayItem.Frame();
8078 const auto* style = frame->StyleSVGReset();
8079 bool isPolygon = style->HasClipPath() && style->mClipPath.IsShape() &&
8080 style->mClipPath.AsShape()._0->IsPolygon();
8081 if (!isPolygon) {
8082 return;
8085 const auto& clipPath = style->mClipPath;
8086 const auto& shape = *clipPath.AsShape()._0;
8087 const nsRect refBox =
8088 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8090 // We only fill polygon data for polygons that are below a complexity
8091 // limit.
8092 nsTArray<nsPoint> vertices =
8093 ShapeUtils::ComputePolygonVertices(shape, refBox);
8094 if (vertices.Length() > wr::POLYGON_CLIP_VERTEX_MAX) {
8095 return;
8098 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8100 for (size_t i = 0; i < vertices.Length(); ++i) {
8101 wr::LayoutPoint point = wr::ToLayoutPoint(
8102 LayoutDevicePoint::FromAppUnits(vertices[i], appUnitsPerDevPixel));
8103 aPoints.AppendElement(point);
8106 aFillRule = (shape.AsPolygon().fill == StyleFillRule::Nonzero)
8107 ? wr::FillRule::Nonzero
8108 : wr::FillRule::Evenodd;
8111 static Maybe<wr::WrClipChainId> CreateWRClipPathAndMasks(
8112 nsDisplayMasksAndClipPaths* aDisplayItem, const LayoutDeviceRect& aBounds,
8113 wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder,
8114 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8115 nsDisplayListBuilder* aDisplayListBuilder) {
8116 if (auto clip = CreateSimpleClipRegion(*aDisplayItem, aBuilder)) {
8117 return clip;
8120 Maybe<wr::ImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(
8121 aDisplayItem, aBuilder, aResources, aSc, aDisplayListBuilder, aBounds);
8122 if (!mask) {
8123 return Nothing();
8126 // We couldn't create a simple clip region, but before we create an image
8127 // mask clip, see if we can get a polygon clip to add to it.
8128 nsTArray<wr::LayoutPoint> points;
8129 wr::FillRule fillRule = wr::FillRule::Nonzero;
8130 FillPolygonDataForDisplayItem(*aDisplayItem, points, fillRule);
8132 wr::WrClipId clipId =
8133 aBuilder.DefineImageMaskClip(mask.ref(), points, fillRule);
8135 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8137 return Some(clipChainId);
8140 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8141 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8142 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8143 nsDisplayListBuilder* aDisplayListBuilder) {
8144 bool snap;
8145 auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8146 nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
8147 LayoutDeviceRect bounds =
8148 LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel);
8150 Maybe<wr::WrClipChainId> clip = CreateWRClipPathAndMasks(
8151 this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder);
8153 float oldOpacity = aBuilder.GetInheritedOpacity();
8155 Maybe<StackingContextHelper> layer;
8156 const StackingContextHelper* sc = &aSc;
8157 if (clip) {
8158 // Create a new stacking context to attach the mask to, ensuring the mask is
8159 // applied to the aggregate, and not the individual elements.
8161 // The stacking context shouldn't have any offset.
8162 bounds.MoveTo(0, 0);
8164 Maybe<float> opacity =
8165 (SVGUtils::DetermineMaskUsage(mFrame, false).IsSimpleClipShape() &&
8166 aBuilder.GetInheritedOpacity() != 1.0f)
8167 ? Some(aBuilder.GetInheritedOpacity())
8168 : Nothing();
8170 wr::StackingContextParams params;
8171 params.clip = wr::WrStackingContextClip::ClipChain(clip->id);
8172 params.opacity = opacity.ptrOr(nullptr);
8173 if (mWrapsBackdropFilter) {
8174 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8176 layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params,
8177 bounds);
8178 sc = layer.ptr();
8181 aBuilder.SetInheritedOpacity(1.0f);
8182 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8183 aBuilder.SetInheritedClipChain(nullptr);
8184 CreateWebRenderCommandsNewClipListOption(aBuilder, aResources, *sc, aManager,
8185 aDisplayListBuilder, layer.isSome());
8186 aBuilder.SetInheritedOpacity(oldOpacity);
8187 aBuilder.SetInheritedClipChain(oldClipChain);
8189 return true;
8192 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8193 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
8194 if (const DisplayItemClip* clip =
8195 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
8196 return Some(clip->GetClipRect());
8198 // This item does not have a clip with respect to |aASR|. However, we
8199 // might still have finite bounds with respect to |aASR|. Check our
8200 // children.
8201 nsDisplayList* childList = GetSameCoordinateSystemChildren();
8202 if (childList) {
8203 return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR));
8205 #ifdef DEBUG
8206 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
8207 #endif
8208 return Nothing();
8211 #ifdef MOZ_DUMP_PAINTING
8212 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) {
8213 nsIFrame* firstFrame =
8214 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8215 bool first = true;
8216 aTo += " effects=(";
8217 SVGClipPathFrame* clipPathFrame;
8218 // XXX Check return value?
8219 SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
8220 if (clipPathFrame) {
8221 if (!first) {
8222 aTo += ", ";
8224 aTo += nsPrintfCString(
8225 "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
8226 first = false;
8227 } else if (mFrame->StyleSVGReset()->HasClipPath()) {
8228 if (!first) {
8229 aTo += ", ";
8231 aTo += "clip(basic-shape)";
8232 first = false;
8235 nsTArray<SVGMaskFrame*> masks;
8236 // XXX check return value?
8237 SVGObserverUtils::GetAndObserveMasks(firstFrame, &masks);
8238 if (!masks.IsEmpty() && masks[0]) {
8239 if (!first) {
8240 aTo += ", ";
8242 aTo += "mask";
8244 aTo += ")";
8246 #endif
8248 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8249 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8250 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8251 nsDisplayListBuilder* aDisplayListBuilder) {
8252 WrFiltersHolder wrFilters;
8253 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8254 auto filterChain = style.StyleEffects()->mBackdropFilters.AsSpan();
8255 bool initialized = true;
8256 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8257 wrFilters) &&
8258 !SVGIntegrationUtils::BuildWebRenderFilters(
8259 mFrame, filterChain, StyleFilterType::BackdropFilter, wrFilters,
8260 initialized)) {
8261 // TODO: If painting backdrop-filters on the content side is implemented,
8262 // consider returning false to fall back to that.
8263 wrFilters = {};
8266 if (!initialized) {
8267 wrFilters = {};
8270 nsCSSRendering::ImageLayerClipState clip;
8271 nsCSSRendering::GetImageLayerClip(
8272 style.StyleBackground()->BottomLayer(), mFrame, *style.StyleBorder(),
8273 mBackdropRect, mBackdropRect, false,
8274 mFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
8276 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
8277 mBackdropRect, mFrame->PresContext()->AppUnitsPerDevPixel());
8279 wr::ComplexClipRegion region =
8280 wr::ToComplexClipRegion(clip.mBGClipArea, clip.mRadii,
8281 mFrame->PresContext()->AppUnitsPerDevPixel());
8283 aBuilder.PushBackdropFilter(wr::ToLayoutRect(bounds), region,
8284 wrFilters.filters, wrFilters.filter_datas,
8285 !BackfaceIsHidden());
8287 wr::StackingContextParams params;
8288 params.clip =
8289 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8290 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8291 params);
8293 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8294 aDisplayListBuilder);
8295 return true;
8298 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder* aBuilder,
8299 gfxContext* aCtx) {
8300 // TODO: Implement backdrop filters
8301 GetChildren()->Paint(aBuilder, aCtx,
8302 mFrame->PresContext()->AppUnitsPerDevPixel());
8305 nsRect nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder* aBuilder,
8306 bool* aSnap) const {
8307 nsRect childBounds = nsDisplayWrapList::GetBounds(aBuilder, aSnap);
8309 *aSnap = false;
8311 return mBackdropRect.Union(childBounds);
8314 /* static */
8315 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder,
8316 nsIFrame* aFrame, nsDisplayList* aList,
8317 nsIFrame* aStyleFrame,
8318 bool aWrapsBackdropFilter)
8319 : nsDisplayEffectsBase(aBuilder, aFrame, aList),
8320 mStyle(aFrame == aStyleFrame ? nullptr : aStyleFrame->Style()),
8321 mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()),
8322 mWrapsBackdropFilter(aWrapsBackdropFilter) {
8323 MOZ_COUNT_CTOR(nsDisplayFilters);
8324 mVisibleRect = aBuilder->GetVisibleRect() +
8325 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
8328 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8329 PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) {
8330 GetChildren()->Paint(aBuilder, aContext,
8331 mFrame->PresContext()->AppUnitsPerDevPixel());
8335 void nsDisplayFilters::PaintWithContentsPaintCallback(
8336 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
8337 const std::function<void(gfxContext* aContext)>& aPaintChildren) {
8338 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
8339 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8340 PaintFramesParams params(*aCtx, mFrame, mVisibleRect, borderArea, aBuilder,
8341 false, imgParams);
8343 gfxPoint userSpaceToFrameSpaceOffset =
8344 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params);
8346 auto filterChain = mStyle ? mStyle->StyleEffects()->mFilters.AsSpan()
8347 : mFrame->StyleEffects()->mFilters.AsSpan();
8348 SVGIntegrationUtils::PaintFilter(
8349 params, filterChain,
8350 [&](gfxContext& aContext, imgDrawingParams&, const gfxMatrix*,
8351 const nsIntRect*) {
8352 gfxContextMatrixAutoSaveRestore autoSR(&aContext);
8353 aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(
8354 -userSpaceToFrameSpaceOffset));
8355 aPaintChildren(&aContext);
8359 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8360 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame);
8363 bool nsDisplayFilters::CreateWebRenderCommands(
8364 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8365 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8366 nsDisplayListBuilder* aDisplayListBuilder) {
8367 WrFiltersHolder wrFilters;
8368 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8369 auto filterChain = style.StyleEffects()->mFilters.AsSpan();
8370 bool initialized = true;
8371 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8372 wrFilters) &&
8373 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame, filterChain,
8374 StyleFilterType::Filter,
8375 wrFilters, initialized)) {
8376 if (mStyle) {
8377 // TODO(bug 1769223): Support fallback filters in the root code-path,
8378 // perhaps. For now treat it the same way as invalid filters.
8379 wrFilters = {};
8380 } else {
8381 // Draw using fallback.
8382 return false;
8386 if (!initialized) {
8387 // https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
8389 // If the filter references a non-existent object or the referenced object
8390 // is not a filter element, then the whole filter chain is ignored. No
8391 // filter is applied to the object.
8393 // Note that other engines have a weird discrepancy between SVG and HTML
8394 // content here, but the spec is clear.
8395 wrFilters = {};
8398 uint64_t clipChainId;
8399 if (wrFilters.post_filters_clip) {
8400 auto devPxRect = LayoutDeviceRect::FromAppUnits(
8401 wrFilters.post_filters_clip.value() + ToReferenceFrame(),
8402 mFrame->PresContext()->AppUnitsPerDevPixel());
8403 auto clipId =
8404 aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect));
8405 clipChainId = aBuilder.DefineClipChain({clipId}, true).id;
8406 } else {
8407 clipChainId = aBuilder.CurrentClipChainId();
8409 wr::WrStackingContextClip clip =
8410 wr::WrStackingContextClip::ClipChain(clipChainId);
8412 float opacity = aBuilder.GetInheritedOpacity();
8413 aBuilder.SetInheritedOpacity(1.0f);
8414 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8415 aBuilder.SetInheritedClipChain(nullptr);
8416 wr::StackingContextParams params;
8417 params.mFilters = std::move(wrFilters.filters);
8418 params.mFilterDatas = std::move(wrFilters.filter_datas);
8419 params.opacity = opacity != 1.0f ? &opacity : nullptr;
8420 params.clip = clip;
8421 if (mWrapsBackdropFilter) {
8422 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8424 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8425 params);
8427 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
8428 aManager, aDisplayListBuilder);
8429 aBuilder.SetInheritedOpacity(opacity);
8430 aBuilder.SetInheritedClipChain(oldClipChain);
8432 return true;
8435 #ifdef MOZ_DUMP_PAINTING
8436 void nsDisplayFilters::PrintEffects(nsACString& aTo) {
8437 nsIFrame* firstFrame =
8438 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8439 bool first = true;
8440 aTo += " effects=(";
8441 // We may exist for a mix of CSS filter functions and/or references to SVG
8442 // filters. If we have invalid references to SVG filters then we paint
8443 // nothing, but otherwise we will apply one or more filters.
8444 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) !=
8445 SVGObserverUtils::eHasRefsSomeInvalid) {
8446 if (!first) {
8447 aTo += ", ";
8449 aTo += "filter";
8451 aTo += ")";
8453 #endif
8455 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder,
8456 nsIFrame* aFrame, nsDisplayList* aList)
8457 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8458 MOZ_COUNT_CTOR(nsDisplaySVGWrapper);
8461 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8462 return !aBuilder->GetWidgetLayerManager();
8465 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8466 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8467 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8468 nsDisplayListBuilder* aDisplayListBuilder) {
8469 return CreateWebRenderCommandsNewClipListOption(
8470 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8473 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder* aBuilder,
8474 nsIFrame* aFrame,
8475 nsDisplayList* aList)
8476 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8477 MOZ_COUNT_CTOR(nsDisplayForeignObject);
8480 #ifdef NS_BUILD_REFCNT_LOGGING
8481 nsDisplayForeignObject::~nsDisplayForeignObject() {
8482 MOZ_COUNT_DTOR(nsDisplayForeignObject);
8484 #endif
8486 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8487 return !aBuilder->GetWidgetLayerManager();
8490 bool nsDisplayForeignObject::CreateWebRenderCommands(
8491 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8492 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8493 nsDisplayListBuilder* aDisplayListBuilder) {
8494 AutoRestore<bool> restoreDoGrouping(aManager->CommandBuilder().mDoGrouping);
8495 aManager->CommandBuilder().mDoGrouping = false;
8496 return CreateWebRenderCommandsNewClipListOption(
8497 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8500 void nsDisplayLink::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8501 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8502 aCtx->GetDrawTarget()->Link(
8503 mLinkSpec.get(), NSRectToRect(GetPaintRect(aBuilder, aCtx), appPerDev));
8506 void nsDisplayDestination::Paint(nsDisplayListBuilder* aBuilder,
8507 gfxContext* aCtx) {
8508 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8509 aCtx->GetDrawTarget()->Destination(
8510 mDestinationName.get(),
8511 NSPointToPoint(GetPaintRect(aBuilder, aCtx).TopLeft(), appPerDev));
8514 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8515 nsDisplayList* aOutResultList, nsIContent* aContent) {
8516 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
8517 // in content document order and SortByZOrder is a stable sort which
8518 // guarantees that boxes produced by the same element are placed together
8519 // in the sort. Consider a position:relative inline element that breaks
8520 // across lines and has absolutely positioned children; all the abs-pos
8521 // children should be z-ordered after all the boxes for the position:relative
8522 // element itself.
8523 PositionedDescendants()->SortByZOrder();
8525 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8526 // 1,2: backgrounds and borders
8527 aOutResultList->AppendToTop(BorderBackground());
8528 // 3: negative z-index children.
8529 for (auto* item : PositionedDescendants()->TakeItems()) {
8530 if (item->ZIndex() < 0) {
8531 aOutResultList->AppendToTop(item);
8532 } else {
8533 PositionedDescendants()->AppendToTop(item);
8537 // 4: block backgrounds
8538 aOutResultList->AppendToTop(BlockBorderBackgrounds());
8539 // 5: floats
8540 aOutResultList->AppendToTop(Floats());
8541 // 7: general content
8542 aOutResultList->AppendToTop(Content());
8543 // 7.5: outlines, in content tree order. We need to sort by content order
8544 // because an element with outline that breaks and has children with outline
8545 // might have placed child outline items between its own outline items.
8546 // The element's outline items need to all come before any child outline
8547 // items.
8548 if (aContent) {
8549 Outlines()->SortByContentOrder(aContent);
8551 aOutResultList->AppendToTop(Outlines());
8552 // 8, 9: non-negative z-index children
8553 aOutResultList->AppendToTop(PositionedDescendants());
8556 uint32_t PaintTelemetry::sPaintLevel = 0;
8558 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8559 // Don't record nested paints.
8560 if (sPaintLevel++ > 0) {
8561 return;
8564 mStart = TimeStamp::Now();
8567 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8568 MOZ_ASSERT(sPaintLevel != 0);
8569 if (--sPaintLevel > 0) {
8570 return;
8573 // If we're in multi-process mode, don't include paint times for the parent
8574 // process.
8575 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8576 return;
8579 // Record the total time.
8580 mozilla::glean::gfx_content::paint_time.AccumulateRawDuration(
8581 TimeStamp::Now() - mStart);
8584 static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
8585 if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
8586 return aFrame;
8589 if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
8590 !aFrame->GetPrevInFlow()) {
8591 return aFrame->GetPlaceholderFrame();
8594 return aFrame;
8597 static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
8598 nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
8599 MOZ_ASSERT(f);
8600 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
8603 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8604 nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
8605 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
8606 const bool aIsTransformed)
8607 : mBuilder(aBuilder),
8608 mPrevFrame(aBuilder->mCurrentFrame),
8609 mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
8610 mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
8611 mPrevAdditionalOffset(aBuilder->mAdditionalOffset),
8612 mPrevVisibleRect(aBuilder->mVisibleRect),
8613 mPrevDirtyRect(aBuilder->mDirtyRect),
8614 mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo),
8615 mPrevAncestorHasApzAwareEventHandler(
8616 aBuilder->mAncestorHasApzAwareEventHandler),
8617 mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
8618 mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
8619 if (aIsTransformed) {
8620 aBuilder->mCurrentOffsetToReferenceFrame =
8621 aBuilder->AdditionalOffset().refOr(nsPoint());
8622 aBuilder->mCurrentReferenceFrame = aForChild;
8623 } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
8624 aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
8625 } else {
8626 aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
8627 aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
8630 // If aForChild is being visited from a frame other than it's ancestor frame,
8631 // mInInvalidSubtree will need to be recalculated the slow way.
8632 if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) {
8633 aBuilder->mInInvalidSubtree =
8634 aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
8635 } else {
8636 aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
8639 aBuilder->mCurrentFrame = aForChild;
8640 aBuilder->mVisibleRect = aVisibleRect;
8641 aBuilder->mDirtyRect =
8642 aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
8645 } // namespace mozilla