no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / layout / painting / nsDisplayList.cpp
blob39001537b684affe3bebf6c79e05ce6fa3b4aee6
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 state->mCaretFrame = [&]() -> nsIFrame* {
1108 RefPtr<nsCaret> caret = state->mPresShell->GetCaret();
1109 nsIFrame* currentCaret = caret->GetPaintGeometry(&mCaretRect);
1111 if (auto* oldCaret = caret->GetLastPaintedFrame();
1112 oldCaret && oldCaret != currentCaret) {
1113 // Make sure to rebuild the old caret if it has changed.
1114 MarkFrameForDisplay(oldCaret, aReferenceFrame);
1117 if (!currentCaret) {
1118 return nullptr;
1121 // Check if the display root for the caret matches the display root that
1122 // we're painting, and only use it if it matches. Likely we only need this
1123 // for popup.
1124 if (nsLayoutUtils::GetDisplayRootFrame(currentCaret) !=
1125 nsLayoutUtils::GetDisplayRootFrame(aReferenceFrame)) {
1126 return nullptr;
1129 // Caret frames add visual area to their frame, but we don't update the
1130 // overflow area. Use flags to make sure we build display items for that
1131 // frame instead.
1132 MOZ_ASSERT(currentCaret->PresShell() == state->mPresShell);
1133 MarkFrameForDisplay(currentCaret, aReferenceFrame);
1134 caret->SetLastPaintedFrame(currentCaret);
1135 return currentCaret;
1136 }();
1139 // A non-blank paint is a paint that does not just contain the canvas
1140 // background.
1141 static bool DisplayListIsNonBlank(nsDisplayList* aList) {
1142 for (nsDisplayItem* i : *aList) {
1143 switch (i->GetType()) {
1144 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
1145 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
1146 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
1147 continue;
1148 case DisplayItemType::TYPE_SOLID_COLOR:
1149 case DisplayItemType::TYPE_BACKGROUND:
1150 case DisplayItemType::TYPE_BACKGROUND_COLOR:
1151 if (i->Frame()->IsCanvasFrame()) {
1152 continue;
1154 return true;
1155 default:
1156 return true;
1159 return false;
1162 // A contentful paint is a paint that does contains DOM content (text,
1163 // images, non-blank canvases, SVG): "First Contentful Paint entry
1164 // contains a DOMHighResTimeStamp reporting the time when the browser
1165 // first rendered any text, image (including background images),
1166 // non-white canvas or SVG. This excludes any content of iframes, but
1167 // includes text with pending webfonts. This is the first time users
1168 // could start consuming page content."
1169 static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
1170 nsDisplayList* aList) {
1171 for (nsDisplayItem* i : *aList) {
1172 DisplayItemType type = i->GetType();
1173 nsDisplayList* children = i->GetChildren();
1175 switch (type) {
1176 case DisplayItemType::TYPE_SUBDOCUMENT: // iframes are ignored
1177 break;
1178 // CANVASes check if they may have been modified (as a stand-in
1179 // actually tracking all modifications)
1180 default:
1181 if (i->IsContentful()) {
1182 bool dummy;
1183 nsRect bound = i->GetBounds(aBuilder, &dummy);
1184 if (!bound.IsEmpty()) {
1185 return true;
1188 if (children) {
1189 if (DisplayListIsContentful(aBuilder, children)) {
1190 return true;
1193 break;
1196 return false;
1199 void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
1200 nsDisplayList* aPaintedContents) {
1201 NS_ASSERTION(
1202 CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
1203 "Presshell mismatch");
1205 if (mIsPaintingToWindow && aPaintedContents) {
1206 nsPresContext* pc = aReferenceFrame->PresContext();
1207 if (!pc->HadNonBlankPaint()) {
1208 if (!CurrentPresShellState()->mIsBackgroundOnly &&
1209 DisplayListIsNonBlank(aPaintedContents)) {
1210 pc->NotifyNonBlankPaint();
1213 nsRootPresContext* rootPresContext = pc->GetRootPresContext();
1214 if (!pc->HasStoppedGeneratingLCP() && rootPresContext) {
1215 if (!CurrentPresShellState()->mIsBackgroundOnly) {
1216 if (pc->HasEverBuiltInvisibleText() ||
1217 DisplayListIsContentful(this, aPaintedContents)) {
1218 pc->NotifyContentfulPaint();
1224 ResetMarkedFramesForDisplayList(aReferenceFrame);
1225 mPresShellStates.RemoveLastElement();
1227 if (!mPresShellStates.IsEmpty()) {
1228 nsPresContext* pc = CurrentPresContext();
1229 nsIDocShell* docShell = pc->GetDocShell();
1230 if (docShell) {
1231 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1233 mIsInChromePresContext = pc->IsChrome();
1234 } else {
1235 for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
1236 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
1238 mFramesMarkedForDisplayIfVisible.SetLength(0);
1242 void nsDisplayListBuilder::FreeClipChains() {
1243 // Iterate the clip chains from newest to oldest (forward
1244 // iteration), so that we destroy descendants first which
1245 // will drop the ref count on their ancestors.
1246 DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;
1248 while (*indirect) {
1249 if (!(*indirect)->mRefCount) {
1250 DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;
1252 mClipDeduplicator.erase(*indirect);
1253 (*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
1254 Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);
1256 *indirect = next;
1257 } else {
1258 indirect = &(*indirect)->mNextClipChainToDestroy;
1263 void nsDisplayListBuilder::FreeTemporaryItems() {
1264 for (nsDisplayItem* i : mTemporaryItems) {
1265 // Temporary display items are not added to the frames.
1266 MOZ_ASSERT(i->Frame());
1267 i->RemoveFrame(i->Frame());
1268 i->Destroy(this);
1271 mTemporaryItems.Clear();
1274 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1275 const nsIFrame* aReferenceFrame) {
1276 // Unmark and pop off the frames marked for display in this pres shell.
1277 uint32_t firstFrameForShell =
1278 CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1279 for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
1280 ++i) {
1281 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
1283 mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1285 firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
1286 for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
1287 mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
1289 mFramesWithOOFData.SetLength(firstFrameForShell);
1292 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1293 CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
1296 void nsDisplayListBuilder::MarkFramesForDisplayList(
1297 nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
1298 nsRect visibleRect = GetVisibleRect();
1299 nsRect dirtyRect = GetDirtyRect();
1301 // If we are entering content that is fixed to the RCD-RSF, we are
1302 // crossing the async zoom container boundary, and need to convert from
1303 // visual to layout coordinates.
1304 if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
1305 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1306 viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
1307 if (viewportFrame->PresShell()->GetRootScrollFrame()) {
1308 #ifdef DEBUG
1309 for (nsIFrame* f : aFrames) {
1310 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
1312 #endif
1313 visibleRect = ViewportUtils::VisualToLayout(visibleRect,
1314 viewportFrame->PresShell());
1315 dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
1316 viewportFrame->PresShell());
1318 #ifdef DEBUG
1319 else {
1320 // This is an edge case that should only happen if we are in a
1321 // document with a XUL root element so that it does not have a root
1322 // scroll frame but it has fixed pos content and all of the frames in
1323 // aFrames are that fixed pos content.
1324 for (nsIFrame* f : aFrames) {
1325 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
1326 f->GetParent() == aDirtyFrame &&
1327 f->StyleDisplay()->mPosition ==
1328 StylePositionProperty::Fixed);
1330 // There's no root scroll frame so there can't be any zooming or async
1331 // panning so we don't need to adjust the visible and dirty rects.
1333 #endif
1337 bool markedFrames = false;
1338 for (nsIFrame* e : aFrames) {
1339 // Skip the AccessibleCaret frame when building no caret.
1340 if (!IsBuildingCaret()) {
1341 nsIContent* content = e->GetContent();
1342 if (content && content->IsInNativeAnonymousSubtree() &&
1343 content->IsElement()) {
1344 const nsAttrValue* classes = content->AsElement()->GetClasses();
1345 if (classes &&
1346 classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) {
1347 continue;
1351 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
1352 markedFrames = true;
1356 if (markedFrames) {
1357 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1358 // to objects on the stack, so we need to clone the chain.
1359 const DisplayItemClipChain* clipChain =
1360 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1361 const DisplayItemClipChain* combinedClipChain =
1362 mClipState.GetCurrentCombinedClipChain(this);
1363 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1365 OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
1366 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1367 visibleRect, dirtyRect);
1368 aDirtyFrame->SetProperty(
1369 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
1370 mFramesWithOOFData.AppendElement(aDirtyFrame);
1373 if (!aDirtyFrame->GetParent()) {
1374 // This is the viewport frame of aDirtyFrame's presshell.
1375 // Store the current display data so that it can be used for fixed
1376 // background images.
1377 NS_ASSERTION(
1378 CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
1379 "Presshell mismatch");
1380 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
1381 "already traversed this presshell's root frame?");
1383 const DisplayItemClipChain* clipChain =
1384 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1385 const DisplayItemClipChain* combinedClipChain =
1386 mClipState.GetCurrentCombinedClipChain(this);
1387 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1388 CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
1389 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1390 GetVisibleRect(), GetDirtyRect());
1395 * Mark all preserve-3d children with
1396 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1397 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1398 * dirty rect for preserve-3d children.
1400 * @param aDirtyFrame is the frame to mark children extending context.
1402 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1403 nsIFrame* aDirtyFrame) {
1404 for (const auto& childList : aDirtyFrame->ChildLists()) {
1405 for (nsIFrame* child : childList.mList) {
1406 if (child->Combines3DTransformWithAncestors()) {
1407 MarkFrameForDisplay(child, aDirtyFrame);
1410 if (child->IsBlockWrapper()) {
1411 // Mark preserve-3d frames inside the block wrapper.
1412 MarkPreserve3DFramesForDisplayList(child);
1418 ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1419 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame) {
1420 RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
1421 aParent, aScrollableFrame, IsRetainingDisplayList());
1422 mActiveScrolledRoots.AppendElement(asr);
1423 return asr;
1426 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1427 const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
1428 const DisplayItemClipChain* aParent) {
1429 MOZ_DIAGNOSTIC_ASSERT(!(aParent && aParent->mOnStack));
1430 void* p = Allocate(sizeof(DisplayItemClipChain),
1431 DisplayListArenaObjectId::CLIPCHAIN);
1432 DisplayItemClipChain* c = new (KnownNotNull, p)
1433 DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy);
1434 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
1435 c->mOnStack = false;
1436 #endif
1437 auto result = mClipDeduplicator.insert(c);
1438 if (!result.second) {
1439 // An equivalent clip chain item was already created, so let's return that
1440 // instead. Destroy the one we just created.
1441 // Note that this can cause clip chains from different coordinate systems to
1442 // collapse into the same clip chain object, because clip chains do not keep
1443 // track of the reference frame that they were created in.
1444 c->DisplayItemClipChain::~DisplayItemClipChain();
1445 Destroy(DisplayListArenaObjectId::CLIPCHAIN, c);
1446 return *(result.first);
1448 mFirstClipChainToDestroy = c;
1449 return c;
1452 struct ClipChainItem {
1453 DisplayItemClip clip;
1454 const ActiveScrolledRoot* asr;
1457 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
1458 const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
1459 for (const ActiveScrolledRoot* asr =
1460 ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
1461 asr; asr = asr->mParent) {
1462 if (aOne == aTwo) {
1463 return aOne;
1465 if (aOne->mASR == asr) {
1466 aOne = aOne->mParent;
1468 if (aTwo->mASR == asr) {
1469 aTwo = aTwo->mParent;
1471 if (!aOne) {
1472 return aTwo;
1474 if (!aTwo) {
1475 return aOne;
1478 return nullptr;
1481 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1482 const DisplayItemClipChain* aAncestor,
1483 const DisplayItemClipChain* aLeafClip1,
1484 const DisplayItemClipChain* aLeafClip2) {
1485 AutoTArray<ClipChainItem, 8> intersectedClips;
1487 const DisplayItemClipChain* clip1 = aLeafClip1;
1488 const DisplayItemClipChain* clip2 = aLeafClip2;
1490 const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
1491 clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);
1493 // Build up the intersection from the leaf to the root and put it into
1494 // intersectedClips. The loop below will convert intersectedClips into an
1495 // actual DisplayItemClipChain.
1496 // (We need to do this in two passes because we need the parent clip in order
1497 // to create the DisplayItemClipChain object, but the parent clip has not
1498 // been created at that point.)
1499 while (!aAncestor || asr != aAncestor->mASR) {
1500 if (clip1 && clip1->mASR == asr) {
1501 if (clip2 && clip2->mASR == asr) {
1502 DisplayItemClip intersection = clip1->mClip;
1503 intersection.IntersectWith(clip2->mClip);
1504 intersectedClips.AppendElement(ClipChainItem{intersection, asr});
1505 clip2 = clip2->mParent;
1506 } else {
1507 intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
1509 clip1 = clip1->mParent;
1510 } else if (clip2 && clip2->mASR == asr) {
1511 intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
1512 clip2 = clip2->mParent;
1514 if (!asr) {
1515 MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
1516 break;
1518 asr = asr->mParent;
1521 // Convert intersectedClips into a DisplayItemClipChain.
1522 const DisplayItemClipChain* parentSC = aAncestor;
1523 for (auto& sc : Reversed(intersectedClips)) {
1524 parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
1526 return parentSC;
1529 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1530 const DisplayItemClipChain* aLeafClip1,
1531 const DisplayItemClipChain* aLeafClip2) {
1532 // aLeafClip2 might be a reference to a clip on the stack. We need to make
1533 // sure that CreateClipChainIntersection will allocate the actual intersected
1534 // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1535 // we supply nullptr as the common ancestor so that
1536 // CreateClipChainIntersection clones the whole chain.
1537 const DisplayItemClipChain* ancestorClip =
1538 aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
1539 : nullptr;
1541 return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2);
1544 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
1545 const DisplayItemClipChain* aClipChain) {
1546 return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
1549 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
1550 const nsIFrame* aFrame, nsPoint* aOffset) const {
1551 auto MaybeApplyAdditionalOffset = [&]() {
1552 if (auto offset = AdditionalOffset()) {
1553 *aOffset += *offset;
1557 if (aFrame == mCurrentFrame) {
1558 if (aOffset) {
1559 *aOffset = mCurrentOffsetToReferenceFrame;
1561 return mCurrentReferenceFrame;
1564 for (const nsIFrame* f = aFrame; f;
1565 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
1566 if (f == mReferenceFrame || f->IsTransformed()) {
1567 if (aOffset) {
1568 *aOffset = aFrame->GetOffsetToCrossDoc(f);
1569 MaybeApplyAdditionalOffset();
1571 return f;
1575 if (aOffset) {
1576 *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1577 MaybeApplyAdditionalOffset();
1580 return mReferenceFrame;
1583 // Sticky frames are active if their nearest scrollable frame is also active.
1584 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
1585 nsIFrame* aFrame) {
1586 MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
1587 StylePositionProperty::Sticky);
1589 StickyScrollContainer* stickyScrollContainer =
1590 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
1591 return stickyScrollContainer &&
1592 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1595 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
1596 nsIFrame** aParent) {
1597 if (aFrame == mReferenceFrame) {
1598 return true;
1601 if (!IsPaintingToWindow()) {
1602 if (aParent) {
1603 *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1605 return false;
1608 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1609 if (!parent) {
1610 return true;
1612 *aParent = parent;
1614 if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
1615 IsStickyFrameActive(this, aFrame)) {
1616 return true;
1619 if (aFrame->IsTransformed()) {
1620 if (EffectCompositor::HasAnimationsForCompositor(
1621 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
1622 return true;
1626 LayoutFrameType parentType = parent->Type();
1627 if (parentType == LayoutFrameType::Scroll ||
1628 parentType == LayoutFrameType::ListControl) {
1629 nsIScrollableFrame* sf = do_QueryFrame(parent);
1630 if (sf->GetScrolledFrame() == aFrame) {
1631 MOZ_ASSERT(!aFrame->IsTransformed());
1632 return sf->IsMaybeAsynchronouslyScrolled();
1636 return false;
1639 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1640 nsIFrame* aFrame) {
1641 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1642 RootReferenceFrame(), aFrame));
1643 nsIFrame* cursor = aFrame;
1644 while (cursor != RootReferenceFrame()) {
1645 nsIFrame* next;
1646 if (IsAnimatedGeometryRoot(cursor, &next)) {
1647 return cursor;
1649 cursor = next;
1651 return cursor;
1654 static nsRect ApplyAllClipNonRoundedIntersection(
1655 const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
1656 nsRect result = aRect;
1657 while (aClipChain) {
1658 result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
1659 aClipChain = aClipChain->mParent;
1661 return result;
1664 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
1665 if (!mWindowDraggingAllowed || !IsForPainting()) {
1666 return;
1669 const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
1670 if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
1671 // This frame has the default value and doesn't influence the window
1672 // dragging region.
1673 return;
1676 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;
1678 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1679 nsIFrame* referenceFrame =
1680 const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1682 if (IsInTransform()) {
1683 // Only support 2d rectilinear transforms. Transform support is needed for
1684 // the horizontal flip transform that's applied to the urlbar textbox in
1685 // RTL mode - it should be able to exclude itself from the draggable region.
1686 referenceFrameToRootReferenceFrame =
1687 ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
1688 nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
1689 RelativeTo{mReferenceFrame})
1690 .GetMatrix());
1691 Matrix referenceFrameToRootReferenceFrame2d;
1692 if (!referenceFrameToRootReferenceFrame.Is2D(
1693 &referenceFrameToRootReferenceFrame2d) ||
1694 !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1695 return;
1697 } else {
1698 MOZ_ASSERT(referenceFrame == mReferenceFrame,
1699 "referenceFrameToRootReferenceFrame needs to be adjusted");
1702 // We do some basic visibility checking on the frame's border box here.
1703 // We intersect it both with the current dirty rect and with the current
1704 // clip. Either one is just a conservative approximation on its own, but
1705 // their intersection luckily works well enough for our purposes, so that
1706 // we don't have to do full-blown visibility computations.
1707 // The most important case we need to handle is the scrolled-off tab:
1708 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1709 // should not be allowed to interfere with the window dragging region. Using
1710 // just the current DisplayItemClip is not enough to cover this case
1711 // completely because clips are reset while building stacking context
1712 // contents, so for example we'd fail to clip frames that have a clip path
1713 // applied to them. But the current dirty rect doesn't get reset in that
1714 // case, so we use it to make this case work.
1715 nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
1716 borderBox += ToReferenceFrame(aFrame);
1717 const DisplayItemClipChain* clip =
1718 ClipState().GetCurrentCombinedClipChain(this);
1719 borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
1720 if (borderBox.IsEmpty()) {
1721 return;
1724 LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
1725 borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1727 LayoutDeviceRect transformedDevPixelBorderBox =
1728 TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1729 transformedDevPixelBorderBox.Round();
1730 LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1732 if (!transformedDevPixelBorderBox.ToIntRect(
1733 &transformedDevPixelBorderBoxInt)) {
1734 return;
1737 LayoutDeviceIntRegion& region =
1738 styleUI->mWindowDragging == StyleWindowDragging::Drag
1739 ? mWindowDraggingRegion
1740 : mWindowNoDraggingRegion;
1742 if (!IsRetainingDisplayList()) {
1743 region.OrWith(transformedDevPixelBorderBoxInt);
1744 return;
1747 gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
1748 if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
1749 mRetainedWindowDraggingRegion.Add(aFrame, rect);
1750 } else {
1751 mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
1755 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
1756 LayoutDeviceIntRegion result;
1757 if (!IsRetainingDisplayList()) {
1758 result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
1759 return result;
1762 LayoutDeviceIntRegion dragRegion =
1763 mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();
1765 LayoutDeviceIntRegion noDragRegion =
1766 mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();
1768 result.Sub(dragRegion, noDragRegion);
1769 return result;
1772 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1773 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes);
1774 aSizes.mLayoutRetainedDisplayListSize +=
1775 aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get());
1778 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1779 mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);
1781 size_t n = 0;
1782 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
1783 n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1784 n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1785 n += mEffectsUpdates.ShallowSizeOfExcludingThis(mallocSizeOf);
1786 n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1787 n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1788 n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
1789 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1791 aSizes.mLayoutRetainedDisplayListSize += n;
1794 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1795 for (nsDisplayItem* item : *this) {
1796 item->AddSizeOfExcludingThis(aSizes);
1797 if (RetainedDisplayList* children = item->GetChildren()) {
1798 children->AddSizeOfExcludingThis(aSizes);
1802 size_t n = 0;
1804 n += mDAG.mDirectPredecessorList.ShallowSizeOfExcludingThis(
1805 aSizes.mState.mMallocSizeOf);
1806 n += mDAG.mNodesInfo.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1807 n += mOldItems.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1809 aSizes.mLayoutRetainedDisplayListSize += n;
1812 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1813 MallocSizeOf aMallocSizeOf) const {
1814 size_t n = 0;
1815 n += mFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
1816 for (const auto& frame : mFrames) {
1817 const UniquePtr<WeakFrame>& weakFrame = frame.mWeakFrame;
1818 n += aMallocSizeOf(weakFrame.get());
1820 n += mRects.ShallowSizeOfExcludingThis(aMallocSizeOf);
1821 return n;
1825 * Removes modified frames and rects from this WeakFrameRegion.
1827 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1828 MOZ_ASSERT(mFrames.Length() == mRects.Length());
1830 uint32_t i = 0;
1831 uint32_t length = mFrames.Length();
1833 while (i < length) {
1834 auto& wrapper = mFrames[i];
1836 if (!wrapper.mWeakFrame->IsAlive() ||
1837 AnyContentAncestorModified(wrapper.mWeakFrame->GetFrame())) {
1838 // To avoid multiple O(n) shifts in the array, move the last element of
1839 // the array to the current position and decrease the array length.
1840 mFrameSet.Remove(wrapper.mFrame);
1841 mFrames[i] = std::move(mFrames[length - 1]);
1842 mRects[i] = std::move(mRects[length - 1]);
1843 length--;
1844 } else {
1845 i++;
1849 mFrames.TruncateLength(length);
1850 mRects.TruncateLength(length);
1853 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1854 mRetainedWindowDraggingRegion.RemoveModifiedFramesAndRects();
1855 mRetainedWindowNoDraggingRegion.RemoveModifiedFramesAndRects();
1856 mRetainedWindowOpaqueRegion.RemoveModifiedFramesAndRects();
1859 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1860 mRetainedWindowDraggingRegion.Clear();
1861 mRetainedWindowNoDraggingRegion.Clear();
1862 mRetainedWindowOpaqueRegion.Clear();
1865 const uint32_t gWillChangeAreaMultiplier = 3;
1866 static uint32_t GetLayerizationCost(const nsSize& aSize) {
1867 // There's significant overhead for each layer created from Gecko
1868 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1869 // Therefore we set a minimum cost threshold of a 64x64 area.
1870 const int minBudgetCost = 64 * 64;
1872 const uint32_t budgetCost = std::max(
1873 minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
1874 nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
1876 return budgetCost;
1879 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
1880 const nsSize& aSize) {
1881 MOZ_ASSERT(IsForPainting());
1883 if (aFrame->MayHaveWillChangeBudget()) {
1884 // The frame is already in the will-change budget.
1885 return true;
1888 const nsPresContext* presContext = aFrame->PresContext();
1889 const nsRect area = presContext->GetVisibleArea();
1890 const uint32_t budgetLimit =
1891 nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1892 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1893 const uint32_t cost = GetLayerizationCost(aSize);
1895 DocumentWillChangeBudget& documentBudget =
1896 mDocumentWillChangeBudgets.LookupOrInsert(presContext);
1898 const bool onBudget =
1899 (documentBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
1901 if (onBudget) {
1902 documentBudget += cost;
1903 mFrameWillChangeBudgets.InsertOrUpdate(
1904 aFrame, FrameWillChangeBudget(presContext, cost));
1905 aFrame->SetMayHaveWillChangeBudget(true);
1908 return onBudget;
1911 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
1912 const nsSize& aSize) {
1913 if (!IsForPainting()) {
1914 // If this nsDisplayListBuilder is not for painting, the layerization should
1915 // not matter. Do the simple thing and return false.
1916 return false;
1919 const bool onBudget = AddToWillChangeBudget(aFrame, aSize);
1920 if (onBudget) {
1921 return true;
1924 auto* pc = aFrame->PresContext();
1925 auto* doc = pc->Document();
1926 if (!doc->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget)) {
1927 AutoTArray<nsString, 2> params;
1928 params.AppendElement()->AppendInt(gWillChangeAreaMultiplier);
1930 nsRect area = pc->GetVisibleArea();
1931 uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1932 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1933 params.AppendElement()->AppendInt(budgetLimit);
1935 doc->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget, false, params);
1938 return false;
1941 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame* aFrame) {
1942 MOZ_ASSERT(IsForPainting());
1944 if (!aFrame->MayHaveWillChangeBudget()) {
1945 return;
1948 aFrame->SetMayHaveWillChangeBudget(false);
1949 RemoveFromWillChangeBudgets(aFrame);
1952 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame* aFrame) {
1953 if (auto entry = mFrameWillChangeBudgets.Lookup(aFrame)) {
1954 const FrameWillChangeBudget& frameBudget = entry.Data();
1956 auto documentBudget =
1957 mDocumentWillChangeBudgets.Lookup(frameBudget.mPresContext);
1959 if (documentBudget) {
1960 *documentBudget -= frameBudget.mUsage;
1963 entry.Remove();
1967 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1968 mFrameWillChangeBudgets.Clear();
1969 mDocumentWillChangeBudgets.Clear();
1972 void nsDisplayListBuilder::EnterSVGEffectsContents(
1973 nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) {
1974 MOZ_ASSERT(aHoistedItemsStorage);
1975 if (mSVGEffectsFrames.IsEmpty()) {
1976 MOZ_ASSERT(!mScrollInfoItemsForHoisting);
1977 mScrollInfoItemsForHoisting = aHoistedItemsStorage;
1979 mSVGEffectsFrames.AppendElement(aEffectsFrame);
1982 void nsDisplayListBuilder::ExitSVGEffectsContents() {
1983 MOZ_ASSERT(!mSVGEffectsFrames.IsEmpty());
1984 mSVGEffectsFrames.RemoveLastElement();
1985 MOZ_ASSERT(mScrollInfoItemsForHoisting);
1986 if (mSVGEffectsFrames.IsEmpty()) {
1987 mScrollInfoItemsForHoisting = nullptr;
1991 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
1993 * Note: if changing the conditions under which scroll info layers
1994 * are created, make a corresponding change to
1995 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
1997 for (nsIFrame* frame : mSVGEffectsFrames) {
1998 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame)) {
1999 return true;
2002 return false;
2005 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
2006 nsDisplayScrollInfoLayer* aScrollInfoItem) {
2007 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2008 MOZ_ASSERT(mScrollInfoItemsForHoisting);
2009 mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
2012 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2013 nsIFrame* aFrame, nsDisplayList* aList) {
2014 MOZ_ASSERT(aFrame);
2015 MOZ_ASSERT(aList);
2017 if (!BuildCompositorHitTestInfo()) {
2018 return;
2021 const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
2022 if (info != CompositorHitTestInvisibleToHit) {
2023 aList->AppendNewToTop<nsDisplayCompositorHitTestInfo>(this, aFrame);
2027 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem* aItem) {
2028 mReuseableItems.Insert(aItem);
2031 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem* aItem) {
2032 MOZ_ASSERT(aItem->IsReusedItem());
2033 mReuseableItems.Remove(aItem);
2036 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2037 const size_t total = mReuseableItems.Count();
2039 size_t reused = 0;
2040 for (auto* item : mReuseableItems) {
2041 if (item->IsReusedItem()) {
2042 reused++;
2043 item->SetReusable();
2044 } else {
2045 item->Destroy(this);
2049 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
2050 mReuseableItems.Clear();
2053 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
2054 const auto* previous = mCurrentContainerASR;
2055 const auto* asr = aItem->GetActiveScrolledRoot();
2056 mCurrentContainerASR =
2057 ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
2059 if (previous != mCurrentContainerASR) {
2060 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
2061 mCurrentContainerASR);
2064 aItem->SetReusedItem();
2067 void nsDisplayListSet::CopyTo(const nsDisplayListSet& aDestination) const {
2068 for (size_t i = 0; i < mLists.size(); ++i) {
2069 auto* from = mLists[i];
2070 auto* to = aDestination.mLists[i];
2072 from->CopyTo(to);
2076 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
2077 aDestination.BorderBackground()->AppendToTop(BorderBackground());
2078 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2079 aDestination.Floats()->AppendToTop(Floats());
2080 aDestination.Content()->AppendToTop(Content());
2081 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
2082 aDestination.Outlines()->AppendToTop(Outlines());
2085 nsRect nsDisplayList::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2086 nsRect bounds;
2087 for (nsDisplayItem* i : *this) {
2088 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
2090 return bounds;
2093 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR(
2094 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
2095 nsRect* aBuildingRect) const {
2096 nsRect bounds;
2097 for (nsDisplayItem* i : *this) {
2098 nsRect r = i->GetClippedBounds(aBuilder);
2099 if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
2100 if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) {
2101 r = clip.ref();
2104 if (aBuildingRect) {
2105 aBuildingRect->UnionRect(*aBuildingRect, i->GetBuildingRect());
2107 bounds.UnionRect(bounds, r);
2109 return bounds;
2112 nsRect nsDisplayList::GetBuildingRect() const {
2113 nsRect result;
2114 for (nsDisplayItem* i : *this) {
2115 result.UnionRect(result, i->GetBuildingRect());
2117 return result;
2120 WindowRenderer* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView** aView) {
2121 if (aView) {
2122 *aView = RootReferenceFrame()->GetView();
2124 if (RootReferenceFrame() !=
2125 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2126 return nullptr;
2128 nsIWidget* window = RootReferenceFrame()->GetNearestWidget();
2129 if (window) {
2130 return window->GetWindowRenderer();
2132 return nullptr;
2135 WebRenderLayerManager* nsDisplayListBuilder::GetWidgetLayerManager(
2136 nsView** aView) {
2137 WindowRenderer* renderer = GetWidgetWindowRenderer();
2138 if (renderer) {
2139 return renderer->AsWebRender();
2141 return nullptr;
2144 void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2145 int32_t aAppUnitsPerDevPixel) {
2146 FlattenedDisplayListIterator iter(aBuilder, this);
2147 while (iter.HasNext()) {
2148 nsPaintedDisplayItem* item = iter.GetNextItem()->AsPaintedDisplayItem();
2149 if (!item) {
2150 continue;
2153 nsRect visible = item->GetClippedBounds(aBuilder);
2154 visible = visible.Intersect(item->GetPaintRect(aBuilder, aCtx));
2155 if (visible.IsEmpty()) {
2156 continue;
2159 DisplayItemClip currentClip = item->GetClip();
2160 if (currentClip.HasClip()) {
2161 aCtx->Save();
2162 if (currentClip.IsRectClippedByRoundedCorner(visible)) {
2163 currentClip.ApplyTo(aCtx, aAppUnitsPerDevPixel);
2164 } else {
2165 currentClip.ApplyRectTo(aCtx, aAppUnitsPerDevPixel);
2168 aCtx->NewPath();
2170 item->Paint(aBuilder, aCtx);
2172 if (currentClip.HasClip()) {
2173 aCtx->Restore();
2179 * We paint by executing a layer manager transaction, constructing a
2180 * single layer representing the display list, and then making it the
2181 * root of the layer manager, drawing into the PaintedLayers.
2183 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2184 uint32_t aFlags,
2185 Maybe<double> aDisplayListBuildTime) {
2186 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS);
2188 RefPtr<WebRenderLayerManager> layerManager;
2189 WindowRenderer* renderer = nullptr;
2190 bool widgetTransaction = false;
2191 bool doBeginTransaction = true;
2192 nsView* view = nullptr;
2193 if (aFlags & PAINT_USE_WIDGET_LAYERS) {
2194 renderer = aBuilder->GetWidgetWindowRenderer(&view);
2195 if (renderer) {
2196 // The fallback renderer doesn't retain any content, so it's
2197 // not meaningful to use it when drawing to an external context.
2198 if (aCtx && renderer->AsFallback()) {
2199 MOZ_ASSERT(!(aFlags & PAINT_EXISTING_TRANSACTION));
2200 renderer = nullptr;
2201 } else {
2202 layerManager = renderer->AsWebRender();
2203 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
2204 widgetTransaction = true;
2209 nsIFrame* frame = aBuilder->RootReferenceFrame();
2210 nsPresContext* presContext = frame->PresContext();
2211 PresShell* presShell = presContext->PresShell();
2212 Document* document = presShell->GetDocument();
2214 ScopeExit g([&]() {
2215 #ifdef DEBUG
2216 MOZ_ASSERT(!layerManager || !layerManager->GetTarget());
2217 #endif
2219 // For layers-free mode, we check the invalidation state bits in the
2220 // EndTransaction. So we clear the invalidation state bits after
2221 // EndTransaction.
2222 if (widgetTransaction ||
2223 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2224 // but they still need the invalidation state bits cleared in order for
2225 // invalidation for CSS/SMIL animation to work properly.
2226 (document && document->IsBeingUsedAsImage())) {
2227 DL_LOGD("Clearing invalidation state bits");
2228 frame->ClearInvalidationStateBits();
2232 if (!renderer) {
2233 if (!aCtx) {
2234 NS_WARNING("Nowhere to paint into");
2235 return;
2237 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(false);
2238 Paint(aBuilder, aCtx, presContext->AppUnitsPerDevPixel());
2240 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2241 return;
2244 if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) {
2245 MOZ_ASSERT(layerManager);
2246 if (doBeginTransaction) {
2247 if (aCtx) {
2248 if (!layerManager->BeginTransactionWithTarget(aCtx, nsCString())) {
2249 return;
2251 } else {
2252 if (!layerManager->BeginTransaction(nsCString())) {
2253 return;
2258 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(true);
2259 layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
2261 bool sent = false;
2262 if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
2263 MOZ_ASSERT(!aCtx);
2264 sent = layerManager->EndEmptyTransaction();
2267 if (!sent) {
2268 auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
2270 nsIDocShell* docShell = presContext->GetDocShell();
2271 WrFiltersHolder wrFilters;
2272 gfx::Matrix5x4* colorMatrix =
2273 nsDocShell::Cast(docShell)->GetColorMatrix();
2274 if (colorMatrix) {
2275 wrFilters.filters.AppendElement(
2276 wr::FilterOp::ColorMatrix(colorMatrix->components));
2279 wrManager->EndTransactionWithoutLayer(this, aBuilder,
2280 std::move(wrFilters), nullptr,
2281 aDisplayListBuildTime.valueOr(0.0));
2284 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2285 if (presContext->RefreshDriver()->HasScheduleFlush()) {
2286 presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2287 frame->GetRect());
2290 return;
2293 FallbackRenderer* fallback = renderer->AsFallback();
2294 MOZ_ASSERT(fallback);
2296 if (doBeginTransaction) {
2297 MOZ_ASSERT(!aCtx);
2298 if (!fallback->BeginTransaction()) {
2299 return;
2303 bool temp = aBuilder->SetIsCompositingCheap(renderer->IsCompositingCheap());
2304 fallback->EndTransactionWithList(aBuilder, this,
2305 presContext->AppUnitsPerDevPixel(),
2306 WindowRenderer::END_DEFAULT);
2308 aBuilder->SetIsCompositingCheap(temp);
2311 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
2312 for (auto* item : TakeItems()) {
2313 item->Destroy(aBuilder);
2317 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) {
2318 return aFrame->Style()->PointerEvents() != StylePointerEvents::None;
2321 // A list of frames, and their z depth. Used for sorting
2322 // the results of hit testing.
2323 struct FramesWithDepth {
2324 explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {}
2326 bool operator<(const FramesWithDepth& aOther) const {
2327 if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
2328 // We want to sort so that the shallowest item (highest depth value) is
2329 // first
2330 return mDepth > aOther.mDepth;
2332 return false;
2334 bool operator==(const FramesWithDepth& aOther) const {
2335 return this == &aOther;
2338 float mDepth;
2339 nsTArray<nsIFrame*> mFrames;
2342 // Sort the frames by depth and then moves all the contained frames to the
2343 // destination
2344 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource,
2345 nsTArray<nsIFrame*>* aDest) {
2346 if (aSource.IsEmpty()) {
2347 return;
2349 aSource.StableSort();
2350 uint32_t length = aSource.Length();
2351 for (uint32_t i = 0; i < length; i++) {
2352 aDest->AppendElements(std::move(aSource[i].mFrames));
2354 aSource.Clear();
2357 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2358 nsDisplayItem::HitTestState* aState,
2359 nsTArray<nsIFrame*>* aOutFrames) const {
2360 nsDisplayItem* item;
2362 if (aState->mInPreserves3D) {
2363 // Collect leaves of the current 3D rendering context.
2364 for (nsDisplayItem* item : *this) {
2365 auto itemType = item->GetType();
2366 if (itemType != DisplayItemType::TYPE_TRANSFORM ||
2367 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2368 item->HitTest(aBuilder, aRect, aState, aOutFrames);
2369 } else {
2370 // One of leaves in the current 3D rendering context.
2371 aState->mItemBuffer.AppendElement(item);
2374 return;
2377 int32_t itemBufferStart = aState->mItemBuffer.Length();
2378 for (nsDisplayItem* item : *this) {
2379 aState->mItemBuffer.AppendElement(item);
2382 AutoTArray<FramesWithDepth, 16> temp;
2383 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart;
2384 --i) {
2385 // Pop element off the end of the buffer. We want to shorten the buffer
2386 // so that recursive calls to HitTest have more buffer space.
2387 item = aState->mItemBuffer[i];
2388 aState->mItemBuffer.SetLength(i);
2390 bool snap;
2391 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
2392 auto itemType = item->GetType();
2393 bool same3DContext =
2394 (itemType == DisplayItemType::TYPE_TRANSFORM &&
2395 static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
2396 (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
2397 item->Frame()->Extend3DContext());
2398 if (same3DContext &&
2399 (itemType != DisplayItemType::TYPE_TRANSFORM ||
2400 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
2401 if (!item->GetClip().MayIntersect(aRect)) {
2402 continue;
2404 AutoTArray<nsIFrame*, 1> neverUsed;
2405 // Start gathering leaves of the 3D rendering context, and
2406 // append leaves at the end of mItemBuffer. Leaves are
2407 // processed at following iterations.
2408 aState->mInPreserves3D = true;
2409 item->HitTest(aBuilder, aRect, aState, &neverUsed);
2410 aState->mInPreserves3D = false;
2411 i = aState->mItemBuffer.Length();
2412 continue;
2414 if (same3DContext || item->GetClip().MayIntersect(r)) {
2415 AutoTArray<nsIFrame*, 16> outFrames;
2416 item->HitTest(aBuilder, aRect, aState, &outFrames);
2418 // For 3d transforms with preserve-3d we add hit frames into the temp list
2419 // so we can sort them later, otherwise we add them directly to the output
2420 // list.
2421 nsTArray<nsIFrame*>* writeFrames = aOutFrames;
2422 if (item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
2423 static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2424 if (outFrames.Length()) {
2425 nsDisplayTransform* transform =
2426 static_cast<nsDisplayTransform*>(item);
2427 nsPoint point = aRect.TopLeft();
2428 // A 1x1 rect means a point, otherwise use the center of the rect
2429 if (aRect.width != 1 || aRect.height != 1) {
2430 point = aRect.Center();
2432 temp.AppendElement(
2433 FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
2434 writeFrames = &temp[temp.Length() - 1].mFrames;
2436 } else {
2437 // We may have just finished a run of consecutive preserve-3d
2438 // transforms, so flush these into the destination array before
2439 // processing our frame list.
2440 FlushFramesArray(temp, aOutFrames);
2443 for (uint32_t j = 0; j < outFrames.Length(); j++) {
2444 nsIFrame* f = outFrames.ElementAt(j);
2445 // Filter out some frames depending on the type of hittest
2446 // we are doing. For visibility tests, pass through all frames.
2447 // For pointer tests, only pass through frames that are styled
2448 // to receive pointer events.
2449 if (aBuilder->HitTestIsForVisibility() ||
2450 IsFrameReceivingPointerEvents(f)) {
2451 writeFrames->AppendElement(f);
2455 if (aBuilder->HitTestIsForVisibility()) {
2456 aState->mHitOccludingItem = [&] {
2457 if (aState->mHitOccludingItem) {
2458 // We already hit something before.
2459 return true;
2461 if (aState->mCurrentOpacity == 1.0f &&
2462 item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) {
2463 // An opaque item always occludes everything. Note that we need to
2464 // check wrapping opacity and such as well.
2465 return true;
2467 float threshold = aBuilder->VisibilityThreshold();
2468 if (threshold == 1.0f) {
2469 return false;
2471 float itemOpacity = [&] {
2472 switch (item->GetType()) {
2473 case DisplayItemType::TYPE_OPACITY:
2474 return static_cast<nsDisplayOpacity*>(item)->GetOpacity();
2475 case DisplayItemType::TYPE_BACKGROUND_COLOR:
2476 return static_cast<nsDisplayBackgroundColor*>(item)
2477 ->GetOpacity();
2478 default:
2479 // Be conservative and assume it won't occlude other items.
2480 return 0.0f;
2482 }();
2483 return itemOpacity * aState->mCurrentOpacity >= threshold;
2484 }();
2486 if (aState->mHitOccludingItem) {
2487 // We're exiting early, so pop the remaining items off the buffer.
2488 aState->mItemBuffer.TruncateLength(itemBufferStart);
2489 break;
2494 // Clear any remaining preserve-3d transforms.
2495 FlushFramesArray(temp, aOutFrames);
2496 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
2497 "How did we forget to pop some elements?");
2500 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, Document* aDoc) {
2501 nsIFrame* f = aItem->Frame();
2502 while (f) {
2503 nsPresContext* pc = f->PresContext();
2504 if (pc->Document() == aDoc) {
2505 return f->GetContent();
2507 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(
2508 pc->PresShell()->GetRootFrame());
2510 return nullptr;
2513 struct ZSortItem {
2514 nsDisplayItem* item;
2515 int32_t zIndex;
2517 explicit ZSortItem(nsDisplayItem* aItem)
2518 : item(aItem), zIndex(aItem->ZIndex()) {}
2520 operator nsDisplayItem*() { return item; }
2523 struct ZOrderComparator {
2524 bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const {
2525 // Note that we can't just take the difference of the two
2526 // z-indices here, because that might overflow a 32-bit int.
2527 return aLeft.zIndex < aRight.zIndex;
2531 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); }
2533 struct ContentComparator {
2534 nsIContent* mCommonAncestor;
2536 explicit ContentComparator(nsIContent* aCommonAncestor)
2537 : mCommonAncestor(aCommonAncestor) {}
2539 bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const {
2540 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2541 // subdocument of commonAncestor, because display items for subdocuments
2542 // have been mixed into the same list. Ensure that we're looking at content
2543 // in commonAncestor's document.
2544 Document* commonAncestorDoc = mCommonAncestor->OwnerDoc();
2545 nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc);
2546 nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc);
2547 if (!content1 || !content2) {
2548 NS_ERROR("Document trees are mixed up!");
2549 // Something weird going on
2550 return true;
2552 return nsContentUtils::CompareTreePosition<TreeKind::Flat>(
2553 content1, content2, mCommonAncestor) < 0;
2557 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) {
2558 Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor));
2561 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2562 static_assert(sizeof(nsDisplayItem) <= 176, "nsDisplayItem has grown");
2563 #endif
2565 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2566 : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
2568 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2569 const ActiveScrolledRoot* aActiveScrolledRoot)
2570 : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) {
2571 MOZ_COUNT_CTOR(nsDisplayItem);
2572 MOZ_ASSERT(mFrame);
2573 if (aBuilder->IsRetainingDisplayList()) {
2574 mFrame->AddDisplayItem(this);
2577 aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
2578 NS_ASSERTION(
2579 aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
2580 "visible rect not set");
2582 mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2584 // The visible rect is for mCurrentFrame, so we have to use
2585 // mCurrentOffsetToReferenceFrame
2586 nsRect visible = aBuilder->GetVisibleRect() +
2587 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
2588 SetBuildingRect(visible);
2590 const nsStyleDisplay* disp = mFrame->StyleDisplay();
2591 if (mFrame->BackfaceIsHidden(disp)) {
2592 mItemFlags += ItemFlag::BackfaceHidden;
2594 if (mFrame->Combines3DTransformWithAncestors()) {
2595 mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
2599 void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
2601 bool nsDisplayItem::HasDeletedFrame() const {
2602 bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) ||
2603 (GetType() == DisplayItemType::TYPE_REMOTE &&
2604 !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader());
2605 MOZ_ASSERT(retval || mFrame);
2606 return retval;
2609 /* static */
2610 bool nsDisplayItem::ForceActiveLayers() {
2611 return StaticPrefs::layers_force_active();
2614 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); }
2616 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
2617 bool aStore) {
2618 mClipChain = aClipChain;
2621 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
2622 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2623 if (const DisplayItemClip* clip =
2624 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
2625 return Some(clip->GetClipRect());
2627 #ifdef DEBUG
2628 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
2629 #endif
2630 return Nothing();
2633 const DisplayItemClip& nsDisplayItem::GetClip() const {
2634 const DisplayItemClip* clip =
2635 DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot);
2636 return clip ? *clip : DisplayItemClip::NoClip();
2639 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
2640 const DisplayItemClipChain* aOther,
2641 bool aStore) {
2642 if (!aOther || mClipChain == aOther) {
2643 return;
2646 // aOther might be a reference to a clip on the stack. We need to make sure
2647 // that CreateClipChainIntersection will allocate the actual intersected
2648 // clip in the builder's arena, so for the mClipChain == nullptr case,
2649 // we supply nullptr as the common ancestor so that
2650 // CreateClipChainIntersection clones the whole chain.
2651 const DisplayItemClipChain* ancestorClip =
2652 mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther)
2653 : nullptr;
2655 SetClipChain(
2656 aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther),
2657 aStore);
2660 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2661 bool snap;
2662 nsRect r = GetBounds(aBuilder, &snap);
2663 return GetClip().ApplyNonRoundedIntersection(r);
2666 nsDisplayContainer::nsDisplayContainer(
2667 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2668 const ActiveScrolledRoot* aActiveScrolledRoot, nsDisplayList* aList)
2669 : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
2670 mChildren(aBuilder) {
2671 MOZ_COUNT_CTOR(nsDisplayContainer);
2672 mChildren.AppendToTop(aList);
2673 UpdateBounds(aBuilder);
2675 // Clear and store the clip chain set by nsDisplayItem constructor.
2676 nsDisplayItem::SetClipChain(nullptr, true);
2679 nsRect nsDisplayItem::GetPaintRect(nsDisplayListBuilder* aBuilder,
2680 gfxContext* aCtx) {
2681 bool dummy;
2682 nsRect result = GetBounds(aBuilder, &dummy);
2683 if (aCtx) {
2684 result.IntersectRect(result,
2685 nsLayoutUtils::RoundGfxRectToAppRect(
2686 aCtx->GetClipExtents(),
2687 mFrame->PresContext()->AppUnitsPerDevPixel()));
2689 return result;
2692 bool nsDisplayContainer::CreateWebRenderCommands(
2693 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2694 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2695 nsDisplayListBuilder* aDisplayListBuilder) {
2696 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2697 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
2698 false);
2699 return true;
2702 nsRect nsDisplayContainer::GetBounds(nsDisplayListBuilder* aBuilder,
2703 bool* aSnap) const {
2704 *aSnap = false;
2705 return mBounds;
2708 nsRect nsDisplayContainer::GetComponentAlphaBounds(
2709 nsDisplayListBuilder* aBuilder) const {
2710 return mChildren.GetComponentAlphaBounds(aBuilder);
2713 static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2714 nsDisplayList* aList,
2715 const nsRect& aListBounds) {
2716 return aList->GetOpaqueRegion(aBuilder);
2719 nsRegion nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2720 bool* aSnap) const {
2721 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
2722 GetBounds(aBuilder, aSnap));
2725 Maybe<nsRect> nsDisplayContainer::GetClipWithRespectToASR(
2726 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2727 // Our children should have finite bounds with respect to |aASR|.
2728 if (aASR == mActiveScrolledRoot) {
2729 return Some(mBounds);
2732 return Some(mChildren.GetClippedBoundsWithRespectToASR(aBuilder, aASR));
2735 void nsDisplayContainer::HitTest(nsDisplayListBuilder* aBuilder,
2736 const nsRect& aRect, HitTestState* aState,
2737 nsTArray<nsIFrame*>* aOutFrames) {
2738 mChildren.HitTest(aBuilder, aRect, aState, aOutFrames);
2741 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder* aBuilder) {
2742 // Container item bounds are expected to be clipped.
2743 mBounds =
2744 mChildren.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
2747 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder,
2748 bool* aSnap) const {
2749 *aSnap = true;
2750 return mBounds;
2753 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
2754 gfxContext* aCtx) {
2755 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2756 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2757 Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx),
2758 appUnitsPerDevPixel, *drawTarget);
2759 drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
2762 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) {
2763 aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
2764 << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
2765 << ")";
2768 bool nsDisplaySolidColor::CreateWebRenderCommands(
2769 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2770 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2771 nsDisplayListBuilder* aDisplayListBuilder) {
2772 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
2773 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
2774 wr::LayoutRect r = wr::ToLayoutRect(bounds);
2775 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, mIsCheckerboardBackground,
2776 wr::ToColorF(ToDeviceColor(mColor)));
2778 return true;
2781 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
2782 bool* aSnap) const {
2783 *aSnap = true;
2784 return mRegion.GetBounds();
2787 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
2788 gfxContext* aCtx) {
2789 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2790 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2791 ColorPattern color(ToDeviceColor(mColor));
2792 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2793 Rect rect =
2794 NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
2795 drawTarget->FillRect(rect, color);
2799 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) {
2800 aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255)
2801 << "," << int(mColor.b * 255) << "," << mColor.a << ")";
2804 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2805 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2806 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2807 nsDisplayListBuilder* aDisplayListBuilder) {
2808 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2809 nsRect rect = iter.Get();
2810 LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
2811 rect, mFrame->PresContext()->AppUnitsPerDevPixel());
2812 wr::LayoutRect r = wr::ToLayoutRect(layerRects);
2813 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
2814 wr::ToColorF(ToDeviceColor(mColor)));
2817 return true;
2820 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder,
2821 nsDisplayItem* aItem, nsIFrame* aFrame,
2822 nsITheme::ThemeGeometryType aType) {
2823 if (aBuilder->IsInChromeDocumentOrPopup()) {
2824 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2825 bool preservesAxisAlignedRectangles = false;
2826 nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(
2827 aFrame, aFrame->GetRectRelativeToSelf(), displayRoot,
2828 &preservesAxisAlignedRectangles);
2829 if (preservesAxisAlignedRectangles) {
2830 aBuilder->RegisterThemeGeometry(
2831 aType, aItem,
2832 LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels(
2833 aFrame->PresContext()->AppUnitsPerDevPixel())));
2838 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2839 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
2840 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
2841 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
2842 nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
2843 nsRect rootRect = rootFrame->GetRectRelativeToSelf();
2844 if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) ==
2845 nsLayoutUtils::TRANSFORM_SUCCEEDED) {
2846 return Some(rootRect + aBuilder->ToReferenceFrame(aFrame));
2848 return Nothing();
2851 /* static */ nsDisplayBackgroundImage::InitData
2852 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder,
2853 nsIFrame* aFrame, uint16_t aLayer,
2854 const nsRect& aBackgroundRect,
2855 const ComputedStyle* aBackgroundStyle) {
2856 nsPresContext* presContext = aFrame->PresContext();
2857 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2858 const nsStyleImageLayers::Layer& layer =
2859 aBackgroundStyle->StyleBackground()->mImage.mLayers[aLayer];
2861 bool isTransformedFixed;
2862 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
2863 presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer,
2864 &isTransformedFixed);
2866 // background-attachment:fixed is treated as background-attachment:scroll
2867 // if it's affected by a transform.
2868 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2869 bool shouldTreatAsFixed =
2870 layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2871 !isTransformedFixed;
2873 bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsNone();
2874 bool isRasterImage = state.mImageRenderer.IsRasterImage();
2875 nsCOMPtr<imgIContainer> image;
2876 if (isRasterImage) {
2877 image = state.mImageRenderer.GetImage();
2879 return InitData{aBuilder, aBackgroundStyle, image,
2880 aBackgroundRect, state.mFillArea, state.mDestArea,
2881 aLayer, isRasterImage, shouldFixToViewport};
2884 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2885 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aInitData,
2886 nsIFrame* aFrameForBounds)
2887 : nsPaintedDisplayItem(aBuilder, aFrame),
2888 mBackgroundStyle(aInitData.backgroundStyle),
2889 mImage(aInitData.image),
2890 mDependentFrame(nullptr),
2891 mBackgroundRect(aInitData.backgroundRect),
2892 mFillRect(aInitData.fillArea),
2893 mDestRect(aInitData.destArea),
2894 mLayer(aInitData.layer),
2895 mIsRasterImage(aInitData.isRasterImage),
2896 mShouldFixToViewport(aInitData.shouldFixToViewport) {
2897 MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
2898 #ifdef DEBUG
2899 if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) {
2900 // If this changes, then you also need to adjust css::ImageLoader to
2901 // invalidate mFrame as needed.
2902 MOZ_ASSERT(mFrame->IsCanvasFrame() || mFrame->IsTablePart());
2904 #endif
2906 mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
2907 if (mShouldFixToViewport) {
2908 // Expand the item's visible rect to cover the entire bounds, limited to the
2909 // viewport rect. This is necessary because the background's clip can move
2910 // asynchronously.
2911 if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
2912 aInitData.builder, mFrame)) {
2913 SetBuildingRect(mBounds.Intersect(*viewportRect));
2918 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2919 MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
2920 if (mDependentFrame) {
2921 mDependentFrame->RemoveDisplayItem(this);
2925 static void SetBackgroundClipRegion(
2926 DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame,
2927 const nsStyleImageLayers::Layer& aLayer, const nsRect& aBackgroundRect,
2928 bool aWillPaintBorder) {
2929 nsCSSRendering::ImageLayerClipState clip;
2930 nsCSSRendering::GetImageLayerClip(
2931 aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect,
2932 aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
2934 if (clip.mHasAdditionalBGClipArea) {
2935 aClipState.ClipContentDescendants(
2936 clip.mAdditionalBGClipArea, clip.mBGClipArea,
2937 clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2938 } else {
2939 aClipState.ClipContentDescendants(
2940 clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2945 * This is used for the find bar highlighter overlay. It's only accessible
2946 * through the AnonymousContent API, so it's not exposed to general web pages.
2948 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
2949 nsIFrame* aFrame,
2950 const nsRect& aBackgroundRect,
2951 nsDisplayList* aList, nscolor aColor) {
2952 nsIContent* content = aFrame->GetContent();
2953 if (!content) {
2954 return false;
2957 void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
2958 if (!cutoutRegion) {
2959 return false;
2962 if (NS_GET_A(aColor) == 0) {
2963 return true;
2966 nsRegion region;
2967 region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
2968 region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
2969 aList->AppendNewToTop<nsDisplaySolidColorRegion>(aBuilder, aFrame, region,
2970 aColor);
2972 return true;
2975 enum class TableType : uint8_t {
2976 Table,
2977 TableCol,
2978 TableColGroup,
2979 TableRow,
2980 TableRowGroup,
2981 TableCell,
2983 MAX,
2986 enum class TableTypeBits : uint8_t { Count = 3 };
2988 static_assert(static_cast<uint8_t>(TableType::MAX) <
2989 (1 << (static_cast<uint8_t>(TableTypeBits::Count) + 1)),
2990 "TableType cannot fit with TableTypeBits::Count");
2991 TableType GetTableTypeFromFrame(nsIFrame* aFrame);
2993 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex,
2994 const TableType aType) {
2995 const uint32_t key = (aIndex << static_cast<uint8_t>(TableTypeBits::Count)) |
2996 static_cast<uint8_t>(aType);
2998 return static_cast<uint16_t>(key);
3001 static nsDisplayBackgroundImage* CreateBackgroundImage(
3002 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3003 const nsDisplayBackgroundImage::InitData& aBgData) {
3004 const auto index = aBgData.layer;
3006 if (aSecondaryFrame) {
3007 const auto tableType = GetTableTypeFromFrame(aFrame);
3008 const uint16_t tableItemIndex = CalculateTablePerFrameKey(index, tableType);
3010 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundImage>(
3011 aBuilder, aSecondaryFrame, tableItemIndex, aBgData, aFrame);
3014 return MakeDisplayItemWithIndex<nsDisplayBackgroundImage>(aBuilder, aFrame,
3015 index, aBgData);
3018 static nsDisplayThemedBackground* CreateThemedBackground(
3019 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3020 const nsRect& aBgRect) {
3021 if (aSecondaryFrame) {
3022 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3023 return MakeDisplayItemWithIndex<nsDisplayTableThemedBackground>(
3024 aBuilder, aSecondaryFrame, index, aBgRect, aFrame);
3027 return MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, aBgRect);
3030 static nsDisplayBackgroundColor* CreateBackgroundColor(
3031 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3032 nsRect& aBgRect, const ComputedStyle* aBgSC, nscolor aColor) {
3033 if (aSecondaryFrame) {
3034 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3035 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>(
3036 aBuilder, aSecondaryFrame, index, aBgRect, aBgSC, aColor, aFrame);
3039 return MakeDisplayItem<nsDisplayBackgroundColor>(aBuilder, aFrame, aBgRect,
3040 aBgSC, aColor);
3043 static void DealWithWindowsAppearanceHacks(nsIFrame* aFrame,
3044 nsDisplayListBuilder* aBuilder) {
3045 const auto& disp = *aFrame->StyleDisplay();
3047 // We use default appearance rather than effective appearance because we want
3048 // to handle when titlebar buttons that have appearance: none.
3049 const auto defaultAppearance = disp.mDefaultAppearance;
3050 if (MOZ_LIKELY(defaultAppearance == StyleAppearance::None)) {
3051 return;
3054 if (auto type = disp.GetWindowButtonType()) {
3055 if (auto* widget = aFrame->GetNearestWidget()) {
3056 auto rect = LayoutDevicePixel::FromAppUnitsToNearest(
3057 nsRect(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize()),
3058 aFrame->PresContext()->AppUnitsPerDevPixel());
3059 widget->SetWindowButtonRect(*type, rect);
3064 /*static*/
3065 AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3066 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3067 const nsRect& aBackgroundRect, nsDisplayList* aList,
3068 bool aAllowWillPaintBorderOptimization, const nsRect& aBackgroundOriginRect,
3069 nsIFrame* aSecondaryReferenceFrame,
3070 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
3071 aAutoBuildingDisplayList) {
3072 MOZ_ASSERT(!aFrame->IsCanvasFrame(),
3073 "We don't expect propagated canvas backgrounds here");
3074 #ifdef DEBUG
3076 nsIFrame* bgFrame = nsCSSRendering::FindBackgroundFrame(aFrame);
3077 MOZ_ASSERT(
3078 !bgFrame || bgFrame == aFrame,
3079 "Should only suppress backgrounds, never propagate to another frame");
3081 #endif
3083 DealWithWindowsAppearanceHacks(aFrame, aBuilder);
3085 const bool isThemed = aFrame->IsThemed();
3087 const ComputedStyle* bgSC = aFrame->Style();
3088 const nsStyleBackground* bg = bgSC->StyleBackground();
3089 const bool needsBackgroundColor =
3090 aBuilder->IsForEventDelivery() ||
3091 (EffectCompositor::HasAnimationsForCompositor(
3092 aFrame, DisplayItemType::TYPE_BACKGROUND_COLOR) &&
3093 !isThemed);
3094 if (!needsBackgroundColor && !isThemed && bg->IsTransparent(bgSC)) {
3095 return AppendedBackgroundType::None;
3098 bool drawBackgroundColor = false;
3099 bool drawBackgroundImage = false;
3100 nscolor color = NS_RGBA(0, 0, 0, 0);
3101 // Don't get background color / images if we propagated our background to the
3102 // canvas (that is, if FindBackgroundFrame is null). But don't early return
3103 // yet, since we might still need a background-color item for hit-testing.
3104 if (!isThemed && nsCSSRendering::FindBackgroundFrame(aFrame)) {
3105 color = nsCSSRendering::DetermineBackgroundColor(
3106 aFrame->PresContext(), bgSC, aFrame, drawBackgroundImage,
3107 drawBackgroundColor);
3110 if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
3111 color)) {
3112 return AppendedBackgroundType::None;
3115 const nsStyleBorder& border = *aFrame->StyleBorder();
3116 const bool willPaintBorder =
3117 aAllowWillPaintBorderOptimization && !isThemed &&
3118 !aFrame->StyleEffects()->HasBoxShadowWithInset(true) &&
3119 border.HasBorder();
3121 auto EnsureBuildingDisplayList = [&] {
3122 if (!aAutoBuildingDisplayList || *aAutoBuildingDisplayList) {
3123 return;
3125 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3126 aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3127 aBuilder->GetVisibleRect() + offset,
3128 aBuilder->GetDirtyRect() + offset);
3131 // An auxiliary list is necessary in case we have background blending; if that
3132 // is the case, background items need to be wrapped by a blend container to
3133 // isolate blending to the background
3134 nsDisplayList bgItemList(aBuilder);
3135 // Even if we don't actually have a background color to paint, we may still
3136 // need to create an item for hit testing and we still need to create an item
3137 // for background-color animations.
3138 if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) ||
3139 needsBackgroundColor) {
3140 EnsureBuildingDisplayList();
3141 Maybe<DisplayListClipState::AutoSaveRestore> clipState;
3142 nsRect bgColorRect = aBackgroundRect;
3143 if (!isThemed && !aBuilder->IsForEventDelivery()) {
3144 // Disable the will-paint-border optimization for background
3145 // colors with no border-radius. Enabling it for background colors
3146 // doesn't help much (there are no tiling issues) and clipping the
3147 // background breaks detection of the element's border-box being
3148 // opaque. For nonzero border-radius we still need it because we
3149 // want to inset the background if possible to avoid antialiasing
3150 // artifacts along the rounded corners.
3151 const bool useWillPaintBorderOptimization =
3152 willPaintBorder &&
3153 nsLayoutUtils::HasNonZeroCorner(border.mBorderRadius);
3155 nsCSSRendering::ImageLayerClipState clip;
3156 nsCSSRendering::GetImageLayerClip(
3157 bg->BottomLayer(), aFrame, border, aBackgroundRect, aBackgroundRect,
3158 useWillPaintBorderOptimization,
3159 aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3161 bgColorRect = bgColorRect.Intersect(clip.mBGClipArea);
3162 if (clip.mHasAdditionalBGClipArea) {
3163 bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea);
3165 if (clip.mHasRoundedCorners) {
3166 clipState.emplace(aBuilder);
3167 clipState->ClipContentDescendants(clip.mBGClipArea, clip.mRadii);
3171 nsDisplayBackgroundColor* bgItem = CreateBackgroundColor(
3172 aBuilder, aFrame, aSecondaryReferenceFrame, bgColorRect, bgSC,
3173 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0));
3175 if (bgItem) {
3176 bgItemList.AppendToTop(bgItem);
3180 if (isThemed) {
3181 nsDisplayThemedBackground* bgItem = CreateThemedBackground(
3182 aBuilder, aFrame, aSecondaryReferenceFrame, aBackgroundRect);
3184 if (bgItem) {
3185 bgItem->Init(aBuilder);
3186 bgItemList.AppendToTop(bgItem);
3189 if (!bgItemList.IsEmpty()) {
3190 aList->AppendToTop(&bgItemList);
3191 return AppendedBackgroundType::ThemedBackground;
3194 return AppendedBackgroundType::None;
3197 if (!drawBackgroundImage) {
3198 if (!bgItemList.IsEmpty()) {
3199 aList->AppendToTop(&bgItemList);
3200 return AppendedBackgroundType::Background;
3203 return AppendedBackgroundType::None;
3206 const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
3208 bool needBlendContainer = false;
3209 const nsRect& bgOriginRect =
3210 aBackgroundOriginRect.IsEmpty() ? aBackgroundRect : aBackgroundOriginRect;
3212 // Passing bg == nullptr in this macro will result in one iteration with
3213 // i = 0.
3214 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
3215 if (bg->mImage.mLayers[i].mImage.IsNone()) {
3216 continue;
3219 EnsureBuildingDisplayList();
3221 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3222 needBlendContainer = true;
3225 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3226 if (!aBuilder->IsForEventDelivery()) {
3227 const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
3228 SetBackgroundClipRegion(clipState, aFrame, layer, aBackgroundRect,
3229 willPaintBorder);
3232 nsDisplayList thisItemList(aBuilder);
3233 nsDisplayBackgroundImage::InitData bgData =
3234 nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect,
3235 bgSC);
3237 if (bgData.shouldFixToViewport) {
3238 auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
3239 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3240 aBuilder, aFrame, aBuilder->GetVisibleRect(),
3241 aBuilder->GetDirtyRect());
3243 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
3244 aBuilder);
3245 if (displayData) {
3246 asrSetter.SetCurrentActiveScrolledRoot(
3247 displayData->mContainingBlockActiveScrolledRoot);
3248 asrSetter.SetCurrentScrollParentId(displayData->mScrollParentId);
3249 if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
3250 // Override the dirty rect on the builder to be the dirty rect of
3251 // the viewport.
3252 // displayData->mDirtyRect is relative to the presshell's viewport
3253 // frame (the root frame), and we need it to be relative to aFrame.
3254 nsIFrame* rootFrame =
3255 aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
3256 // There cannot be any transforms between aFrame and rootFrame
3257 // because then bgData.shouldFixToViewport would have been false.
3258 nsRect visibleRect =
3259 displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame);
3260 aBuilder->SetVisibleRect(visibleRect);
3261 nsRect dirtyRect =
3262 displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
3263 aBuilder->SetDirtyRect(dirtyRect);
3267 nsDisplayBackgroundImage* bgItem = nullptr;
3269 // The clip is captured by the nsDisplayFixedPosition, so clear the
3270 // clip for the nsDisplayBackgroundImage inside.
3271 DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
3272 bgImageClip.Clear();
3273 bgItem = CreateBackgroundImage(aBuilder, aFrame,
3274 aSecondaryReferenceFrame, bgData);
3276 if (bgItem) {
3277 thisItemList.AppendToTop(
3278 nsDisplayFixedPosition::CreateForFixedBackground(
3279 aBuilder, aFrame, aSecondaryReferenceFrame, bgItem, i, asr));
3281 } else { // bgData.shouldFixToViewport == false
3282 nsDisplayBackgroundImage* bgItem = CreateBackgroundImage(
3283 aBuilder, aFrame, aSecondaryReferenceFrame, bgData);
3284 if (bgItem) {
3285 thisItemList.AppendToTop(bgItem);
3289 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3290 // asr is scrolled. Even if we wrap a fixed background layer, that's
3291 // fine, because the item will have a scrolled clip that limits the
3292 // item with respect to asr.
3293 if (aSecondaryReferenceFrame) {
3294 const auto tableType = GetTableTypeFromFrame(aFrame);
3295 const uint16_t index = CalculateTablePerFrameKey(i + 1, tableType);
3297 thisItemList.AppendNewToTopWithIndex<nsDisplayTableBlendMode>(
3298 aBuilder, aSecondaryReferenceFrame, index, &thisItemList,
3299 bg->mImage.mLayers[i].mBlendMode, asr, aFrame, true);
3300 } else {
3301 thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
3302 aBuilder, aFrame, i + 1, &thisItemList,
3303 bg->mImage.mLayers[i].mBlendMode, asr, true);
3306 bgItemList.AppendToTop(&thisItemList);
3309 if (needBlendContainer) {
3310 bgItemList.AppendToTop(
3311 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3312 aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr));
3315 if (!bgItemList.IsEmpty()) {
3316 aList->AppendToTop(&bgItemList);
3317 return AppendedBackgroundType::Background;
3320 return AppendedBackgroundType::None;
3323 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3324 // intersects aRect. Assumes that the unrounded border has already
3325 // been checked for intersection.
3326 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame,
3327 const nsPoint& aFrameToReferenceFrame,
3328 const nsRect& aTestRect) {
3329 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize())
3330 .Intersects(aTestRect)) {
3331 return false;
3334 nscoord radii[8];
3335 return !aFrame->GetBorderRadii(radii) ||
3336 nsLayoutUtils::RoundedRectIntersectsRect(
3337 nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii,
3338 aTestRect);
3341 // Returns TRUE if aContainedRect is guaranteed to be contained in
3342 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3343 // handled conservatively by returning FALSE in some situations where
3344 // a more thorough analysis could return TRUE.
3346 // See also RoundedRectIntersectsRect.
3347 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
3348 const nscoord aRadii[8],
3349 const nsRect& aContainedRect) {
3350 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii,
3351 aContainedRect);
3352 return rgn.Contains(aContainedRect);
3355 bool nsDisplayBackgroundImage::CanApplyOpacity(
3356 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3357 return CanBuildWebRenderDisplayItems(aManager, aBuilder);
3360 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3361 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3362 return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip !=
3363 StyleGeometryBox::Text &&
3364 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3365 aManager, *StyleFrame()->PresContext(), StyleFrame(),
3366 mBackgroundStyle->StyleBackground(), mLayer,
3367 aBuilder->GetBackgroundPaintFlags());
3370 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3371 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3372 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3373 nsDisplayListBuilder* aDisplayListBuilder) {
3374 if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(),
3375 aDisplayListBuilder)) {
3376 return false;
3379 uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
3380 bool dummy;
3381 nsCSSRendering::PaintBGParams params =
3382 nsCSSRendering::PaintBGParams::ForSingleLayer(
3383 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy),
3384 mBackgroundRect, StyleFrame(), paintFlags, mLayer,
3385 CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity());
3386 params.bgClipRect = &mBounds;
3387 ImgDrawResult result =
3388 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3389 params, aBuilder, aResources, aSc, aManager, this);
3390 if (result == ImgDrawResult::NOT_SUPPORTED) {
3391 return false;
3394 if (nsIContent* content = StyleFrame()->GetContent()) {
3395 if (imgRequestProxy* requestProxy = mBackgroundStyle->StyleBackground()
3396 ->mImage.mLayers[mLayer]
3397 .mImage.GetImageRequest()) {
3398 // LCP don't consider gradient backgrounds.
3399 LCPHelpers::FinalizeLCPEntryForImage(content->AsElement(), requestProxy,
3400 mBounds - ToReferenceFrame());
3404 return true;
3407 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
3408 const nsRect& aRect,
3409 HitTestState* aState,
3410 nsTArray<nsIFrame*>* aOutFrames) {
3411 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3412 aOutFrames->AppendElement(mFrame);
3416 static nsRect GetInsideClipRect(const nsDisplayItem* aItem,
3417 StyleGeometryBox aClip, const nsRect& aRect,
3418 const nsRect& aBackgroundRect) {
3419 if (aRect.IsEmpty()) {
3420 return {};
3423 nsIFrame* frame = aItem->Frame();
3425 nsRect clipRect = aBackgroundRect;
3426 if (frame->IsCanvasFrame()) {
3427 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3428 clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
3429 } else if (aClip == StyleGeometryBox::PaddingBox ||
3430 aClip == StyleGeometryBox::ContentBox) {
3431 nsMargin border = frame->GetUsedBorder();
3432 if (aClip == StyleGeometryBox::ContentBox) {
3433 border += frame->GetUsedPadding();
3435 border.ApplySkipSides(frame->GetSkipSides());
3436 clipRect.Deflate(border);
3439 return clipRect.Intersect(aRect);
3442 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
3443 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3444 nsRegion result;
3445 *aSnap = false;
3447 if (!mBackgroundStyle) {
3448 return result;
3451 *aSnap = true;
3453 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3454 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3455 // which expects frames to be sent to it in content order, not reverse
3456 // content order which we'll produce here.
3457 // Of course, if there's only one frame in the flow, it doesn't matter.
3458 if (mFrame->StyleBorder()->mBoxDecorationBreak ==
3459 StyleBoxDecorationBreak::Clone ||
3460 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
3461 const nsStyleImageLayers::Layer& layer =
3462 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3463 if (layer.mImage.IsOpaque() && layer.mBlendMode == StyleBlend::Normal &&
3464 layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
3465 layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
3466 layer.mClip != StyleGeometryBox::Text) {
3467 result = GetInsideClipRect(this, layer.mClip, mBounds, mBackgroundRect);
3471 return result;
3474 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform(
3475 nsDisplayListBuilder* aBuilder) const {
3476 if (!mBackgroundStyle) {
3477 return Some(NS_RGBA(0, 0, 0, 0));
3479 return Nothing();
3482 nsRect nsDisplayBackgroundImage::GetPositioningArea() const {
3483 if (!mBackgroundStyle) {
3484 return nsRect();
3486 nsIFrame* attachedToFrame;
3487 bool transformedFixed;
3488 return nsCSSRendering::ComputeImageLayerPositioningArea(
3489 mFrame->PresContext(), mFrame, mBackgroundRect,
3490 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer],
3491 &attachedToFrame, &transformedFixed) +
3492 ToReferenceFrame();
3495 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3496 const {
3497 if (!mBackgroundStyle) {
3498 return false;
3501 nscoord radii[8];
3502 if (mFrame->GetBorderRadii(radii)) {
3503 // A change in the size of the positioning area might change the position
3504 // of the rounded corners.
3505 return true;
3508 const nsStyleImageLayers::Layer& layer =
3509 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3510 return layer.RenderingMightDependOnPositioningAreaSizeChange();
3513 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
3514 gfxContext* aCtx) {
3515 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &mBounds);
3518 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
3519 gfxContext* aCtx,
3520 const nsRect& aBounds,
3521 nsRect* aClipRect) {
3522 gfxContext* ctx = aCtx;
3523 StyleGeometryBox clip =
3524 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip;
3526 if (clip == StyleGeometryBox::Text) {
3527 if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect,
3528 aBuilder)) {
3529 return;
3533 nsCSSRendering::PaintBGParams params =
3534 nsCSSRendering::PaintBGParams::ForSingleLayer(
3535 *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
3536 aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER,
3537 1.0f);
3538 params.bgClipRect = aClipRect;
3539 Unused << nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
3541 if (clip == StyleGeometryBox::Text) {
3542 ctx->PopGroupAndBlend();
3546 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3547 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3548 nsRegion* aInvalidRegion) const {
3549 if (!mBackgroundStyle) {
3550 return;
3553 const auto* geometry =
3554 static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
3556 bool snap;
3557 nsRect bounds = GetBounds(aBuilder, &snap);
3558 nsRect positioningArea = GetPositioningArea();
3559 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
3560 (positioningArea.Size() != geometry->mPositioningArea.Size() &&
3561 RenderingMightDependOnPositioningAreaSizeChange())) {
3562 // Positioning area changed in a way that could cause everything to change,
3563 // so invalidate everything (both old and new painting areas).
3564 aInvalidRegion->Or(bounds, geometry->mBounds);
3565 return;
3567 if (!mDestRect.IsEqualInterior(geometry->mDestRect)) {
3568 // Dest area changed in a way that could cause everything to change,
3569 // so invalidate everything (both old and new painting areas).
3570 aInvalidRegion->Or(bounds, geometry->mBounds);
3571 return;
3573 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3574 // Positioning area is unchanged, so invalidate just the change in the
3575 // painting area.
3576 aInvalidRegion->Xor(bounds, geometry->mBounds);
3580 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder,
3581 bool* aSnap) const {
3582 *aSnap = true;
3583 return mBounds;
3586 nsRect nsDisplayBackgroundImage::GetBoundsInternal(
3587 nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) {
3588 // This allows nsDisplayTableBackgroundImage to change the frame used for
3589 // bounds calculation.
3590 nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame;
3592 nsPresContext* presContext = frame->PresContext();
3594 if (!mBackgroundStyle) {
3595 return nsRect();
3598 nsRect clipRect = mBackgroundRect;
3599 if (frame->IsCanvasFrame()) {
3600 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3601 clipRect = canvasFrame->CanvasArea() + ToReferenceFrame();
3603 const nsStyleImageLayers::Layer& layer =
3604 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3605 return nsCSSRendering::GetBackgroundLayerRect(
3606 presContext, frame, mBackgroundRect, clipRect, layer,
3607 aBuilder->GetBackgroundPaintFlags());
3610 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3611 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aData,
3612 nsIFrame* aCellFrame)
3613 : nsDisplayBackgroundImage(aBuilder, aFrame, aData, aCellFrame),
3614 mStyleFrame(aCellFrame) {
3615 if (aBuilder->IsRetainingDisplayList()) {
3616 mStyleFrame->AddDisplayItem(this);
3620 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3621 if (mStyleFrame) {
3622 mStyleFrame->RemoveDisplayItem(this);
3626 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const {
3627 bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
3628 aRect += ToReferenceFrame();
3629 return result;
3632 nsDisplayThemedBackground::nsDisplayThemedBackground(
3633 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3634 const nsRect& aBackgroundRect)
3635 : nsPaintedDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) {
3636 MOZ_COUNT_CTOR(nsDisplayThemedBackground);
3639 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) {
3640 const nsStyleDisplay* disp = StyleFrame()->StyleDisplay();
3641 mAppearance = disp->EffectiveAppearance();
3642 StyleFrame()->IsThemed(disp, &mThemeTransparency);
3644 // Perform necessary RegisterThemeGeometry
3645 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3646 nsITheme::ThemeGeometryType type =
3647 theme->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance);
3648 if (type != nsITheme::eThemeGeometryTypeUnknown) {
3649 RegisterThemeGeometry(aBuilder, this, StyleFrame(), type);
3652 mBounds = GetBoundsInternal();
3655 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) {
3656 aStream << " (themed, appearance:" << (int)mAppearance << ")";
3659 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
3660 const nsRect& aRect,
3661 HitTestState* aState,
3662 nsTArray<nsIFrame*>* aOutFrames) {
3663 // Assume that any point in our background rect is a hit.
3664 if (mBackgroundRect.Intersects(aRect)) {
3665 aOutFrames->AppendElement(mFrame);
3669 nsRegion nsDisplayThemedBackground::GetOpaqueRegion(
3670 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3671 nsRegion result;
3672 *aSnap = false;
3674 if (mThemeTransparency == nsITheme::eOpaque) {
3675 *aSnap = true;
3676 result = mBackgroundRect;
3678 return result;
3681 Maybe<nscolor> nsDisplayThemedBackground::IsUniform(
3682 nsDisplayListBuilder* aBuilder) const {
3683 return Nothing();
3686 nsRect nsDisplayThemedBackground::GetPositioningArea() const {
3687 return mBackgroundRect;
3690 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
3691 gfxContext* aCtx) {
3692 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), nullptr);
3695 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
3696 gfxContext* aCtx,
3697 const nsRect& aBounds,
3698 nsRect* aClipRect) {
3699 // XXXzw this ignores aClipRect.
3700 nsPresContext* presContext = StyleFrame()->PresContext();
3701 nsITheme* theme = presContext->Theme();
3702 nsRect drawing(mBackgroundRect);
3703 theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(),
3704 mAppearance, &drawing);
3705 drawing.IntersectRect(drawing, aBounds);
3706 theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect,
3707 drawing);
3710 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3711 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3712 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3713 nsDisplayListBuilder* aDisplayListBuilder) {
3714 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3715 return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc,
3716 aManager, StyleFrame(),
3717 mAppearance, mBackgroundRect);
3720 bool nsDisplayThemedBackground::IsWindowActive() const {
3721 return !mFrame->PresContext()->Document()->IsTopLevelWindowInactive();
3724 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3725 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3726 nsRegion* aInvalidRegion) const {
3727 const auto* geometry =
3728 static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
3730 bool snap;
3731 nsRect bounds = GetBounds(aBuilder, &snap);
3732 nsRect positioningArea = GetPositioningArea();
3733 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
3734 // Invalidate everything (both old and new painting areas).
3735 aInvalidRegion->Or(bounds, geometry->mBounds);
3736 return;
3738 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3739 // Positioning area is unchanged, so invalidate just the change in the
3740 // painting area.
3741 aInvalidRegion->Xor(bounds, geometry->mBounds);
3743 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3744 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
3745 IsWindowActive() != geometry->mWindowIsActive) {
3746 aInvalidRegion->Or(*aInvalidRegion, bounds);
3750 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder,
3751 bool* aSnap) const {
3752 *aSnap = true;
3753 return mBounds;
3756 nsRect nsDisplayThemedBackground::GetBoundsInternal() {
3757 nsPresContext* presContext = mFrame->PresContext();
3759 nsRect r = mBackgroundRect - ToReferenceFrame();
3760 presContext->Theme()->GetWidgetOverflow(
3761 presContext->DeviceContext(), mFrame,
3762 mFrame->StyleDisplay()->EffectiveAppearance(), &r);
3763 return r + ToReferenceFrame();
3766 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
3767 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder,
3768 gfxContext* aCtx) {
3769 mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(),
3770 mFrame, ToReferenceFrame(), mColor);
3772 #endif
3774 bool nsDisplayBackgroundColor::CanApplyOpacity(
3775 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3776 // Don't apply opacity if the background color is animated since the color is
3777 // going to be changed on the compositor.
3778 return !EffectCompositor::HasAnimationsForCompositor(
3779 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR);
3782 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3783 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3784 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3785 nsDisplayListBuilder* aDisplayListBuilder) {
3786 gfx::sRGBColor color = mColor;
3787 color.a *= aBuilder.GetInheritedOpacity();
3789 if (color == sRGBColor() &&
3790 !EffectCompositor::HasAnimationsForCompositor(
3791 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3792 return true;
3795 if (HasBackgroundClipText()) {
3796 return false;
3799 uint64_t animationsId = 0;
3800 // We don't support background-color animations on table elements yet.
3801 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
3802 animationsId =
3803 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
3806 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
3807 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3808 wr::LayoutRect r = wr::ToLayoutRect(bounds);
3810 if (animationsId) {
3811 wr::WrAnimationProperty prop{
3812 wr::WrAnimationType::BackgroundColor,
3813 animationsId,
3815 aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(),
3816 wr::ToColorF(ToDeviceColor(color)), &prop);
3817 } else {
3818 aBuilder.StartGroup(this);
3819 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
3820 wr::ToColorF(ToDeviceColor(color)));
3821 aBuilder.FinishGroup();
3824 return true;
3827 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder,
3828 gfxContext* aCtx,
3829 const DisplayItemClip& aClip) {
3830 MOZ_ASSERT(!HasBackgroundClipText());
3832 if (mColor == sRGBColor()) {
3833 return;
3836 nsRect fillRect = mBackgroundRect;
3837 if (aClip.HasClip()) {
3838 fillRect.IntersectRect(fillRect, aClip.GetClipRect());
3841 DrawTarget* dt = aCtx->GetDrawTarget();
3842 int32_t A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
3843 Rect bounds = ToRect(nsLayoutUtils::RectToGfxRect(fillRect, A2D));
3844 MaybeSnapToDevicePixels(bounds, *dt);
3845 ColorPattern fill(ToDeviceColor(mColor));
3847 if (aClip.GetRoundedRectCount()) {
3848 MOZ_ASSERT(aClip.GetRoundedRectCount() == 1);
3850 AutoTArray<DisplayItemClip::RoundedRect, 1> roundedRect;
3851 aClip.AppendRoundedRects(&roundedRect);
3853 bool pushedClip = false;
3854 if (!fillRect.Contains(roundedRect[0].mRect)) {
3855 dt->PushClipRect(bounds);
3856 pushedClip = true;
3859 RectCornerRadii pixelRadii;
3860 nsCSSRendering::ComputePixelRadii(roundedRect[0].mRadii, A2D, &pixelRadii);
3861 dt->FillRoundedRect(
3862 RoundedRect(NSRectToSnappedRect(roundedRect[0].mRect, A2D, *dt),
3863 pixelRadii),
3864 fill);
3865 if (pushedClip) {
3866 dt->PopClip();
3868 } else {
3869 dt->FillRect(bounds, fill);
3873 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
3874 gfxContext* aCtx) {
3875 if (mColor == sRGBColor()) {
3876 return;
3879 #if 0
3880 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3881 // results in a precision induced rounding issue that makes the rect one
3882 // pixel shorter in rare cases. Disabled in favor of the old code for now.
3883 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3884 // reproduce the bug.
3886 // TODO:
3887 // This new path does not include support for background-clip:text; need to
3888 // be fixed if/when we switch to this new code path.
3890 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
3892 Rect rect = NSRectToSnappedRect(mBackgroundRect,
3893 mFrame->PresContext()->AppUnitsPerDevPixel(),
3894 aDrawTarget);
3895 ColorPattern color(ToDeviceColor(mColor));
3896 aDrawTarget.FillRect(rect, color);
3897 #else
3898 gfxContext* ctx = aCtx;
3899 gfxRect bounds = nsLayoutUtils::RectToGfxRect(
3900 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3902 if (HasBackgroundClipText()) {
3903 if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
3904 return;
3907 ctx->SetColor(mColor);
3908 ctx->NewPath();
3909 ctx->SnappedRectangle(bounds);
3910 ctx->Fill();
3911 ctx->PopGroupAndBlend();
3912 return;
3915 ctx->SetColor(mColor);
3916 ctx->NewPath();
3917 ctx->SnappedRectangle(bounds);
3918 ctx->Fill();
3919 #endif
3922 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion(
3923 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3924 *aSnap = false;
3926 if (mColor.a != 1 ||
3927 // Even if the current alpha channel is 1, we treat this item as if it's
3928 // non-opaque if there is a background-color animation since the animation
3929 // might change the alpha channel.
3930 EffectCompositor::HasAnimationsForCompositor(
3931 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3932 return nsRegion();
3935 if (!mHasStyle || HasBackgroundClipText()) {
3936 return nsRegion();
3939 *aSnap = true;
3940 return GetInsideClipRect(this, mBottomLayerClip, mBackgroundRect,
3941 mBackgroundRect);
3944 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform(
3945 nsDisplayListBuilder* aBuilder) const {
3946 return Some(mColor.ToABGR());
3949 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
3950 const nsRect& aRect,
3951 HitTestState* aState,
3952 nsTArray<nsIFrame*>* aOutFrames) {
3953 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3954 // aRect doesn't intersect our border-radius curve.
3955 return;
3958 aOutFrames->AppendElement(mFrame);
3961 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) {
3962 aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << ","
3963 << mColor.a << ")";
3964 aStream << " backgroundRect" << mBackgroundRect;
3967 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
3968 bool* aSnap) const {
3969 *aSnap = false;
3970 return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
3973 nsRect nsDisplayOutline::GetInnerRect() const {
3974 if (nsRect* savedOutlineInnerRect =
3975 mFrame->GetProperty(nsIFrame::OutlineInnerRectProperty())) {
3976 return *savedOutlineInnerRect;
3978 return mFrame->GetRectRelativeToSelf();
3981 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
3982 // TODO join outlines together
3983 MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(),
3984 "Should have not created a nsDisplayOutline!");
3986 nsRect rect = GetInnerRect() + ToReferenceFrame();
3987 nsPresContext* pc = mFrame->PresContext();
3988 if (IsThemedOutline()) {
3989 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
3990 pc->Theme()->DrawWidgetBackground(aCtx, mFrame,
3991 StyleAppearance::FocusOutline, rect,
3992 GetPaintRect(aBuilder, aCtx));
3993 return;
3996 nsCSSRendering::PaintNonThemedOutline(
3997 pc, *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), rect, mFrame->Style());
4000 bool nsDisplayOutline::IsThemedOutline() const {
4001 #ifdef DEBUG
4002 nsPresContext* pc = mFrame->PresContext();
4003 MOZ_ASSERT(
4004 pc->Theme()->ThemeSupportsWidget(pc, mFrame,
4005 StyleAppearance::FocusOutline),
4006 "All of our supported platforms have support for themed focus-outlines");
4007 #endif
4008 return mFrame->StyleOutline()->mOutlineStyle.IsAuto();
4011 bool nsDisplayOutline::CreateWebRenderCommands(
4012 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4013 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4014 nsDisplayListBuilder* aDisplayListBuilder) {
4015 nsPresContext* pc = mFrame->PresContext();
4016 nsRect rect = GetInnerRect() + ToReferenceFrame();
4017 if (IsThemedOutline()) {
4018 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
4019 return pc->Theme()->CreateWebRenderCommandsForWidget(
4020 aBuilder, aResources, aSc, aManager, mFrame,
4021 StyleAppearance::FocusOutline, rect);
4024 bool dummy;
4025 Maybe<nsCSSBorderRenderer> borderRenderer =
4026 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4027 pc, /* aDrawTarget = */ nullptr, mFrame,
4028 GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style());
4030 if (!borderRenderer) {
4031 // No border renderer means "there is no outline".
4032 // Paint nothing and return success.
4033 return true;
4036 borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
4037 return true;
4040 bool nsDisplayOutline::HasRadius() const {
4041 const auto& radius = mFrame->StyleBorder()->mBorderRadius;
4042 return !nsLayoutUtils::HasNonZeroCorner(radius);
4045 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const {
4046 const nsStyleOutline* outline = mFrame->StyleOutline();
4047 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
4048 if (borderBox.Contains(aRect) && !HasRadius() &&
4049 outline->mOutlineOffset.ToCSSPixels() >= 0.0f) {
4050 // aRect is entirely inside the border-rect, and the outline isn't rendered
4051 // inside the border-rect, so the outline is not visible.
4052 return true;
4054 return false;
4057 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
4058 const nsRect& aRect, HitTestState* aState,
4059 nsTArray<nsIFrame*>* aOutFrames) {
4060 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4061 // aRect doesn't intersect our border-radius curve.
4062 return;
4065 aOutFrames->AppendElement(mFrame);
4068 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4069 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4070 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4071 nsDisplayListBuilder* aDisplayListBuilder) {
4072 return true;
4075 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4076 return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
4079 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) {
4080 mOverrideZIndex = Some(aZIndex);
4083 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
4084 nsIFrame* aCaretFrame)
4085 : nsPaintedDisplayItem(aBuilder, aCaretFrame),
4086 mCaret(aBuilder->GetCaret()),
4087 mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) {
4088 MOZ_COUNT_CTOR(nsDisplayCaret);
4089 // The presence of a caret doesn't change the overflow rect
4090 // of the owning frame, so the normal building rect might not
4091 // include the caret at all. We use MarkFrameForDisplay to ensure
4092 // we build this item, and here we override the building rect
4093 // to cover the pixels we're going to draw.
4094 SetBuildingRect(mBounds);
4097 #ifdef NS_BUILD_REFCNT_LOGGING
4098 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret); }
4099 #endif
4101 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder,
4102 bool* aSnap) const {
4103 *aSnap = true;
4104 // The caret returns a rect in the coordinates of mFrame.
4105 return mBounds;
4108 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4109 // Note: Because we exist, we know that the caret is visible, so we don't
4110 // need to check for the caret's visibility.
4111 mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
4114 bool nsDisplayCaret::CreateWebRenderCommands(
4115 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4116 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4117 nsDisplayListBuilder* aDisplayListBuilder) {
4118 using namespace layers;
4119 nsRect caretRect;
4120 nsRect hookRect;
4121 nscolor caretColor;
4122 nsIFrame* frame =
4123 mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor);
4124 if (NS_WARN_IF(!frame) || NS_WARN_IF(frame != mFrame)) {
4125 return true;
4128 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
4129 gfx::DeviceColor color = ToDeviceColor(caretColor);
4130 LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
4131 caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
4132 LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
4133 hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
4135 wr::LayoutRect caret = wr::ToLayoutRect(devCaretRect);
4136 wr::LayoutRect hook = wr::ToLayoutRect(devHookRect);
4138 // Note, WR will pixel snap anything that is layout aligned.
4139 aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), false, false,
4140 wr::ToColorF(color));
4142 if (!devHookRect.IsEmpty()) {
4143 aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), false, false,
4144 wr::ToColorF(color));
4146 return true;
4149 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder,
4150 nsIFrame* aFrame)
4151 : nsPaintedDisplayItem(aBuilder, aFrame) {
4152 MOZ_COUNT_CTOR(nsDisplayBorder);
4154 mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
4157 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const {
4158 nsRect paddingRect = GetPaddingRect();
4159 const nsStyleBorder* styleBorder;
4160 if (paddingRect.Contains(aRect) &&
4161 !(styleBorder = mFrame->StyleBorder())->IsBorderImageSizeAvailable() &&
4162 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
4163 // aRect is entirely inside the content rect, and no part
4164 // of the border is rendered inside the content rect, so we are not
4165 // visible
4166 // Skip this if there's a border-image (which draws a background
4167 // too) or if there is a border-radius (which makes the border draw
4168 // further in).
4169 return true;
4172 return false;
4175 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry(
4176 nsDisplayListBuilder* aBuilder) {
4177 return new nsDisplayBorderGeometry(this, aBuilder);
4180 void nsDisplayBorder::ComputeInvalidationRegion(
4181 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4182 nsRegion* aInvalidRegion) const {
4183 const auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
4184 bool snap;
4186 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
4187 // We can probably get away with only invalidating the difference
4188 // between the border and padding rects, but the XUL ui at least
4189 // is apparently painting a background with this?
4190 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4194 bool nsDisplayBorder::CreateWebRenderCommands(
4195 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4196 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4197 nsDisplayListBuilder* aDisplayListBuilder) {
4198 nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
4200 ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder(
4201 this, mFrame, rect, aBuilder, aResources, aSc, aManager,
4202 aDisplayListBuilder);
4204 if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
4205 return false;
4207 return true;
4210 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4211 nsPoint offset = ToReferenceFrame();
4213 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
4214 ? PaintBorderFlags::SyncDecodeImages
4215 : PaintBorderFlags();
4217 Unused << nsCSSRendering::PaintBorder(
4218 mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
4219 nsRect(offset, mFrame->GetSize()), mFrame->Style(), flags,
4220 mFrame->GetSkipSides());
4223 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder,
4224 bool* aSnap) const {
4225 *aSnap = true;
4226 return mBounds;
4229 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
4230 gfxContext* aCtx) {
4231 nsPoint offset = ToReferenceFrame();
4232 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4233 nsPresContext* presContext = mFrame->PresContext();
4235 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
4237 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
4238 GetPaintRect(aBuilder, aCtx), 1.0f);
4241 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
4242 bool* aSnap) const {
4243 *aSnap = false;
4244 return mBounds;
4247 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() {
4248 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
4249 ToReferenceFrame();
4252 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const {
4253 nsPoint origin = ToReferenceFrame();
4254 nsRect frameRect(origin, mFrame->GetSize());
4255 if (!frameRect.Contains(aRect)) {
4256 return false;
4259 // the visible region is entirely inside the border-rect, and box shadows
4260 // never render within the border-rect (unless there's a border radius).
4261 nscoord twipsRadii[8];
4262 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
4263 if (!hasBorderRadii) {
4264 return true;
4267 return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
4270 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4271 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4272 if (shadows.IsEmpty()) {
4273 return false;
4276 bool hasBorderRadius;
4277 // We don't support native themed things yet like box shadows around
4278 // input buttons.
4280 // TODO(emilio): The non-native theme could provide the right rect+radius
4281 // instead relatively painlessly, if we find this causes performance issues or
4282 // what not.
4283 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4286 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4287 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4288 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4289 nsDisplayListBuilder* aDisplayListBuilder) {
4290 if (!CanBuildWebRenderDisplayItems()) {
4291 return false;
4294 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4295 nsPoint offset = ToReferenceFrame();
4296 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4297 bool snap;
4298 nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
4300 bool hasBorderRadius;
4301 bool nativeTheme =
4302 nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4304 // Don't need the full size of the shadow rect like we do in
4305 // nsCSSRendering since WR takes care of calculations for blur
4306 // and spread radius.
4307 nsRect frameRect =
4308 nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
4310 RectCornerRadii borderRadii;
4311 if (hasBorderRadius) {
4312 hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
4313 mFrame, borderRadii);
4316 // Everything here is in app units, change to device units.
4317 LayoutDeviceRect clipRect =
4318 LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel);
4319 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4320 MOZ_ASSERT(!shadows.IsEmpty());
4322 for (const auto& shadow : Reversed(shadows)) {
4323 if (shadow.inset) {
4324 continue;
4327 float blurRadius =
4328 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4329 gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor(
4330 shadow.base, mFrame, aBuilder.GetInheritedOpacity());
4332 // We don't move the shadow rect here since WR does it for us
4333 // Now translate everything to device pixels.
4334 const nsRect& shadowRect = frameRect;
4335 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4336 nsPoint(shadow.base.horizontal.ToAppUnits(),
4337 shadow.base.vertical.ToAppUnits()),
4338 appUnitsPerDevPixel);
4340 LayoutDeviceRect deviceBox =
4341 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4342 wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
4343 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4345 LayoutDeviceSize zeroSize;
4346 wr::BorderRadius borderRadius =
4347 wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
4348 if (hasBorderRadius) {
4349 borderRadius = wr::ToBorderRadius(
4350 LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
4351 LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
4352 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
4353 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
4356 float spreadRadius =
4357 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4359 aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
4360 deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
4361 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius,
4362 spreadRadius, borderRadius,
4363 wr::BoxShadowClipMode::Outset);
4366 return true;
4369 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4370 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4371 nsRegion* aInvalidRegion) const {
4372 const auto* geometry =
4373 static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
4374 bool snap;
4375 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
4376 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
4377 nsRegion oldShadow, newShadow;
4378 nscoord dontCare[8];
4379 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
4380 if (hasBorderRadius) {
4381 // If we have rounded corners then we need to invalidate the frame area
4382 // too since we paint into it.
4383 oldShadow = geometry->mBounds;
4384 newShadow = GetBounds(aBuilder, &snap);
4385 } else {
4386 oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
4387 newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
4389 aInvalidRegion->Or(oldShadow, newShadow);
4393 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
4394 gfxContext* aCtx) {
4395 nsPoint offset = ToReferenceFrame();
4396 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4397 nsPresContext* presContext = mFrame->PresContext();
4399 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS);
4401 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
4404 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4405 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4406 const nsPoint& aReferenceOffset) {
4407 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4408 if (shadows.IsEmpty()) {
4409 // Means we don't have to paint anything
4410 return true;
4413 bool hasBorderRadius;
4414 bool nativeTheme =
4415 nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
4417 // We don't support native themed things yet like box shadows around
4418 // input buttons.
4419 return !nativeTheme;
4422 /* static */
4423 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4424 wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
4425 nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect) {
4426 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) {
4427 return;
4430 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
4432 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4434 LayoutDeviceRect clipRect =
4435 LayoutDeviceRect::FromAppUnits(aVisibleRect, appUnitsPerDevPixel);
4437 for (const auto& shadow : Reversed(shadows)) {
4438 if (!shadow.inset) {
4439 continue;
4442 nsRect shadowRect =
4443 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
4444 RectCornerRadii innerRadii;
4445 nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
4447 // Now translate everything to device pixels.
4448 LayoutDeviceRect deviceBoxRect =
4449 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4450 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4451 sRGBColor shadowColor =
4452 nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0);
4454 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4455 nsPoint(shadow.base.horizontal.ToAppUnits(),
4456 shadow.base.vertical.ToAppUnits()),
4457 appUnitsPerDevPixel);
4459 float blurRadius =
4460 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4462 wr::BorderRadius borderRadius = wr::ToBorderRadius(
4463 LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
4464 LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
4465 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
4466 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
4467 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4468 float spreadRadius =
4469 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4471 aBuilder.PushBoxShadow(
4472 wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
4473 !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
4474 wr::ToLayoutVector2D(shadowOffset),
4475 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, spreadRadius,
4476 borderRadius, wr::BoxShadowClipMode::Inset);
4480 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4481 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4482 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4483 nsDisplayListBuilder* aDisplayListBuilder) {
4484 if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame,
4485 ToReferenceFrame())) {
4486 return false;
4489 bool snap;
4490 nsRect visible = GetBounds(aDisplayListBuilder, &snap);
4491 nsPoint offset = ToReferenceFrame();
4492 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4493 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4494 aBuilder, aSc, visible, mFrame, borderRect);
4496 return true;
4499 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4500 nsIFrame* aFrame, nsDisplayList* aList)
4501 : nsDisplayWrapList(aBuilder, aFrame, aList,
4502 aBuilder->CurrentActiveScrolledRoot(), false) {}
4504 nsDisplayWrapList::nsDisplayWrapList(
4505 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4506 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
4507 : nsPaintedDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
4508 mList(aBuilder),
4509 mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
4510 mOverrideZIndex(0),
4511 mHasZIndexOverride(false),
4512 mClearingClipChain(aClearClipChain) {
4513 MOZ_COUNT_CTOR(nsDisplayWrapList);
4515 mBaseBuildingRect = GetBuildingRect();
4517 mListPtr = &mList;
4518 mListPtr->AppendToTop(aList);
4519 mOriginalClipChain = mClipChain;
4520 nsDisplayWrapList::UpdateBounds(aBuilder);
4523 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4524 nsIFrame* aFrame, nsDisplayItem* aItem)
4525 : nsPaintedDisplayItem(aBuilder, aFrame,
4526 aBuilder->CurrentActiveScrolledRoot()),
4527 mList(aBuilder),
4528 mOverrideZIndex(0),
4529 mHasZIndexOverride(false) {
4530 MOZ_COUNT_CTOR(nsDisplayWrapList);
4532 mBaseBuildingRect = GetBuildingRect();
4534 mListPtr = &mList;
4535 mListPtr->AppendToTop(aItem);
4536 mOriginalClipChain = mClipChain;
4537 nsDisplayWrapList::UpdateBounds(aBuilder);
4539 if (!aFrame || !aFrame->IsTransformed()) {
4540 return;
4543 // See the previous nsDisplayWrapList constructor
4544 if (aItem->Frame() == aFrame) {
4545 mToReferenceFrame = aItem->ToReferenceFrame();
4548 nsRect visible = aBuilder->GetVisibleRect() +
4549 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
4551 SetBuildingRect(visible);
4554 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList); }
4556 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder,
4557 const nsRect& aRect, HitTestState* aState,
4558 nsTArray<nsIFrame*>* aOutFrames) {
4559 mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames);
4562 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder,
4563 bool* aSnap) const {
4564 *aSnap = false;
4565 return mBounds;
4568 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4569 bool* aSnap) const {
4570 *aSnap = false;
4571 bool snap;
4572 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
4573 GetBounds(aBuilder, &snap));
4576 Maybe<nscolor> nsDisplayWrapList::IsUniform(
4577 nsDisplayListBuilder* aBuilder) const {
4578 // We could try to do something but let's conservatively just return Nothing.
4579 return Nothing();
4582 void nsDisplayWrapper::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4583 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4586 nsRect nsDisplayWrapList::GetComponentAlphaBounds(
4587 nsDisplayListBuilder* aBuilder) const {
4588 return mListPtr->GetComponentAlphaBounds(aBuilder);
4591 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4592 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4593 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4594 nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList) {
4595 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4596 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
4597 aNewClipList);
4598 return true;
4601 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder,
4602 nsIFrame* aFrame, nsDisplayList* aList,
4603 nsDisplayItemWrapper* aWrapper) {
4604 if (!aList->GetTop()) {
4605 return NS_OK;
4607 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
4608 if (!item) {
4609 return NS_ERROR_OUT_OF_MEMORY;
4611 // aList was emptied
4612 aList->AppendToTop(item);
4613 return NS_OK;
4616 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
4617 nsDisplayList* aList,
4618 nsDisplayItemWrapper* aWrapper) {
4619 for (nsDisplayItem* item : aList->TakeItems()) {
4620 item = aWrapper->WrapItem(aBuilder, item);
4621 if (!item) {
4622 return NS_ERROR_OUT_OF_MEMORY;
4624 aList->AppendToTop(item);
4626 // aList was emptied
4627 return NS_OK;
4630 nsresult nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
4631 nsIFrame* aFrame,
4632 const nsDisplayListSet& aIn,
4633 const nsDisplayListSet& aOut) {
4634 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
4635 NS_ENSURE_SUCCESS(rv, rv);
4637 if (&aOut == &aIn) {
4638 return NS_OK;
4640 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
4641 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
4642 aOut.Floats()->AppendToTop(aIn.Floats());
4643 aOut.Content()->AppendToTop(aIn.Content());
4644 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
4645 aOut.Outlines()->AppendToTop(aIn.Outlines());
4646 return NS_OK;
4649 nsresult nsDisplayItemWrapper::WrapListsInPlace(
4650 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4651 const nsDisplayListSet& aLists) {
4652 nsresult rv;
4653 if (WrapBorderBackground()) {
4654 // Our border-backgrounds are in-flow
4655 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
4656 NS_ENSURE_SUCCESS(rv, rv);
4658 // Our block border-backgrounds are in-flow
4659 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
4660 NS_ENSURE_SUCCESS(rv, rv);
4661 // The floats are not in flow
4662 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
4663 NS_ENSURE_SUCCESS(rv, rv);
4664 // Our child content is in flow
4665 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
4666 NS_ENSURE_SUCCESS(rv, rv);
4667 // The positioned descendants may not be in-flow
4668 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
4669 NS_ENSURE_SUCCESS(rv, rv);
4670 // The outlines may not be in-flow
4671 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
4674 nsDisplayOpacity::nsDisplayOpacity(
4675 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4676 const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsOnly,
4677 bool aNeedsActiveLayer, bool aWrapsBackdropFilter)
4678 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4679 mOpacity(aFrame->StyleEffects()->mOpacity),
4680 mForEventsOnly(aForEventsOnly),
4681 mNeedsActiveLayer(aNeedsActiveLayer),
4682 mChildOpacityState(ChildOpacityState::Unknown),
4683 mWrapsBackdropFilter(aWrapsBackdropFilter) {
4684 MOZ_COUNT_CTOR(nsDisplayOpacity);
4687 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder,
4688 const nsRect& aRect,
4689 nsDisplayItem::HitTestState* aState,
4690 nsTArray<nsIFrame*>* aOutFrames) {
4691 AutoRestore<float> opacity(aState->mCurrentOpacity);
4692 aState->mCurrentOpacity *= mOpacity;
4694 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4695 // only consider fully opaque items? Or make this configurable somehow?
4696 if (aBuilder->HitTestIsForVisibility() && mOpacity == 0.0f) {
4697 return;
4699 nsDisplayWrapList::HitTest(aBuilder, aRect, aState, aOutFrames);
4702 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4703 bool* aSnap) const {
4704 *aSnap = false;
4705 // The only time where mOpacity == 1.0 should be when we have will-change.
4706 // We could report this as opaque then but when the will-change value starts
4707 // animating the element would become non opaque and could cause repaints.
4708 return nsRegion();
4711 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4712 if (GetOpacity() == 0.0f) {
4713 return;
4716 if (GetOpacity() == 1.0f) {
4717 GetChildren()->Paint(aBuilder, aCtx,
4718 mFrame->PresContext()->AppUnitsPerDevPixel());
4719 return;
4722 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4723 // allocation.
4724 aCtx->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4725 GetChildren()->Paint(aBuilder, aCtx,
4726 mFrame->PresContext()->AppUnitsPerDevPixel());
4727 aCtx->GetDrawTarget()->PopLayer();
4730 /* static */
4731 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
4732 nsIFrame* aFrame) {
4733 return EffectCompositor::HasAnimationsForCompositor(
4734 aFrame, DisplayItemType::TYPE_OPACITY) ||
4735 (ActiveLayerTracker::IsStyleAnimated(
4736 aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()));
4739 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager,
4740 nsDisplayListBuilder* aBuilder) const {
4741 return !EffectCompositor::HasAnimationsForCompositor(
4742 mFrame, DisplayItemType::TYPE_OPACITY);
4745 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4746 // children that don't overlap and can all apply the opacity to themselves.
4747 static const size_t kOpacityMaxChildCount = 3;
4749 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4750 // are likely have more child items than |kOpacityMaxChildCount|.
4751 static const size_t kOpacityMaxListSize = kOpacityMaxChildCount * 2;
4754 * Recursively iterates through |aList| and collects at most
4755 * |kOpacityMaxChildCount| display item pointers to items that return true for
4756 * CanApplyOpacity(). The item pointers are added to |aArray|.
4758 * LayerEventRegions and WrapList items are ignored.
4760 * We need to do this recursively, because the child display items might contain
4761 * nested nsDisplayWrapLists.
4763 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4764 * item that returns false for CanApplyOpacity() is encountered.
4765 * Otherwise returns true.
4767 static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager,
4768 nsDisplayListBuilder* aBuilder,
4769 nsDisplayList* aList,
4770 nsTArray<nsPaintedDisplayItem*>& aArray) {
4771 if (aList->Length() > kOpacityMaxListSize) {
4772 // Exit early, since |aList| will likely contain more than
4773 // |kOpacityMaxChildCount| items.
4774 return false;
4777 for (nsDisplayItem* i : *aList) {
4778 const DisplayItemType type = i->GetType();
4780 if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
4781 continue;
4784 // Descend only into wraplists.
4785 if (type == DisplayItemType::TYPE_WRAP_LIST ||
4786 type == DisplayItemType::TYPE_CONTAINER) {
4787 // The current display item has children, process them first.
4788 if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(),
4789 aArray)) {
4790 return false;
4793 continue;
4796 if (aArray.Length() == kOpacityMaxChildCount) {
4797 return false;
4800 auto* item = i->AsPaintedDisplayItem();
4801 if (!item || !item->CanApplyOpacity(aManager, aBuilder)) {
4802 return false;
4805 aArray.AppendElement(item);
4808 return true;
4811 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager,
4812 nsDisplayListBuilder* aBuilder) {
4813 if (mChildOpacityState == ChildOpacityState::Deferred) {
4814 return false;
4817 // Iterate through the child display list and copy at most
4818 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4819 AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items;
4820 if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) {
4821 mChildOpacityState = ChildOpacityState::Deferred;
4822 return false;
4825 struct {
4826 nsPaintedDisplayItem* item{};
4827 nsRect bounds;
4828 } children[kOpacityMaxChildCount];
4830 bool snap;
4831 size_t childCount = 0;
4832 for (nsPaintedDisplayItem* item : items) {
4833 children[childCount].item = item;
4834 children[childCount].bounds = item->GetBounds(aBuilder, &snap);
4835 childCount++;
4838 for (size_t i = 0; i < childCount; i++) {
4839 for (size_t j = i + 1; j < childCount; j++) {
4840 if (children[i].bounds.Intersects(children[j].bounds)) {
4841 mChildOpacityState = ChildOpacityState::Deferred;
4842 return false;
4847 mChildOpacityState = ChildOpacityState::Applied;
4848 return true;
4852 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4853 * that has the same frame as the opacity item, and that supports painting with
4854 * opacity. In this case the opacity item can be optimized away.
4856 bool nsDisplayOpacity::ApplyToMask() {
4857 if (mList.Length() != 1) {
4858 return false;
4861 nsDisplayItem* item = mList.GetBottom();
4862 if (item->Frame() != mFrame) {
4863 // The effect item needs to have the same frame as the opacity item.
4864 return false;
4867 const DisplayItemType type = item->GetType();
4868 if (type == DisplayItemType::TYPE_MASK) {
4869 return true;
4872 return false;
4875 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4876 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder,
4877 float aInheritedOpacity) {
4878 if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
4879 mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
4880 // If we've been split, then we might need to merge, so
4881 // don't flatten us away.
4882 return false;
4885 if (mNeedsActiveLayer || mOpacity == 0.0) {
4886 // If our opacity is zero then we'll discard all descendant display items
4887 // except for layer event regions, so there's no point in doing this
4888 // optimization (and if we do do it, then invalidations of those descendants
4889 // might trigger repainting).
4890 return false;
4893 if (mList.IsEmpty()) {
4894 return false;
4897 // We can only flatten opacity items into a mask if we haven't
4898 // already flattened an earlier ancestor, since the SVG code pulls the opacity
4899 // from style directly, and won't know about the outer opacity value.
4900 if (aInheritedOpacity == 1.0f && ApplyToMask()) {
4901 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame));
4902 mChildOpacityState = ChildOpacityState::Applied;
4903 return true;
4906 // Return true if we successfully applied opacity to child items.
4907 return CanApplyToChildren(aManager, aBuilder);
4910 void nsDisplayOpacity::ComputeInvalidationRegion(
4911 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4912 nsRegion* aInvalidRegion) const {
4913 const auto* geometry =
4914 static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
4916 bool snap;
4917 if (mOpacity != geometry->mOpacity) {
4918 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4922 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) {
4923 aStream << " (opacity " << mOpacity << ", mChildOpacityState: ";
4924 switch (mChildOpacityState) {
4925 case ChildOpacityState::Unknown:
4926 aStream << "Unknown";
4927 break;
4928 case ChildOpacityState::Applied:
4929 aStream << "Applied";
4930 break;
4931 case ChildOpacityState::Deferred:
4932 aStream << "Deferred";
4933 break;
4934 default:
4935 break;
4938 aStream << ")";
4941 bool nsDisplayOpacity::CreateWebRenderCommands(
4942 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4943 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4944 nsDisplayListBuilder* aDisplayListBuilder) {
4945 MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied);
4946 float oldOpacity = aBuilder.GetInheritedOpacity();
4947 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
4948 aBuilder.SetInheritedOpacity(1.0f);
4949 aBuilder.SetInheritedClipChain(nullptr);
4950 float opacity = mOpacity * oldOpacity;
4951 float* opacityForSC = &opacity;
4953 uint64_t animationsId =
4954 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
4955 wr::WrAnimationProperty prop{
4956 wr::WrAnimationType::Opacity,
4957 animationsId,
4960 wr::StackingContextParams params;
4961 params.animation = animationsId ? &prop : nullptr;
4962 params.opacity = opacityForSC;
4963 params.clip =
4964 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
4965 if (mWrapsBackdropFilter) {
4966 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
4968 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
4969 params);
4971 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4972 &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
4973 aBuilder.SetInheritedOpacity(oldOpacity);
4974 aBuilder.SetInheritedClipChain(oldClipChain);
4975 return true;
4978 nsDisplayBlendMode::nsDisplayBlendMode(
4979 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4980 StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
4981 const bool aIsForBackground)
4982 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4983 mBlendMode(aBlendMode),
4984 mIsForBackground(aIsForBackground) {
4985 MOZ_COUNT_CTOR(nsDisplayBlendMode);
4988 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4989 bool* aSnap) const {
4990 *aSnap = false;
4991 // We are never considered opaque
4992 return nsRegion();
4995 bool nsDisplayBlendMode::CreateWebRenderCommands(
4996 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4997 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4998 nsDisplayListBuilder* aDisplayListBuilder) {
4999 wr::StackingContextParams params;
5000 params.mix_blend_mode =
5001 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
5002 params.clip =
5003 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5004 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5005 params);
5007 return nsDisplayWrapList::CreateWebRenderCommands(
5008 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5011 void nsDisplayBlendMode::Paint(nsDisplayListBuilder* aBuilder,
5012 gfxContext* aCtx) {
5013 // This should be switched to use PushLayerWithBlend, once it's
5014 // been implemented for all DrawTarget backends.
5015 DrawTarget* dt = aCtx->GetDrawTarget();
5016 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5017 Rect rect = NSRectToRect(GetPaintRect(aBuilder, aCtx), appUnitsPerDevPixel);
5018 rect.RoundOut();
5020 // Create a temporary DrawTarget that is clipped to the area that
5021 // we're going to draw to. This will include the same transform as
5022 // is currently on |dt|.
5023 RefPtr<DrawTarget> temp =
5024 dt->CreateClippedDrawTarget(rect, SurfaceFormat::B8G8R8A8);
5025 if (!temp) {
5026 return;
5029 gfxContext ctx(temp, /* aPreserveTransform */ true);
5031 GetChildren()->Paint(aBuilder, &ctx,
5032 mFrame->PresContext()->AppUnitsPerDevPixel());
5034 // Draw the temporary DT to the real destination, applying the blend mode, but
5035 // no transform.
5036 temp->Flush();
5037 RefPtr<SourceSurface> surface = temp->Snapshot();
5038 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
5039 dt->SetTransform(Matrix());
5040 dt->DrawSurface(
5041 surface, Rect(surface->GetRect()), Rect(surface->GetRect()),
5042 DrawSurfaceOptions(),
5043 DrawOptions(1.0f, nsCSSRendering::GetGFXBlendMode(mBlendMode)));
5046 gfx::CompositionOp nsDisplayBlendMode::BlendMode() {
5047 return nsCSSRendering::GetGFXBlendMode(mBlendMode);
5050 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const {
5051 // Items for the same content element should be merged into a single
5052 // compositing group.
5053 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
5054 !HasSameContent(aItem)) {
5055 return false;
5058 const auto* item = static_cast<const nsDisplayBlendMode*>(aItem);
5059 if (mIsForBackground || item->mIsForBackground) {
5060 // Don't merge background-blend-mode items
5061 return false;
5064 return true;
5067 /* static */
5068 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForMixBlendMode(
5069 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5070 const ActiveScrolledRoot* aActiveScrolledRoot) {
5071 return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
5072 aActiveScrolledRoot, false);
5075 /* static */
5076 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5077 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5078 nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot) {
5079 if (aSecondaryFrame) {
5080 auto type = GetTableTypeFromFrame(aFrame);
5081 auto index = static_cast<uint16_t>(type);
5083 return MakeDisplayItemWithIndex<nsDisplayTableBlendContainer>(
5084 aBuilder, aSecondaryFrame, index, aList, aActiveScrolledRoot, true,
5085 aFrame);
5088 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>(
5089 aBuilder, aFrame, 1, aList, aActiveScrolledRoot, true);
5092 nsDisplayBlendContainer::nsDisplayBlendContainer(
5093 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5094 const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground)
5095 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5096 mIsForBackground(aIsForBackground) {
5097 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
5100 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder* aBuilder,
5101 gfxContext* aCtx) {
5102 aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5103 GetChildren()->Paint(aBuilder, aCtx,
5104 mFrame->PresContext()->AppUnitsPerDevPixel());
5105 aCtx->GetDrawTarget()->PopLayer();
5108 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5109 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5110 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5111 nsDisplayListBuilder* aDisplayListBuilder) {
5112 wr::StackingContextParams params;
5113 params.flags |= wr::StackingContextFlags::IS_BLEND_CONTAINER;
5114 params.clip =
5115 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5116 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5117 params);
5119 return nsDisplayWrapList::CreateWebRenderCommands(
5120 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5123 nsDisplayOwnLayer::nsDisplayOwnLayer(
5124 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5125 const ActiveScrolledRoot* aActiveScrolledRoot,
5126 nsDisplayOwnLayerFlags aFlags, const ScrollbarData& aScrollbarData,
5127 bool aForceActive, bool aClearClipChain)
5128 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
5129 aClearClipChain),
5130 mFlags(aFlags),
5131 mScrollbarData(aScrollbarData),
5132 mForceActive(aForceActive),
5133 mWrAnimationId(0) {
5134 MOZ_COUNT_CTOR(nsDisplayOwnLayer);
5137 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5138 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb;
5141 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5142 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container;
5145 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5146 return IsScrollbarContainer() && IsScrollbarLayerForRoot();
5149 bool nsDisplayOwnLayer::IsScrollbarLayerForRoot() const {
5150 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5151 mScrollbarData.mTargetViewId ==
5152 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5155 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5156 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM;
5159 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5160 return GetType() == DisplayItemType::TYPE_FIXED_POSITION ||
5161 GetType() == DisplayItemType::TYPE_TABLE_FIXED_POSITION;
5164 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5165 return GetType() == DisplayItemType::TYPE_STICKY_POSITION;
5168 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5169 if (!mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
5170 return false;
5172 return mFrame->PresContext()->HasDynamicToolbar() ||
5173 // For tests on Android, this pref is set to simulate the dynamic
5174 // toolbar
5175 StaticPrefs::apz_fixed_margin_override_enabled();
5178 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5179 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5180 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5181 nsDisplayListBuilder* aDisplayListBuilder) {
5182 Maybe<wr::WrAnimationProperty> prop;
5183 bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() &&
5184 (IsScrollThumbLayer() || IsZoomingLayer() ||
5185 ShouldGetFixedOrStickyAnimationId() ||
5186 (IsRootScrollbarContainer() && HasDynamicToolbar()));
5188 if (needsProp) {
5189 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5190 // to create and set an animation id. That way APZ can adjust the position/
5191 // zoom of this content asynchronously as needed.
5192 RefPtr<WebRenderAPZAnimationData> animationData =
5193 aManager->CommandBuilder()
5194 .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(this);
5195 mWrAnimationId = animationData->GetAnimationId();
5197 prop.emplace();
5198 prop->id = mWrAnimationId;
5199 prop->key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5200 wr::SpatialKeyKind::APZ);
5201 prop->effect_type = wr::WrAnimationType::Transform;
5204 wr::StackingContextParams params;
5205 params.animation = prop.ptrOr(nullptr);
5206 params.clip =
5207 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5208 if (IsScrollbarContainer() && IsRootScrollbarContainer()) {
5209 params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER;
5211 if (IsZoomingLayer() ||
5212 (ShouldGetFixedOrStickyAnimationId() ||
5213 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5214 params.is_2d_scale_translation = true;
5215 params.should_snap = true;
5218 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5219 params);
5221 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
5222 aDisplayListBuilder);
5223 return true;
5226 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData,
5227 WebRenderLayerScrollData* aLayerData) {
5228 bool isRelevantToApz =
5229 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5230 ShouldGetFixedOrStickyAnimationId());
5232 if (!isRelevantToApz) {
5233 return false;
5236 if (!aLayerData) {
5237 return true;
5240 if (IsZoomingLayer()) {
5241 aLayerData->SetZoomAnimationId(mWrAnimationId);
5242 return true;
5245 if (IsFixedPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5246 aLayerData->SetFixedPositionAnimationId(mWrAnimationId);
5247 return true;
5250 if (IsStickyPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5251 aLayerData->SetStickyPositionAnimationId(mWrAnimationId);
5252 return true;
5255 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5257 aLayerData->SetScrollbarData(mScrollbarData);
5259 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5260 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5261 return true;
5264 if (IsScrollThumbLayer()) {
5265 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5266 LayoutDeviceRect bounds = LayoutDeviceIntRect::FromAppUnits(
5267 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
5268 // Subframe scrollbars are subject to the pinch-zoom scale,
5269 // but root scrollbars are not because they are outside of the
5270 // region that is zoomed.
5271 const float resolution =
5272 IsScrollbarLayerForRoot()
5273 ? 1.0f
5274 : mFrame->PresShell()->GetCumulativeResolution();
5275 LayerIntRect layerBounds =
5276 RoundedOut(bounds * LayoutDeviceToLayerScale(resolution));
5277 aLayerData->SetVisibleRect(layerBounds);
5279 return true;
5282 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) {
5283 aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
5284 (int)mFlags, mScrollbarData.mTargetViewId)
5285 .get();
5288 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
5289 nsIFrame* aFrame,
5290 nsSubDocumentFrame* aSubDocFrame,
5291 nsDisplayList* aList,
5292 nsDisplayOwnLayerFlags aFlags)
5293 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5294 aBuilder->CurrentActiveScrolledRoot(), aFlags),
5295 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5296 mShouldFlatten(false),
5297 mSubDocFrame(aSubDocFrame) {
5298 MOZ_COUNT_CTOR(nsDisplaySubDocument);
5300 if (mSubDocFrame && mSubDocFrame != mFrame) {
5301 mSubDocFrame->AddDisplayItem(this);
5305 nsDisplaySubDocument::~nsDisplaySubDocument() {
5306 MOZ_COUNT_DTOR(nsDisplaySubDocument);
5307 if (mSubDocFrame) {
5308 mSubDocFrame->RemoveDisplayItem(this);
5312 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const {
5313 return mSubDocFrame ? mSubDocFrame : mFrame;
5316 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) {
5317 if (aFrame == mSubDocFrame) {
5318 mSubDocFrame = nullptr;
5319 SetDeletedFrame();
5321 nsDisplayOwnLayer::RemoveFrame(aFrame);
5324 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder,
5325 nsIFrame* aFrame) {
5326 return aBuilder->IsPaintingToWindow() &&
5327 DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext());
5330 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder,
5331 bool* aSnap) const {
5332 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5334 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5335 usingDisplayPort) {
5336 *aSnap = false;
5337 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
5340 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
5343 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5344 bool* aSnap) const {
5345 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5347 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5348 usingDisplayPort) {
5349 *aSnap = false;
5350 return nsRegion();
5353 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
5356 /* static */
5357 nsDisplayFixedPosition* nsDisplayFixedPosition::CreateForFixedBackground(
5358 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5359 nsDisplayBackgroundImage* aImage, const uint16_t aIndex,
5360 const ActiveScrolledRoot* aScrollTargetASR) {
5361 nsDisplayList temp(aBuilder);
5362 temp.AppendToTop(aImage);
5364 if (aSecondaryFrame) {
5365 auto tableType = GetTableTypeFromFrame(aFrame);
5366 const uint16_t index = CalculateTablePerFrameKey(aIndex + 1, tableType);
5367 return MakeDisplayItemWithIndex<nsDisplayTableFixedPosition>(
5368 aBuilder, aSecondaryFrame, index, &temp, aFrame, aScrollTargetASR);
5371 return MakeDisplayItemWithIndex<nsDisplayFixedPosition>(
5372 aBuilder, aFrame, aIndex + 1, &temp, aScrollTargetASR);
5375 nsDisplayFixedPosition::nsDisplayFixedPosition(
5376 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5377 const ActiveScrolledRoot* aActiveScrolledRoot,
5378 const ActiveScrolledRoot* aScrollTargetASR)
5379 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5380 mScrollTargetASR(aScrollTargetASR),
5381 mIsFixedBackground(false) {
5382 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5385 nsDisplayFixedPosition::nsDisplayFixedPosition(
5386 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5387 const ActiveScrolledRoot* aScrollTargetASR)
5388 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5389 aBuilder->CurrentActiveScrolledRoot()),
5390 // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5391 mScrollTargetASR(aScrollTargetASR),
5392 mIsFixedBackground(true) {
5393 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5396 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() const {
5397 if (mScrollTargetASR &&
5398 (mIsFixedBackground || !nsLayoutUtils::IsReallyFixedPos(mFrame))) {
5399 return mScrollTargetASR->GetViewId();
5401 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5404 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5405 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5406 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5407 nsDisplayListBuilder* aDisplayListBuilder) {
5408 SideBits sides = SideBits::eNone;
5409 if (!mIsFixedBackground) {
5410 sides = nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5413 // We install this RAII scrolltarget tracker so that any
5414 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5415 // share the same ASR as this item) use the correct scroll target. That way
5416 // attempts to scroll on those items will scroll the root scroll frame.
5417 wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker(
5418 aBuilder, GetActiveScrolledRoot(), GetScrollTargetId(), sides);
5419 return nsDisplayOwnLayer::CreateWebRenderCommands(
5420 aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
5423 bool nsDisplayFixedPosition::UpdateScrollData(
5424 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5425 if (aLayerData) {
5426 if (!mIsFixedBackground) {
5427 aLayerData->SetFixedPositionSides(
5428 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame));
5430 aLayerData->SetFixedPositionScrollContainerId(GetScrollTargetId());
5432 nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5433 return true;
5436 bool nsDisplayFixedPosition::ShouldGetFixedOrStickyAnimationId() {
5437 #if defined(MOZ_WIDGET_ANDROID)
5438 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5439 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) ==
5440 GetScrollTargetId();
5441 #else
5442 return false;
5443 #endif
5446 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream& aStream) {
5447 aStream << nsPrintfCString(
5448 " (containerASR %s) (scrolltarget %" PRIu64 ")",
5449 ActiveScrolledRoot::ToString(mScrollTargetASR).get(),
5450 GetScrollTargetId())
5451 .get();
5454 TableType GetTableTypeFromFrame(nsIFrame* aFrame) {
5455 if (aFrame->IsTableFrame()) {
5456 return TableType::Table;
5459 if (aFrame->IsTableColFrame()) {
5460 return TableType::TableCol;
5463 if (aFrame->IsTableColGroupFrame()) {
5464 return TableType::TableColGroup;
5467 if (aFrame->IsTableRowFrame()) {
5468 return TableType::TableRow;
5471 if (aFrame->IsTableRowGroupFrame()) {
5472 return TableType::TableRowGroup;
5475 if (aFrame->IsTableCellFrame()) {
5476 return TableType::TableCell;
5479 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5480 return TableType::Table;
5483 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5484 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5485 nsIFrame* aAncestorFrame, const ActiveScrolledRoot* aScrollTargetASR)
5486 : nsDisplayFixedPosition(aBuilder, aFrame, aList, aScrollTargetASR),
5487 mAncestorFrame(aAncestorFrame) {
5488 if (aBuilder->IsRetainingDisplayList()) {
5489 mAncestorFrame->AddDisplayItem(this);
5493 nsDisplayStickyPosition::nsDisplayStickyPosition(
5494 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5495 const ActiveScrolledRoot* aActiveScrolledRoot,
5496 const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
5497 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5498 mContainerASR(aContainerASR),
5499 mClippedToDisplayPort(aClippedToDisplayPort),
5500 mShouldFlatten(false) {
5501 MOZ_COUNT_CTOR(nsDisplayStickyPosition);
5504 // Returns the smallest distance from "0" to the range [min, max] where
5505 // min <= max. Despite the name, the return value is actually a 1-D vector,
5506 // and so may be negative if max < 0.
5507 static nscoord DistanceToRange(nscoord min, nscoord max) {
5508 MOZ_ASSERT(min <= max);
5509 if (max < 0) {
5510 return max;
5512 if (min > 0) {
5513 return min;
5515 MOZ_ASSERT(min <= 0 && max >= 0);
5516 return 0;
5519 // Returns the magnitude of the part of the range [min, max] that is greater
5520 // than zero. The return value is always non-negative.
5521 static nscoord PositivePart(nscoord min, nscoord max) {
5522 MOZ_ASSERT(min <= max);
5523 if (min >= 0) {
5524 return max - min;
5526 if (max > 0) {
5527 return max;
5529 return 0;
5532 // Returns the magnitude of the part of the range [min, max] that is less
5533 // than zero. The return value is always non-negative.
5534 static nscoord NegativePart(nscoord min, nscoord max) {
5535 MOZ_ASSERT(min <= max);
5536 if (max <= 0) {
5537 return max - min;
5539 if (min < 0) {
5540 return 0 - min;
5542 return 0;
5545 StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() {
5546 StickyScrollContainer* stickyScrollContainer =
5547 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
5548 if (stickyScrollContainer) {
5549 // If there's no ASR for the scrollframe that this sticky item is attached
5550 // to, then don't create a WR sticky item for it either. Trying to do so
5551 // will end in sadness because WR will interpret some coordinates as
5552 // relative to the nearest enclosing scrollframe, which will correspond
5553 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5554 // same as the scrollframe this sticky item is actually supposed to be
5555 // attached to, thus the sadness.
5556 // Not sending WR the sticky item is ok, because the enclosing scrollframe
5557 // will never be asynchronously scrolled. Instead we will always position
5558 // the sticky items correctly on the gecko side and WR will never need to
5559 // adjust their position itself.
5560 MOZ_ASSERT(
5561 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5562 if (!stickyScrollContainer->ScrollFrame()
5563 ->IsMaybeAsynchronouslyScrolled()) {
5564 stickyScrollContainer = nullptr;
5567 return stickyScrollContainer;
5570 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5571 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5572 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5573 nsDisplayListBuilder* aDisplayListBuilder) {
5574 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5576 Maybe<wr::SpaceAndClipChainHelper> saccHelper;
5578 if (stickyScrollContainer) {
5579 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5581 bool snap;
5582 nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
5584 Maybe<float> topMargin;
5585 Maybe<float> rightMargin;
5586 Maybe<float> bottomMargin;
5587 Maybe<float> leftMargin;
5588 wr::StickyOffsetBounds vBounds = {0.0, 0.0};
5589 wr::StickyOffsetBounds hBounds = {0.0, 0.0};
5590 nsPoint appliedOffset;
5592 nsRectAbsolute outer;
5593 nsRectAbsolute inner;
5594 stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5596 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
5597 nsPoint offset =
5598 scrollFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5600 // Adjust the scrollPort coordinates to be relative to the reference frame,
5601 // so that it is in the same space as everything else.
5602 nsRect scrollPort =
5603 stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
5604 scrollPort += offset;
5606 // The following computations make more sense upon understanding the
5607 // semantics of "inner" and "outer", which is explained in the comment on
5608 // SetStickyPositionData in Layers.h.
5610 if (outer.YMost() != inner.YMost()) {
5611 // Question: How far will itemBounds.y be from the top of the scrollport
5612 // when we have scrolled from the current scroll position of "0" to
5613 // reach the range [inner.YMost(), outer.YMost()] where the item gets
5614 // stuck?
5615 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5616 // needs to be adjusted by the distance to the range, less any other
5617 // sticky ranges that fall between 0 and the range. If the distance is
5618 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5619 // scrolling upwards (decreasing scroll offset) to reach that range,
5620 // which would increase itemBounds.y and make it farther away from the
5621 // top of the scrollport. So in that case the adjustment is -distance.
5622 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5623 // we would be scrolling downwards, itemBounds.y would decrease, and we
5624 // again need to adjust by -distance. If we are already in the range
5625 // then no adjustment is needed and distance is 0 so again using
5626 // -distance works. If the distance is positive, and the item has both
5627 // top and bottom sticky ranges, then the bottom sticky range may fall
5628 // (entirely[1] or partly[2]) between the current scroll position.
5629 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5630 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5631 // In these cases, the item doesn't actually move for that part of the
5632 // distance, so we need to subtract out that bit, which can be computed
5633 // as the positive portion of the range [outer.Y(), inner.Y()].
5634 nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
5635 if (distance > 0) {
5636 distance -= PositivePart(outer.Y(), inner.Y());
5638 topMargin = Some(NSAppUnitsToFloatPixels(
5639 itemBounds.y - scrollPort.y - distance, auPerDevPixel));
5640 // Question: What is the maximum positive ("downward") offset that WR
5641 // will have to apply to this item in order to prevent the item from
5642 // visually moving?
5643 // Answer: Since the item is "sticky" in the range [inner.YMost(),
5644 // outer.YMost()], the maximum offset will be the size of the range, which
5645 // is outer.YMost() - inner.YMost().
5646 vBounds.max =
5647 NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
5648 // Question: how much of an offset has layout already applied to the item?
5649 // Answer: if we are
5650 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5651 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5652 // then layout has already applied some offset to the position of the
5653 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5654 // and |outer.YMost() - inner.YMost()| in case (b).
5655 if (inner.YMost() < 0) {
5656 appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
5657 MOZ_ASSERT(appliedOffset.y > 0);
5660 if (outer.Y() != inner.Y()) {
5661 // Similar logic as in the previous section, but this time we care about
5662 // the distance from itemBounds.YMost() to scrollPort.YMost().
5663 nscoord distance = DistanceToRange(outer.Y(), inner.Y());
5664 if (distance < 0) {
5665 distance += NegativePart(inner.YMost(), outer.YMost());
5667 bottomMargin = Some(NSAppUnitsToFloatPixels(
5668 scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
5669 // And here WR will be moving the item upwards rather than downwards so
5670 // again things are inverted from the previous block.
5671 vBounds.min =
5672 NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
5673 // We can't have appliedOffset be both positive and negative, and the top
5674 // adjustment takes priority. So here we only update appliedOffset.y if
5675 // it wasn't set by the top-sticky case above.
5676 if (appliedOffset.y == 0 && inner.Y() > 0) {
5677 appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
5678 MOZ_ASSERT(appliedOffset.y < 0);
5681 // Same as above, but for the x-axis
5682 if (outer.XMost() != inner.XMost()) {
5683 nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
5684 if (distance > 0) {
5685 distance -= PositivePart(outer.X(), inner.X());
5687 leftMargin = Some(NSAppUnitsToFloatPixels(
5688 itemBounds.x - scrollPort.x - distance, auPerDevPixel));
5689 hBounds.max =
5690 NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
5691 if (inner.XMost() < 0) {
5692 appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
5693 MOZ_ASSERT(appliedOffset.x > 0);
5696 if (outer.X() != inner.X()) {
5697 nscoord distance = DistanceToRange(outer.X(), inner.X());
5698 if (distance < 0) {
5699 distance += NegativePart(inner.XMost(), outer.XMost());
5701 rightMargin = Some(NSAppUnitsToFloatPixels(
5702 scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
5703 hBounds.min =
5704 NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
5705 if (appliedOffset.x == 0 && inner.X() > 0) {
5706 appliedOffset.x = std::max(0, outer.X()) - inner.X();
5707 MOZ_ASSERT(appliedOffset.x < 0);
5711 LayoutDeviceRect bounds =
5712 LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
5713 wr::LayoutVector2D applied = {
5714 NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
5715 NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
5716 wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame(
5717 wr::ToLayoutRect(bounds), topMargin.ptrOr(nullptr),
5718 rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
5719 leftMargin.ptrOr(nullptr), vBounds, hBounds, applied,
5720 wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5721 wr::SpatialKeyKind::Sticky));
5723 saccHelper.emplace(aBuilder, spatialId);
5724 aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId);
5728 wr::StackingContextParams params;
5729 params.clip =
5730 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5731 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
5732 aBuilder, params);
5733 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc,
5734 aManager, aDisplayListBuilder);
5737 if (stickyScrollContainer) {
5738 aManager->CommandBuilder().PopOverrideForASR(mContainerASR);
5741 return true;
5744 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5745 StickyScrollContainer* aStickyScrollContainer, float aAppUnitsPerDevPixel,
5746 float aScaleX, float aScaleY, LayerRectAbsolute& aStickyOuter,
5747 LayerRectAbsolute& aStickyInner) {
5748 nsRectAbsolute outer;
5749 nsRectAbsolute inner;
5750 aStickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5751 aStickyOuter.SetBox(
5752 NSAppUnitsToFloatPixels(outer.X(), aAppUnitsPerDevPixel) * aScaleX,
5753 NSAppUnitsToFloatPixels(outer.Y(), aAppUnitsPerDevPixel) * aScaleY,
5754 NSAppUnitsToFloatPixels(outer.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5755 NSAppUnitsToFloatPixels(outer.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5756 aStickyInner.SetBox(
5757 NSAppUnitsToFloatPixels(inner.X(), aAppUnitsPerDevPixel) * aScaleX,
5758 NSAppUnitsToFloatPixels(inner.Y(), aAppUnitsPerDevPixel) * aScaleY,
5759 NSAppUnitsToFloatPixels(inner.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5760 NSAppUnitsToFloatPixels(inner.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5763 bool nsDisplayStickyPosition::UpdateScrollData(
5764 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5765 bool hasDynamicToolbar = HasDynamicToolbar();
5766 if (aLayerData && hasDynamicToolbar) {
5767 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5768 if (stickyScrollContainer) {
5769 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5770 float cumulativeResolution =
5771 mFrame->PresShell()->GetCumulativeResolution();
5772 LayerRectAbsolute stickyOuter;
5773 LayerRectAbsolute stickyInner;
5774 CalculateLayerScrollRanges(stickyScrollContainer, auPerDevPixel,
5775 cumulativeResolution, cumulativeResolution,
5776 stickyOuter, stickyInner);
5777 aLayerData->SetStickyScrollRangeOuter(stickyOuter);
5778 aLayerData->SetStickyScrollRangeInner(stickyInner);
5780 SideBits sides =
5781 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5782 aLayerData->SetFixedPositionSides(sides);
5784 ScrollableLayerGuid::ViewID scrollId =
5785 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5786 ->GetScrolledFrame()
5787 ->GetContent());
5788 aLayerData->SetStickyPositionScrollContainerId(scrollId);
5791 // Return true if either there is a dynamic toolbar affecting this sticky
5792 // item or the OwnLayer base implementation returns true for some other
5793 // reason.
5794 bool ret = hasDynamicToolbar;
5795 ret |= nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5796 return ret;
5799 bool nsDisplayStickyPosition::ShouldGetFixedOrStickyAnimationId() {
5800 #if defined(MOZ_WIDGET_ANDROID)
5801 if (HasDynamicToolbar()) { // also implies being in the cross-process RCD
5802 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5803 if (stickyScrollContainer) {
5804 ScrollableLayerGuid::ViewID scrollId =
5805 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5806 ->GetScrolledFrame()
5807 ->GetContent());
5808 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) ==
5809 scrollId;
5812 #endif
5813 return false;
5816 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5817 nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
5818 nsIFrame* aScrollFrame, const CompositorHitTestInfo& aHitInfo,
5819 const nsRect& aHitArea)
5820 : nsDisplayWrapList(aBuilder, aScrollFrame),
5821 mScrollFrame(aScrollFrame),
5822 mScrolledFrame(aScrolledFrame),
5823 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5824 mHitInfo(aHitInfo),
5825 mHitArea(aHitArea) {
5826 #ifdef NS_BUILD_REFCNT_LOGGING
5827 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
5828 #endif
5831 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5832 nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) {
5833 ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
5834 mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(),
5835 ToReferenceFrame(), aLayerManager, mScrollParentId,
5836 mScrollFrame->GetSize(), false);
5837 metadata.GetMetrics().SetIsScrollInfoLayer(true);
5838 nsIScrollableFrame* scrollableFrame = mScrollFrame->GetScrollTargetFrame();
5839 if (scrollableFrame) {
5840 aBuilder->AddScrollFrameToNotify(scrollableFrame);
5843 return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
5846 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5847 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5848 if (aLayerData) {
5849 UniquePtr<ScrollMetadata> metadata =
5850 ComputeScrollMetadata(aData->GetBuilder(), aData->GetManager());
5851 MOZ_ASSERT(aData);
5852 MOZ_ASSERT(metadata);
5853 aLayerData->AppendScrollMetadata(*aData, *metadata);
5855 return true;
5858 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5859 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5860 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5861 nsDisplayListBuilder* aDisplayListBuilder) {
5862 ScrollableLayerGuid::ViewID scrollId =
5863 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame->GetContent());
5865 const LayoutDeviceRect devRect = LayoutDeviceRect::FromAppUnits(
5866 mHitArea, mScrollFrame->PresContext()->AppUnitsPerDevPixel());
5868 const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
5870 aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, mHitInfo,
5871 SideBits::eNone);
5873 return true;
5876 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) {
5877 aStream << " (scrollframe " << mScrollFrame << " scrolledFrame "
5878 << mScrolledFrame << ")";
5881 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5882 nsSubDocumentFrame* aSubDocFrame,
5883 nsDisplayList* aList, int32_t aAPD,
5884 int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags)
5885 : nsDisplaySubDocument(aBuilder, aFrame, aSubDocFrame, aList, aFlags),
5886 mAPD(aAPD),
5887 mParentAPD(aParentAPD) {
5888 MOZ_COUNT_CTOR(nsDisplayZoom);
5891 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder,
5892 bool* aSnap) const {
5893 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
5894 *aSnap = false;
5895 return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD);
5898 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5899 HitTestState* aState,
5900 nsTArray<nsIFrame*>* aOutFrames) {
5901 nsRect rect;
5902 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5903 // rect as well instead of possibly rounding the width or height to zero.
5904 if (aRect.width == 1 && aRect.height == 1) {
5905 rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD));
5906 rect.width = rect.height = 1;
5907 } else {
5908 rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
5910 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5913 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5914 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5915 const ActiveScrolledRoot* aActiveScrolledRoot, FrameMetrics::ViewID aViewID)
5916 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5917 mViewID(aViewID) {
5918 MOZ_COUNT_CTOR(nsDisplayAsyncZoom);
5921 #ifdef NS_BUILD_REFCNT_LOGGING
5922 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5923 MOZ_COUNT_DTOR(nsDisplayAsyncZoom);
5925 #endif
5927 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder* aBuilder,
5928 const nsRect& aRect, HitTestState* aState,
5929 nsTArray<nsIFrame*>* aOutFrames) {
5930 #ifdef DEBUG
5931 nsIScrollableFrame* scrollFrame = do_QueryFrame(mFrame);
5932 MOZ_ASSERT(scrollFrame && ViewportUtils::IsZoomedContentRoot(
5933 scrollFrame->GetScrolledFrame()));
5934 #endif
5935 nsRect rect = ViewportUtils::VisualToLayout(aRect, mFrame->PresShell());
5936 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5939 bool nsDisplayAsyncZoom::UpdateScrollData(
5940 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5941 bool ret = nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5942 MOZ_ASSERT(ret);
5943 if (aLayerData) {
5944 aLayerData->SetAsyncZoomContainerId(mViewID);
5946 return ret;
5949 ///////////////////////////////////////////////////
5950 // nsDisplayTransform Implementation
5953 #ifndef DEBUG
5954 static_assert(sizeof(nsDisplayTransform) <= 512,
5955 "nsDisplayTransform has grown");
5956 #endif
5958 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5959 nsIFrame* aFrame, nsDisplayList* aList,
5960 const nsRect& aChildrenBuildingRect)
5961 : nsPaintedDisplayItem(aBuilder, aFrame),
5962 mChildren(aBuilder),
5963 mTransform(Some(Matrix4x4())),
5964 mChildrenBuildingRect(aChildrenBuildingRect),
5965 mPrerenderDecision(PrerenderDecision::No),
5966 mIsTransformSeparator(true),
5967 mHasTransformGetter(false),
5968 mHasAssociatedPerspective(false),
5969 mContainsASRs(false) {
5970 MOZ_COUNT_CTOR(nsDisplayTransform);
5971 MOZ_ASSERT(aFrame, "Must have a frame!");
5972 Init(aBuilder, aList);
5975 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5976 nsIFrame* aFrame, nsDisplayList* aList,
5977 const nsRect& aChildrenBuildingRect,
5978 PrerenderDecision aPrerenderDecision)
5979 : nsPaintedDisplayItem(aBuilder, aFrame),
5980 mChildren(aBuilder),
5981 mChildrenBuildingRect(aChildrenBuildingRect),
5982 mPrerenderDecision(aPrerenderDecision),
5983 mIsTransformSeparator(false),
5984 mHasTransformGetter(false),
5985 mHasAssociatedPerspective(false),
5986 mContainsASRs(false) {
5987 MOZ_COUNT_CTOR(nsDisplayTransform);
5988 MOZ_ASSERT(aFrame, "Must have a frame!");
5989 SetReferenceFrameToAncestor(aBuilder);
5990 Init(aBuilder, aList);
5993 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5994 nsIFrame* aFrame, nsDisplayList* aList,
5995 const nsRect& aChildrenBuildingRect,
5996 decltype(WithTransformGetter))
5997 : nsPaintedDisplayItem(aBuilder, aFrame),
5998 mChildren(aBuilder),
5999 mChildrenBuildingRect(aChildrenBuildingRect),
6000 mPrerenderDecision(PrerenderDecision::No),
6001 mIsTransformSeparator(false),
6002 mHasTransformGetter(true),
6003 mHasAssociatedPerspective(false),
6004 mContainsASRs(false) {
6005 MOZ_COUNT_CTOR(nsDisplayTransform);
6006 MOZ_ASSERT(aFrame, "Must have a frame!");
6007 MOZ_ASSERT(aFrame->GetTransformGetter());
6008 Init(aBuilder, aList);
6011 void nsDisplayTransform::SetReferenceFrameToAncestor(
6012 nsDisplayListBuilder* aBuilder) {
6013 if (mFrame == aBuilder->RootReferenceFrame()) {
6014 return;
6016 // We manually recompute mToReferenceFrame without going through the
6017 // builder, since this won't apply the 'additional offset'. Our
6018 // children will already be painting with that applied, and we don't
6019 // want to include it a second time in our transform. We don't recompute
6020 // our visible/building rects, since those should still include the additional
6021 // offset.
6022 // TODO: Are there are things computed using our ToReferenceFrame that should
6023 // have the additional offset applied? Should we instead just manually remove
6024 // the offset from our transform instead of this more general value?
6025 // Can we instead apply the additional offset to us and not our children, like
6026 // we do for all other offsets (and how reference frames are supposed to
6027 // work)?
6028 nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame);
6029 const nsIFrame* referenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
6030 mToReferenceFrame = mFrame->GetOffsetToCrossDoc(referenceFrame);
6033 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
6034 nsDisplayList* aChildren) {
6035 mChildren.AppendToTop(aChildren);
6036 UpdateBounds(aBuilder);
6039 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
6040 return false;
6043 /* Returns the delta specified by the transform-origin property.
6044 * This is a positive delta, meaning that it indicates the direction to move
6045 * to get from (0, 0) of the frame to the transform origin. This function is
6046 * called off the main thread.
6048 /* static */
6049 Point3D nsDisplayTransform::GetDeltaToTransformOrigin(
6050 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6051 float aAppUnitsPerPixel) {
6052 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6053 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6054 aFrame->Combines3DTransformWithAncestors(),
6055 "Shouldn't get a delta for an untransformed frame!");
6057 if (!aFrame->IsTransformed()) {
6058 return Point3D();
6061 /* For both of the coordinates, if the value of transform is a
6062 * percentage, it's relative to the size of the frame. Otherwise, if it's
6063 * a distance, it's already computed for us!
6065 const nsStyleDisplay* display = aFrame->StyleDisplay();
6067 const StyleTransformOrigin& transformOrigin = display->mTransformOrigin;
6068 CSSPoint origin = nsStyleTransformMatrix::Convert2DPosition(
6069 transformOrigin.horizontal, transformOrigin.vertical, aRefBox);
6071 // Note:
6072 // 1. SVG frames have a reference box that can be (and typically is) offset
6073 // from the TopLeft() of the frame. We need to account for that here.
6074 // 2. If we are using transform-box:content-box in CSS layout, we have the
6075 // offset from TopLeft() of the frame as well.
6076 origin.x += CSSPixel::FromAppUnits(aRefBox.X());
6077 origin.y += CSSPixel::FromAppUnits(aRefBox.Y());
6079 float scale = AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel);
6080 float z = transformOrigin.depth._0;
6081 return Point3D(origin.x * scale, origin.y * scale, z * scale);
6084 /* static */
6085 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
6086 float aAppUnitsPerPixel,
6087 Matrix4x4& aOutMatrix) {
6088 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6089 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6090 aFrame->Combines3DTransformWithAncestors(),
6091 "Shouldn't get a delta for an untransformed frame!");
6092 MOZ_ASSERT(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
6094 if (!aFrame->IsTransformed()) {
6095 return false;
6098 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6099 // correctly yet (similar to the aBoundsOverride case for
6100 // GetResultingTransformMatrix)?
6101 nsIFrame* perspectiveFrame =
6102 aFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6103 if (!perspectiveFrame) {
6104 return false;
6107 /* Grab the values for perspective and perspective-origin (if present) */
6108 const nsStyleDisplay* perspectiveDisplay = perspectiveFrame->StyleDisplay();
6109 if (perspectiveDisplay->mChildPerspective.IsNone()) {
6110 return false;
6113 MOZ_ASSERT(perspectiveDisplay->mChildPerspective.IsLength());
6114 float perspective =
6115 perspectiveDisplay->mChildPerspective.AsLength().ToCSSPixels();
6116 perspective = std::max(1.0f, perspective);
6117 if (perspective < std::numeric_limits<Float>::epsilon()) {
6118 return true;
6121 TransformReferenceBox refBox(perspectiveFrame);
6123 Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition(
6124 perspectiveDisplay->mPerspectiveOrigin.horizontal,
6125 perspectiveDisplay->mPerspectiveOrigin.vertical, refBox,
6126 aAppUnitsPerPixel);
6128 /* GetOffsetTo computes the offset required to move from 0,0 in
6129 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6130 * this, it's faster to compute this way.
6132 nsPoint frameToPerspectiveOffset = -aFrame->GetOffsetTo(perspectiveFrame);
6133 Point frameToPerspectiveGfxOffset(
6134 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.x, aAppUnitsPerPixel),
6135 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.y, aAppUnitsPerPixel));
6137 /* Move the perspective origin to be relative to aFrame, instead of relative
6138 * to the containing block which is how it was specified in the style system.
6140 perspectiveOrigin += frameToPerspectiveGfxOffset;
6142 aOutMatrix._34 =
6143 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective),
6144 aAppUnitsPerPixel);
6146 aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
6147 return true;
6150 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6151 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6152 float aAppUnitsPerPixel)
6153 : mFrame(aFrame),
6154 mTranslate(aFrame->StyleDisplay()->mTranslate),
6155 mRotate(aFrame->StyleDisplay()->mRotate),
6156 mScale(aFrame->StyleDisplay()->mScale),
6157 mTransform(aFrame->StyleDisplay()->mTransform),
6158 mMotion(aFrame->StyleDisplay()->mOffsetPath.IsNone()
6159 ? Nothing()
6160 : MotionPathUtils::ResolveMotionPath(aFrame, aRefBox)),
6161 mToTransformOrigin(
6162 GetDeltaToTransformOrigin(aFrame, aRefBox, aAppUnitsPerPixel)) {}
6164 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6165 * translates from local coordinate space to transform coordinate space, then
6166 * hands it back.
6168 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6169 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6170 float aAppUnitsPerPixel) {
6171 return GetResultingTransformMatrixInternal(aProperties, aRefBox, nsPoint(),
6172 aAppUnitsPerPixel, 0);
6175 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6176 const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel,
6177 uint32_t aFlags) {
6178 TransformReferenceBox refBox(aFrame);
6179 FrameTransformProperties props(aFrame, refBox, aAppUnitsPerPixel);
6180 return GetResultingTransformMatrixInternal(props, refBox, aOrigin,
6181 aAppUnitsPerPixel, aFlags);
6184 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal(
6185 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6186 const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags) {
6187 const nsIFrame* frame = aProperties.mFrame;
6188 NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE),
6189 "Must have a frame to compute perspective!");
6191 // IncrementScaleRestyleCountIfNeeded in ActiveLayerTracker.cpp is a
6192 // simplified copy of this function.
6194 // Get the underlying transform matrix:
6196 /* Get the matrix, then change its basis to factor in the origin. */
6197 Matrix4x4 result;
6198 // Call IsSVGTransformed() regardless of the value of
6199 // aProperties.HasTransform(), since we still need any
6200 // potential parentsChildrenOnlyTransform.
6201 Matrix svgTransform, parentsChildrenOnlyTransform;
6202 const bool hasSVGTransforms =
6203 frame && frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
6204 frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform);
6205 bool shouldRound = nsLayoutUtils::ShouldSnapToGrid(frame);
6207 /* Transformed frames always have a transform, or are preserving 3d (and might
6208 * still have perspective!) */
6209 if (aProperties.HasTransform()) {
6210 result = nsStyleTransformMatrix::ReadTransforms(
6211 aProperties.mTranslate, aProperties.mRotate, aProperties.mScale,
6212 aProperties.mMotion.ptrOr(nullptr), aProperties.mTransform, aRefBox,
6213 aAppUnitsPerPixel);
6214 } else if (hasSVGTransforms) {
6215 // Correct the translation components for zoom:
6216 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6217 svgTransform._31 *= pixelsPerCSSPx;
6218 svgTransform._32 *= pixelsPerCSSPx;
6219 result = Matrix4x4::From2D(svgTransform);
6222 // Apply any translation due to 'transform-origin' and/or 'transform-box':
6223 result.ChangeBasis(aProperties.mToTransformOrigin);
6225 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
6226 // an explanation of what children-only transforms are.
6227 const bool parentHasChildrenOnlyTransform =
6228 hasSVGTransforms && !parentsChildrenOnlyTransform.IsIdentity();
6230 if (parentHasChildrenOnlyTransform) {
6231 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6232 parentsChildrenOnlyTransform._31 *= pixelsPerCSSPx;
6233 parentsChildrenOnlyTransform._32 *= pixelsPerCSSPx;
6235 Point3D frameOffset(
6236 NSAppUnitsToFloatPixels(-frame->GetPosition().x, aAppUnitsPerPixel),
6237 NSAppUnitsToFloatPixels(-frame->GetPosition().y, aAppUnitsPerPixel), 0);
6238 Matrix4x4 parentsChildrenOnlyTransform3D =
6239 Matrix4x4::From2D(parentsChildrenOnlyTransform)
6240 .ChangeBasis(frameOffset);
6242 result *= parentsChildrenOnlyTransform3D;
6245 Matrix4x4 perspectiveMatrix;
6246 bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE;
6247 if (hasPerspective) {
6248 if (ComputePerspectiveMatrix(frame, aAppUnitsPerPixel, perspectiveMatrix)) {
6249 result *= perspectiveMatrix;
6253 if ((aFlags & INCLUDE_PRESERVE3D_ANCESTORS) && frame &&
6254 frame->Combines3DTransformWithAncestors()) {
6255 // Include the transform set on our parent
6256 nsIFrame* parentFrame =
6257 frame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6258 NS_ASSERTION(parentFrame && parentFrame->IsTransformed() &&
6259 parentFrame->Extend3DContext(),
6260 "Preserve3D mismatch!");
6261 TransformReferenceBox refBox(parentFrame);
6262 FrameTransformProperties props(parentFrame, refBox, aAppUnitsPerPixel);
6264 uint32_t flags =
6265 aFlags & (INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE);
6267 // If this frame isn't transformed (but we exist for backface-visibility),
6268 // then we're not a reference frame so no offset to origin will be added.
6269 // Otherwise we need to manually translate into our parent's coordinate
6270 // space.
6271 if (frame->IsTransformed()) {
6272 nsLayoutUtils::PostTranslate(result, frame->GetPosition(),
6273 aAppUnitsPerPixel, shouldRound);
6275 Matrix4x4 parent = GetResultingTransformMatrixInternal(
6276 props, refBox, nsPoint(0, 0), aAppUnitsPerPixel, flags);
6277 result = result * parent;
6280 if (aFlags & OFFSET_BY_ORIGIN) {
6281 nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel,
6282 shouldRound);
6285 return result;
6288 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6289 static constexpr nsCSSPropertyIDSet opacitySet =
6290 nsCSSPropertyIDSet::OpacityProperties();
6291 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, opacitySet)) {
6292 return true;
6295 EffectCompositor::SetPerformanceWarning(
6296 mFrame, opacitySet,
6297 AnimationPerformanceWarning(
6298 AnimationPerformanceWarning::Type::OpacityFrameInactive));
6300 return false;
6303 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6304 return mPrerenderDecision != PrerenderDecision::No;
6307 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
6308 nsDisplayListBuilder* aBuilder) {
6309 return StaticPrefs::gfx_omta_background_color();
6312 static bool IsInStickyPositionedSubtree(const nsIFrame* aFrame) {
6313 for (const nsIFrame* frame = aFrame; frame;
6314 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame)) {
6315 if (frame->IsStickyPositioned()) {
6316 return true;
6319 return false;
6322 static bool ShouldUsePartialPrerender(const nsIFrame* aFrame) {
6323 return StaticPrefs::layout_animation_prerender_partial() &&
6324 // Bug 1642547: Support partial prerender for position:sticky elements.
6325 !IsInStickyPositionedSubtree(aFrame);
6328 /* static */
6329 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
6330 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect)
6331 -> PrerenderInfo {
6332 PrerenderInfo result;
6333 // If we are in a preserve-3d tree, and we've disallowed async animations, we
6334 // return No prerender decision directly.
6335 if ((aFrame->Extend3DContext() ||
6336 aFrame->Combines3DTransformWithAncestors()) &&
6337 !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
6338 return result;
6341 // Elements whose transform has been modified recently, or which
6342 // have a compositor-animated transform, can be prerendered. An element
6343 // might have only just had its transform animated in which case
6344 // the ActiveLayerManager may not have been notified yet.
6345 static constexpr nsCSSPropertyIDSet transformSet =
6346 nsCSSPropertyIDSet::TransformLikeProperties();
6347 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame) &&
6348 !EffectCompositor::HasAnimationsForCompositor(
6349 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
6350 EffectCompositor::SetPerformanceWarning(
6351 aFrame, transformSet,
6352 AnimationPerformanceWarning(
6353 AnimationPerformanceWarning::Type::TransformFrameInactive));
6355 // This case happens when we're sure that the frame is not animated and its
6356 // preserve-3d ancestors are not, either. So we don't need to pre-render.
6357 // However, this decision shouldn't affect the decisions for other frames in
6358 // the preserve-3d context. We need this flag to determine whether we should
6359 // block async animations on other frames in the current preserve-3d tree.
6360 result.mHasAnimations = false;
6361 return result;
6364 // We should not allow prerender if any ancestor container element has
6365 // mask/clip-path effects.
6367 // With prerender and async transform animation, we do not need to restyle an
6368 // animated element to respect position changes, since that transform is done
6369 // by layer animation. As a result, the container element is not aware of
6370 // position change of that containing element and loses the chance to update
6371 // the content of mask/clip-path.
6373 // Why do we need to update a mask? This is relative to how we generate a
6374 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
6375 // mask layer, to reduce memory usage, we did not choose the size of the
6376 // masked element as mask size. Instead, we read the union of bounds of all
6377 // children display items by nsDisplayWrapList::GetBounds, which is smaller
6378 // than or equal to the masked element's boundary, and use it as the position
6379 // size of the mask layer. That union bounds is actually affected by the
6380 // geometry of the animated element. To keep the content of mask up to date,
6381 // forbidding of prerender is required.
6382 for (nsIFrame* container =
6383 nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
6384 container;
6385 container = nsLayoutUtils::GetCrossDocParentFrameInProcess(container)) {
6386 const nsStyleSVGReset* svgReset = container->StyleSVGReset();
6387 if (svgReset->HasMask() || svgReset->HasClipPath()) {
6388 return result;
6392 // If the incoming dirty rect already contains the entire overflow area,
6393 // we are already rendering the entire content.
6394 nsRect overflow = aFrame->InkOverflowRectRelativeToSelf();
6395 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
6396 // in cases of non-invertible transforms, so we set `untransformedRect` to
6397 // `aDirtyRect` as an initial value for such cases.
6398 nsRect untransformedDirtyRect = *aDirtyRect;
6399 UntransformRect(*aDirtyRect, overflow, aFrame, &untransformedDirtyRect);
6400 if (untransformedDirtyRect.Contains(overflow)) {
6401 *aDirtyRect = untransformedDirtyRect;
6402 result.mDecision = PrerenderDecision::Full;
6403 return result;
6406 float viewportRatio =
6407 StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
6408 uint32_t absoluteLimitX =
6409 StaticPrefs::layout_animation_prerender_absolute_limit_x();
6410 uint32_t absoluteLimitY =
6411 StaticPrefs::layout_animation_prerender_absolute_limit_y();
6412 nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
6414 float resolution = aFrame->PresShell()->GetCumulativeResolution();
6415 if (resolution < 1.0f) {
6416 refSize.SizeTo(
6417 NSCoordSaturatingNonnegativeMultiply(refSize.width, 1.0f / resolution),
6418 NSCoordSaturatingNonnegativeMultiply(refSize.height,
6419 1.0f / resolution));
6422 // Only prerender if the transformed frame's size is <= a multiple of the
6423 // reference frame size (~viewport), and less than an absolute limit.
6424 // Both the ratio and the absolute limit are configurable.
6425 nscoord maxLength = std::max(nscoord(refSize.width * viewportRatio),
6426 nscoord(refSize.height * viewportRatio));
6427 nsSize relativeLimit(maxLength, maxLength);
6428 nsSize absoluteLimit(
6429 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
6430 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
6431 nsSize maxSize = Min(relativeLimit, absoluteLimit);
6433 const auto transform = nsLayoutUtils::GetTransformToAncestor(
6434 RelativeTo{aFrame},
6435 RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});
6436 const gfxRect transformedBounds = transform.TransformAndClipBounds(
6437 gfxRect(overflow.x, overflow.y, overflow.width, overflow.height),
6438 gfxRect::MaxIntRect());
6439 const nsSize frameSize =
6440 nsSize(transformedBounds.width, transformedBounds.height);
6442 uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
6443 uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
6444 if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
6445 *aDirtyRect = overflow;
6446 result.mDecision = PrerenderDecision::Full;
6447 return result;
6450 if (ShouldUsePartialPrerender(aFrame)) {
6451 *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(
6452 aFrame, untransformedDirtyRect, overflow, maxSize);
6453 result.mDecision = PrerenderDecision::Partial;
6454 return result;
6457 if (frameArea > maxLimitArea) {
6458 uint64_t appUnitsPerPixel = AppUnitsPerCSSPixel();
6459 EffectCompositor::SetPerformanceWarning(
6460 aFrame, transformSet,
6461 AnimationPerformanceWarning(
6462 AnimationPerformanceWarning::Type::ContentTooLargeArea,
6464 int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
6465 int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
6466 }));
6467 } else {
6468 EffectCompositor::SetPerformanceWarning(
6469 aFrame, transformSet,
6470 AnimationPerformanceWarning(
6471 AnimationPerformanceWarning::Type::ContentTooLarge,
6473 nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
6474 nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
6475 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
6476 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
6477 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
6478 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
6479 }));
6482 return result;
6485 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
6486 * visible or hit. */
6487 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix) {
6488 if (aMatrix.IsSingular()) {
6489 return false;
6491 if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) {
6492 return false;
6494 return true;
6497 const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const {
6498 if (mTransform) {
6499 return *mTransform;
6502 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6504 if (mHasTransformGetter) {
6505 mTransform.emplace((mFrame->GetTransformGetter())(mFrame, scale));
6506 Point3D newOrigin =
6507 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
6508 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f);
6509 mTransform->ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
6510 } else if (!mIsTransformSeparator) {
6511 DebugOnly<bool> isReference = mFrame->IsTransformed() ||
6512 mFrame->Combines3DTransformWithAncestors() ||
6513 mFrame->Extend3DContext();
6514 MOZ_ASSERT(isReference);
6515 mTransform.emplace(
6516 GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale,
6517 INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN));
6518 } else {
6519 // Use identity matrix
6520 mTransform.emplace();
6523 return *mTransform;
6526 const Matrix4x4Flagged& nsDisplayTransform::GetInverseTransform() const {
6527 if (mInverseTransform) {
6528 return *mInverseTransform;
6531 MOZ_ASSERT(!GetTransform().IsSingular());
6533 mInverseTransform.emplace(GetTransform().Inverse());
6535 return *mInverseTransform;
6538 Matrix4x4 nsDisplayTransform::GetTransformForRendering(
6539 LayoutDevicePoint* aOutOrigin) const {
6540 if (!mFrame->HasPerspective() || mHasTransformGetter ||
6541 mIsTransformSeparator) {
6542 if (!mHasTransformGetter && !mIsTransformSeparator && aOutOrigin) {
6543 // If aOutOrigin is provided, put the offset to origin into it, because
6544 // we need to keep it separate for webrender. The combination of
6545 // *aOutOrigin and the returned matrix here should always be equivalent
6546 // to what GetTransform() would have returned.
6547 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6548 *aOutOrigin = LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale);
6550 // The rounding behavior should also be the same as GetTransform().
6551 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6552 aOutOrigin->Round();
6554 return GetResultingTransformMatrix(mFrame, nsPoint(0, 0), scale,
6555 INCLUDE_PERSPECTIVE);
6557 return GetTransform().GetMatrix();
6559 MOZ_ASSERT(!mHasTransformGetter);
6561 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6562 // Don't include perspective transform, or the offset to origin, since
6563 // nsDisplayPerspective will handle both of those.
6564 return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
6567 const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
6568 nsDisplayListBuilder* aBuilder) {
6569 MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
6571 if (!IsLeafOf3DContext()) {
6572 return GetTransform().GetMatrix();
6575 if (!mTransformPreserves3D) {
6576 const nsIFrame* establisher; // Establisher of the 3D rendering context.
6577 for (establisher = mFrame;
6578 establisher && establisher->Combines3DTransformWithAncestors();
6579 establisher =
6580 establisher->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
6582 const nsIFrame* establisherReference = aBuilder->FindReferenceFrameFor(
6583 nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher));
6585 nsPoint offset = establisher->GetOffsetToCrossDoc(establisherReference);
6586 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6587 uint32_t flags =
6588 INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN;
6589 mTransformPreserves3D = MakeUnique<Matrix4x4>(
6590 GetResultingTransformMatrix(mFrame, offset, scale, flags));
6593 return *mTransformPreserves3D;
6596 bool nsDisplayTransform::CreateWebRenderCommands(
6597 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
6598 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
6599 nsDisplayListBuilder* aDisplayListBuilder) {
6600 // We want to make sure we don't pollute the transform property in the WR
6601 // stacking context by including the position of this frame (relative to the
6602 // parent reference frame). We need to keep those separate; the position of
6603 // this frame goes into the stacking context bounds while the transform goes
6604 // into the transform.
6605 LayoutDevicePoint position;
6606 Matrix4x4 newTransformMatrix = GetTransformForRendering(&position);
6608 gfx::Matrix4x4* transformForSC = &newTransformMatrix;
6609 if (newTransformMatrix.IsIdentity()) {
6610 // If the transform is an identity transform, strip it out so that WR
6611 // doesn't turn this stacking context into a reference frame, as it
6612 // affects positioning. Bug 1345577 tracks a better fix.
6613 transformForSC = nullptr;
6615 // In ChooseScaleAndSetTransform, we round the offset from the reference
6616 // frame used to adjust the transform, if there is no transform, or it
6617 // is just a translation. We need to do the same here.
6618 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6619 position.Round();
6623 auto key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
6624 wr::SpatialKeyKind::Transform);
6626 // We don't send animations for transform separator display items.
6627 uint64_t animationsId =
6628 mIsTransformSeparator
6630 : AddAnimationsForWebRender(
6631 this, aManager, aDisplayListBuilder,
6632 IsPartialPrerender() ? Some(position) : Nothing());
6633 wr::WrAnimationProperty prop{wr::WrAnimationType::Transform, animationsId,
6634 key};
6636 nsDisplayTransform* deferredTransformItem = nullptr;
6637 if (ShouldDeferTransform()) {
6638 // If it has perspective, we create a new scroll data via the
6639 // UpdateScrollData call because that scenario is more complex. Otherwise,
6640 // if we don't contain any ASRs then just stash the transform on the
6641 // StackingContextHelper and apply it to any scroll data that are created
6642 // inside this nsDisplayTransform.
6643 deferredTransformItem = this;
6646 // Determine if we're possibly animated (= would need an active layer in FLB).
6647 bool animated = !mIsTransformSeparator &&
6648 ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
6650 wr::StackingContextParams params;
6651 params.mBoundTransform = &newTransformMatrix;
6652 params.animation = animationsId ? &prop : nullptr;
6654 wr::WrTransformInfo transform_info;
6655 if (transformForSC) {
6656 transform_info.transform = wr::ToLayoutTransform(newTransformMatrix);
6657 transform_info.key = key;
6658 params.mTransformPtr = &transform_info;
6659 } else {
6660 params.mTransformPtr = nullptr;
6663 params.prim_flags = !BackfaceIsHidden()
6664 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
6665 : wr::PrimitiveFlags{0};
6666 params.paired_with_perspective = mHasAssociatedPerspective;
6667 params.mDeferredTransformItem = deferredTransformItem;
6668 params.mAnimated = animated;
6669 // Determine if we would have to rasterize any items in local raster space
6670 // (i.e. disable subpixel AA). We don't always need to rasterize locally even
6671 // if the stacking context is possibly animated (at the cost of potentially
6672 // some false negatives with respect to will-change handling), so we pass in
6673 // this determination separately to accurately match with when FLB would
6674 // normally disable subpixel AA.
6675 params.mRasterizeLocally = animated && Frame()->HasAnimationOfTransform();
6676 params.SetPreserve3D(mFrame->Extend3DContext() && !mIsTransformSeparator);
6677 params.clip =
6678 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
6680 LayoutDeviceSize boundsSize = LayoutDeviceSize::FromAppUnits(
6681 mChildBounds.Size(), mFrame->PresContext()->AppUnitsPerDevPixel());
6683 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
6684 params, LayoutDeviceRect(position, boundsSize));
6686 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6687 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
6688 return true;
6691 bool nsDisplayTransform::UpdateScrollData(
6692 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
6693 if (ShouldDeferTransform()) {
6694 // This case is handled in CreateWebRenderCommands by stashing the transform
6695 // on the stacking context.
6696 return false;
6698 if (aLayerData) {
6699 aLayerData->SetTransform(GetTransform().GetMatrix());
6700 aLayerData->SetTransformIsPerspective(mFrame->ChildrenHavePerspective());
6702 return true;
6705 bool nsDisplayTransform::ShouldSkipTransform(
6706 nsDisplayListBuilder* aBuilder) const {
6707 return (aBuilder->RootReferenceFrame() == mFrame) &&
6708 aBuilder->IsForGenerateGlyphMask();
6711 void nsDisplayTransform::Collect3DTransformLeaves(
6712 nsDisplayListBuilder* aBuilder, nsTArray<nsDisplayTransform*>& aLeaves) {
6713 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6714 aLeaves.AppendElement(this);
6715 return;
6718 FlattenedDisplayListIterator iter(aBuilder, &mChildren);
6719 while (iter.HasNext()) {
6720 nsDisplayItem* item = iter.GetNextItem();
6721 if (item->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
6722 auto* perspective = static_cast<nsDisplayPerspective*>(item);
6723 if (!perspective->GetChildren()->GetTop()) {
6724 continue;
6726 item = perspective->GetChildren()->GetTop();
6728 if (item->GetType() != DisplayItemType::TYPE_TRANSFORM) {
6729 gfxCriticalError() << "Invalid child item within 3D transform of type: "
6730 << item->Name();
6731 continue;
6733 static_cast<nsDisplayTransform*>(item)->Collect3DTransformLeaves(aBuilder,
6734 aLeaves);
6738 static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
6739 const gfx::Polygon& aPolygon) {
6740 MOZ_ASSERT(!aPolygon.IsEmpty());
6742 RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
6743 const nsTArray<Point4D>& points = aPolygon.GetPoints();
6745 pathBuilder->MoveTo(points[0].As2DPoint());
6747 for (size_t i = 1; i < points.Length(); ++i) {
6748 pathBuilder->LineTo(points[i].As2DPoint());
6751 pathBuilder->Close();
6752 return pathBuilder->Finish();
6755 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6756 nsDisplayListBuilder* aBuilder, nsTArray<TransformPolygon>& aLeaves) {
6757 std::list<TransformPolygon> inputLayers;
6759 nsTArray<nsDisplayTransform*> leaves;
6760 Collect3DTransformLeaves(aBuilder, leaves);
6761 for (nsDisplayTransform* item : leaves) {
6762 auto bounds = LayoutDeviceRect::FromAppUnits(
6763 item->mChildBounds, item->mFrame->PresContext()->AppUnitsPerDevPixel());
6764 Matrix4x4 transform = item->GetAccumulatedPreserved3DTransform(aBuilder);
6766 if (!IsFrameVisible(item->mFrame, transform)) {
6767 continue;
6769 gfx::Polygon polygon =
6770 gfx::Polygon::FromRect(gfx::Rect(bounds.ToUnknownRect()));
6772 polygon.TransformToScreenSpace(transform);
6774 if (polygon.GetPoints().Length() >= 3) {
6775 inputLayers.push_back(TransformPolygon(item, std::move(polygon)));
6779 if (inputLayers.empty()) {
6780 return;
6783 BSPTree<nsDisplayTransform> tree(inputLayers);
6784 nsTArray<TransformPolygon> orderedLayers(tree.GetDrawOrder());
6786 for (TransformPolygon& polygon : orderedLayers) {
6787 Matrix4x4 inverse =
6788 polygon.data->GetAccumulatedPreserved3DTransform(aBuilder).Inverse();
6790 MOZ_ASSERT(polygon.geometry);
6791 polygon.geometry->TransformToLayerSpace(inverse);
6794 aLeaves = std::move(orderedLayers);
6797 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder,
6798 gfxContext* aCtx) {
6799 Paint(aBuilder, aCtx, Nothing());
6802 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
6803 const Maybe<gfx::Polygon>& aPolygon) {
6804 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6805 MOZ_ASSERT(!aPolygon);
6806 nsTArray<TransformPolygon> leaves;
6807 CollectSorted3DTransformLeaves(aBuilder, leaves);
6808 for (TransformPolygon& item : leaves) {
6809 item.data->Paint(aBuilder, aCtx, item.geometry);
6811 return;
6814 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
6815 Matrix4x4 trans = ShouldSkipTransform(aBuilder)
6816 ? Matrix4x4()
6817 : GetAccumulatedPreserved3DTransform(aBuilder);
6818 if (!IsFrameVisible(mFrame, trans)) {
6819 return;
6822 Matrix trans2d;
6823 if (trans.CanDraw2D(&trans2d)) {
6824 aCtx->Multiply(ThebesMatrix(trans2d));
6826 if (aPolygon) {
6827 RefPtr<gfx::Path> path =
6828 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6829 aCtx->GetDrawTarget()->PushClip(path);
6832 GetChildren()->Paint(aBuilder, aCtx,
6833 mFrame->PresContext()->AppUnitsPerDevPixel());
6835 if (aPolygon) {
6836 aCtx->GetDrawTarget()->PopClip();
6838 return;
6841 // TODO: Implement 3d transform handling, including plane splitting and
6842 // sorting. See BasicCompositor.
6843 auto pixelBounds = LayoutDeviceRect::FromAppUnitsToOutside(
6844 mChildBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
6845 RefPtr<DrawTarget> untransformedDT =
6846 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6847 IntSize(pixelBounds.Width(), pixelBounds.Height()),
6848 SurfaceFormat::B8G8R8A8, true);
6849 if (!untransformedDT || !untransformedDT->IsValid()) {
6850 return;
6852 untransformedDT->SetTransform(
6853 Matrix::Translation(-Point(pixelBounds.X(), pixelBounds.Y())));
6855 gfxContext groupTarget(untransformedDT, /* aPreserveTransform */ true);
6857 if (aPolygon) {
6858 RefPtr<gfx::Path> path =
6859 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6860 aCtx->GetDrawTarget()->PushClip(path);
6863 GetChildren()->Paint(aBuilder, &groupTarget,
6864 mFrame->PresContext()->AppUnitsPerDevPixel());
6866 if (aPolygon) {
6867 aCtx->GetDrawTarget()->PopClip();
6870 RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
6872 trans.PreTranslate(pixelBounds.X(), pixelBounds.Y(), 0);
6873 aCtx->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf, trans);
6876 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
6877 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6878 // completely bypass the main thread for this animation, so it is always
6879 // worthwhile.
6880 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6881 // already involved so there is less to be gained.
6882 // Therefore we check that the *post-transform* bounds of this item are
6883 // big enough to justify an active layer.
6884 return EffectCompositor::HasAnimationsForCompositor(
6885 mFrame, DisplayItemType::TYPE_TRANSFORM) ||
6886 (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame));
6889 nsRect nsDisplayTransform::TransformUntransformedBounds(
6890 nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
6891 bool snap;
6892 const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
6893 // GetTransform always operates in dev pixels.
6894 const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
6895 return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
6896 factor);
6900 * Returns the bounds for this transform. The bounds are calculated during
6901 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6903 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
6904 bool* aSnap) const {
6905 *aSnap = false;
6906 return mBounds;
6909 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
6910 MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
6912 /* Some transforms can get empty bounds in 2D, but might get transformed again
6913 * and get non-empty bounds. A simple example of this would be a 180 degree
6914 * rotation getting applied twice.
6915 * We should not depend on transforming bounds level by level.
6917 * This function collects the bounds of this transform and stores it in
6918 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6919 * down and include the bounds of the child transforms.
6920 * The bounds are transformed with the accumulated transformation matrix up to
6921 * the 3D context root coordinate space.
6923 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6924 accTransform.Accumulate(GetTransform().GetMatrix());
6926 // Do not dive into another 3D context.
6927 if (!IsLeafOf3DContext()) {
6928 for (nsDisplayItem* i : *GetChildren()) {
6929 i->DoUpdateBoundsPreserves3D(aBuilder);
6933 /* The child transforms that extend 3D context further will have empty bounds,
6934 * so the untransformed bounds here is the bounds of all the non-preserve-3d
6935 * content under this transform.
6937 const nsRect rect = TransformUntransformedBounds(
6938 aBuilder, accTransform.GetCurrentTransform());
6939 aBuilder->AccumulateRect(rect);
6942 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6943 nsDisplayListBuilder* aBuilder) {
6944 MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
6945 IsTransformSeparator());
6946 // Updating is not going through to child 3D context.
6947 ComputeBounds(aBuilder);
6950 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
6951 UpdateUntransformedBounds(aBuilder);
6953 if (IsTransformSeparator()) {
6954 MOZ_ASSERT(GetTransform().IsIdentity());
6955 mBounds = mChildBounds;
6956 return;
6959 if (mFrame->Extend3DContext()) {
6960 if (!Combines3DTransformWithAncestors()) {
6961 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6962 // collect the bounds from the child transforms.
6963 UpdateBoundsFor3D(aBuilder);
6964 } else {
6965 // With nested 3D transforms, the 2D bounds might not be useful.
6966 mBounds = nsRect();
6969 return;
6972 MOZ_ASSERT(!mFrame->Extend3DContext());
6974 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
6975 // but mix-blend-mode relies on having bounds set. See bug 1556956.
6977 // A stand-alone transform.
6978 mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
6981 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
6982 MOZ_ASSERT(mFrame->Extend3DContext() &&
6983 !mFrame->Combines3DTransformWithAncestors() &&
6984 !IsTransformSeparator());
6986 // Always start updating from an establisher of a 3D rendering context.
6987 nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
6988 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6989 accTransform.StartRoot();
6990 ComputeBounds(aBuilder);
6991 mBounds = aBuilder->GetAccumulatedRect();
6994 void nsDisplayTransform::UpdateUntransformedBounds(
6995 nsDisplayListBuilder* aBuilder) {
6996 mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
6997 aBuilder, mActiveScrolledRoot);
7000 #ifdef DEBUG_HIT
7001 # include <time.h>
7002 #endif
7004 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
7005 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
7006 const nsRect& aRect, HitTestState* aState,
7007 nsTArray<nsIFrame*>* aOutFrames) {
7008 if (aState->mInPreserves3D) {
7009 GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
7010 return;
7013 /* Here's how this works:
7014 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
7015 * anything).
7016 * 2. Invert the matrix.
7017 * 3. Use it to transform the rect into the correct space.
7018 * 4. Pass that rect down through to the list's version of HitTest.
7020 // GetTransform always operates in dev pixels.
7021 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7022 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7024 if (!IsFrameVisible(mFrame, matrix)) {
7025 return;
7028 const bool oldHitOccludingItem = aState->mHitOccludingItem;
7030 /* We want to go from transformed-space to regular space.
7031 * Thus we have to invert the matrix, which normally does
7032 * the reverse operation (e.g. regular->transformed)
7035 /* Now, apply the transform and pass it down the channel. */
7036 matrix.Invert();
7037 nsRect resultingRect;
7038 // Magic width/height indicating we're hit testing a point, not a rect
7039 const bool testingPoint = aRect.width == 1 && aRect.height == 1;
7040 if (testingPoint) {
7041 Point4D point =
7042 matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
7043 NSAppUnitsToFloatPixels(aRect.y, factor)));
7044 if (!point.HasPositiveWCoord()) {
7045 return;
7048 Point point2d = point.As2DPoint();
7050 resultingRect =
7051 nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
7052 NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1);
7054 } else {
7055 Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
7056 NSAppUnitsToFloatPixels(aRect.y, factor),
7057 NSAppUnitsToFloatPixels(aRect.width, factor),
7058 NSAppUnitsToFloatPixels(aRect.height, factor));
7060 Rect childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds.x, factor),
7061 NSAppUnitsToFloatPixels(mChildBounds.y, factor),
7062 NSAppUnitsToFloatPixels(mChildBounds.width, factor),
7063 NSAppUnitsToFloatPixels(mChildBounds.height, factor));
7065 Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
7067 resultingRect =
7068 nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
7069 NSFloatPixelsToAppUnits(float(rect.Y()), factor),
7070 NSFloatPixelsToAppUnits(float(rect.Width()), factor),
7071 NSFloatPixelsToAppUnits(float(rect.Height()), factor));
7074 if (resultingRect.IsEmpty()) {
7075 return;
7078 #ifdef DEBUG_HIT
7079 printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
7080 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(),
7081 resultingRect.Y());
7082 uint32_t originalFrameCount = aOutFrames.Length();
7083 #endif
7085 GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
7087 if (aState->mHitOccludingItem && !testingPoint &&
7088 !mChildBounds.Contains(aRect)) {
7089 MOZ_ASSERT(aBuilder->HitTestIsForVisibility());
7090 // We're hit-testing a rect that's bigger than our child bounds, but
7091 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so
7092 // we can't stop hit-testing altogether.
7094 // FIXME(emilio): I think this means that theoretically we might include
7095 // some frames fully behind other transformed-but-opaque frames? Then again
7096 // that's our pre-existing behavior for other untransformed content that
7097 // doesn't fill the whole rect. To be fully correct I think we'd need proper
7098 // "known occluded region" tracking, but that might be overkill for our
7099 // purposes here.
7100 aState->mHitOccludingItem = oldHitOccludingItem;
7103 #ifdef DEBUG_HIT
7104 if (originalFrameCount != aOutFrames.Length())
7105 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7106 dynamic_cast<void*>(aOutFrames.ElementAt(0)));
7107 printf("=== end of hit test ===\n");
7108 #endif
7111 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
7112 const nsPoint& aPoint) {
7113 // GetTransform always operates in dev pixels.
7114 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7115 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7117 NS_ASSERTION(IsFrameVisible(mFrame, matrix),
7118 "We can't have hit a frame that isn't visible!");
7120 Matrix4x4 inverse = matrix;
7121 inverse.Invert();
7122 Point4D point =
7123 inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
7124 NSAppUnitsToFloatPixels(aPoint.y, factor)));
7126 Point point2d = point.As2DPoint();
7128 Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
7129 return transformed.z;
7132 /* The transform is opaque iff the transform consists solely of scales and
7133 * translations and if the underlying content is opaque. Thus if the transform
7134 * is of the form
7136 * |a c e|
7137 * |b d f|
7138 * |0 0 1|
7140 * We need b and c to be zero.
7142 * We also need to check whether the underlying opaque content completely fills
7143 * our visible rect. We use UntransformRect which expands to the axis-aligned
7144 * bounding rect, but that's OK since if
7145 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7146 * certainly contains the actual (non-axis-aligned) untransformed rect.
7148 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7149 bool* aSnap) const {
7150 *aSnap = false;
7152 nsRect untransformedVisible;
7153 if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
7154 return nsRegion();
7157 const Matrix4x4Flagged& matrix = GetTransform();
7158 Matrix matrix2d;
7159 if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
7160 return nsRegion();
7163 nsRegion result;
7165 bool tmpSnap;
7166 const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
7167 const nsRegion opaque =
7168 ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
7170 if (opaque.Contains(untransformedVisible)) {
7171 result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
7173 return result;
7176 nsRect nsDisplayTransform::GetComponentAlphaBounds(
7177 nsDisplayListBuilder* aBuilder) const {
7178 if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
7179 return nsRect();
7182 bool snap;
7183 return GetBounds(aBuilder, &snap);
7186 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7187 * the smallest rectangle (in app space) containing the transformed image of
7188 * that rectangle. That is, it takes the four corners of the rectangle,
7189 * transforms them according to the matrix associated with the specified frame,
7190 * then returns the smallest rectangle containing the four transformed points.
7192 * @param aUntransformedBounds The rectangle (in app units) to transform.
7193 * @param aFrame The frame whose transformation should be applied.
7194 * @param aOrigin The delta from the frame origin to the coordinate space origin
7195 * @return The smallest rectangle containing the image of the transformed
7196 * rectangle.
7198 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds,
7199 const nsIFrame* aFrame,
7200 TransformReferenceBox& aRefBox) {
7201 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7203 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7205 FrameTransformProperties props(aFrame, aRefBox, factor);
7206 return nsLayoutUtils::MatrixTransformRect(
7207 aUntransformedBounds,
7208 GetResultingTransformMatrixInternal(props, aRefBox, nsPoint(), factor,
7209 kTransformRectFlags),
7210 factor);
7213 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7214 const nsRect& aChildBounds,
7215 const nsIFrame* aFrame,
7216 nsRect* aOutRect) {
7217 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7219 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7220 Matrix4x4 transform = GetResultingTransformMatrix(aFrame, nsPoint(), factor,
7221 kTransformRectFlags);
7222 return UntransformRect(aTransformedBounds, aChildBounds, transform, factor,
7223 aOutRect);
7226 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7227 const nsRect& aChildBounds,
7228 const Matrix4x4& aMatrix,
7229 float aAppUnitsPerPixel,
7230 nsRect* aOutRect) {
7231 if (aMatrix.IsSingular()) {
7232 return false;
7235 RectDouble result(
7236 NSAppUnitsToFloatPixels(aTransformedBounds.x, aAppUnitsPerPixel),
7237 NSAppUnitsToFloatPixels(aTransformedBounds.y, aAppUnitsPerPixel),
7238 NSAppUnitsToFloatPixels(aTransformedBounds.width, aAppUnitsPerPixel),
7239 NSAppUnitsToFloatPixels(aTransformedBounds.height, aAppUnitsPerPixel));
7241 RectDouble childGfxBounds(
7242 NSAppUnitsToFloatPixels(aChildBounds.x, aAppUnitsPerPixel),
7243 NSAppUnitsToFloatPixels(aChildBounds.y, aAppUnitsPerPixel),
7244 NSAppUnitsToFloatPixels(aChildBounds.width, aAppUnitsPerPixel),
7245 NSAppUnitsToFloatPixels(aChildBounds.height, aAppUnitsPerPixel));
7247 result = aMatrix.Inverse().ProjectRectBounds(result, childGfxBounds);
7248 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result),
7249 aAppUnitsPerPixel);
7250 return true;
7253 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder,
7254 const nsRect& aRect,
7255 nsRect* aOutRect) const {
7256 if (GetTransform().IsSingular()) {
7257 return false;
7260 // GetTransform always operates in dev pixels.
7261 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7262 RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor),
7263 NSAppUnitsToFloatPixels(aRect.y, factor),
7264 NSAppUnitsToFloatPixels(aRect.width, factor),
7265 NSAppUnitsToFloatPixels(aRect.height, factor));
7267 bool snap;
7268 nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
7269 RectDouble childGfxBounds(
7270 NSAppUnitsToFloatPixels(childBounds.x, factor),
7271 NSAppUnitsToFloatPixels(childBounds.y, factor),
7272 NSAppUnitsToFloatPixels(childBounds.width, factor),
7273 NSAppUnitsToFloatPixels(childBounds.height, factor));
7275 /* We want to untransform the matrix, so invert the transformation first! */
7276 result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds);
7278 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
7280 return true;
7283 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) {
7284 aStream << GetTransform().GetMatrix();
7285 if (IsTransformSeparator()) {
7286 aStream << " transform-separator";
7288 if (IsLeafOf3DContext()) {
7289 aStream << " 3d-context-leaf";
7291 if (mFrame->Extend3DContext()) {
7292 aStream << " extends-3d-context";
7294 if (mFrame->Combines3DTransformWithAncestors()) {
7295 aStream << " combines-3d-with-ancestors";
7298 aStream << " prerender(";
7299 switch (mPrerenderDecision) {
7300 case PrerenderDecision::No:
7301 aStream << "no";
7302 break;
7303 case PrerenderDecision::Partial:
7304 aStream << "partial";
7305 break;
7306 case PrerenderDecision::Full:
7307 aStream << "full";
7308 break;
7310 aStream << ")";
7311 aStream << " childrenBuildingRect" << mChildrenBuildingRect;
7314 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
7315 nsIFrame* aFrame,
7316 nsDisplayList* aList)
7317 : nsPaintedDisplayItem(aBuilder, aFrame), mList(aBuilder) {
7318 mList.AppendToTop(aList);
7319 MOZ_ASSERT(mList.Length() == 1);
7320 MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
7323 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder,
7324 gfxContext* aCtx) {
7325 // Just directly recurse into children, since we'll include the persepctive
7326 // value in any nsDisplayTransform children.
7327 GetChildren()->Paint(aBuilder, aCtx,
7328 mFrame->PresContext()->AppUnitsPerDevPixel());
7331 nsRegion nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7332 bool* aSnap) const {
7333 if (!GetChildren()->GetTop()) {
7334 *aSnap = false;
7335 return nsRegion();
7338 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder, aSnap);
7341 bool nsDisplayPerspective::CreateWebRenderCommands(
7342 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7343 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7344 nsDisplayListBuilder* aDisplayListBuilder) {
7345 float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
7346 Matrix4x4 perspectiveMatrix;
7347 DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
7348 mFrame, appUnitsPerPixel, perspectiveMatrix);
7349 MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
7352 * ClipListToRange can remove our child after we were created.
7354 if (!GetChildren()->GetTop()) {
7355 return false;
7359 * The resulting matrix is still in the coordinate space of the transformed
7360 * frame. Append a translation to the reference frame coordinates.
7362 nsDisplayTransform* transform =
7363 static_cast<nsDisplayTransform*>(GetChildren()->GetTop());
7365 Point3D newOrigin =
7366 Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
7367 appUnitsPerPixel),
7368 NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
7369 appUnitsPerPixel),
7370 0.0f);
7371 Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
7373 perspectiveMatrix.PostTranslate(roundedOrigin);
7375 nsIFrame* perspectiveFrame =
7376 mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
7378 // Passing true here is always correct, since perspective always combines
7379 // transforms with the descendants. However that'd make WR do a lot of work
7380 // that it doesn't really need to do if there aren't other transforms forming
7381 // part of the 3D context.
7383 // WR knows how to treat perspective in that case, so the only thing we need
7384 // to do is to ensure we pass true when we're involved in a 3d context in any
7385 // other way via the transform-style property on either the transformed frame
7386 // or the perspective frame in order to not confuse WR's preserve-3d code in
7387 // very awful ways.
7388 bool preserve3D =
7389 mFrame->Extend3DContext() || perspectiveFrame->Extend3DContext();
7391 wr::StackingContextParams params;
7393 wr::WrTransformInfo transform_info;
7394 transform_info.transform = wr::ToLayoutTransform(perspectiveMatrix);
7395 transform_info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
7396 wr::SpatialKeyKind::Perspective);
7397 params.mTransformPtr = &transform_info;
7399 params.reference_frame_kind = wr::WrReferenceFrameKind::Perspective;
7400 params.prim_flags = !BackfaceIsHidden()
7401 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7402 : wr::PrimitiveFlags{0};
7403 params.SetPreserve3D(preserve3D);
7404 params.clip =
7405 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
7407 Maybe<uint64_t> scrollingRelativeTo;
7408 for (const auto* asr = GetActiveScrolledRoot(); asr; asr = asr->mParent) {
7409 // In OOP documents, the root scrollable frame of the in-process root
7410 // document is always active, so using IsAncestorFrameCrossDocInProcess
7411 // should be fine here.
7412 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7413 asr->mScrollableFrame->GetScrolledFrame(), perspectiveFrame)) {
7414 scrollingRelativeTo.emplace(asr->GetViewId());
7415 break;
7419 // We put the perspective reference frame wrapping the transformed frame,
7420 // even though there may be arbitrarily nested scroll frames in between.
7422 // We need to know how many ancestor scroll-frames are we nested in, in order
7423 // for the async scrolling code in WebRender to calculate the right
7424 // transformation for the perspective contents.
7425 params.scrolling_relative_to = scrollingRelativeTo.ptrOr(nullptr);
7427 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
7428 params);
7430 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7431 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
7433 return true;
7436 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
7437 nsTextFrame* aFrame)
7438 : nsPaintedDisplayItem(aBuilder, aFrame),
7439 mVisIStartEdge(0),
7440 mVisIEndEdge(0) {
7441 MOZ_COUNT_CTOR(nsDisplayText);
7442 mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7443 // Bug 748228
7444 mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
7445 mVisibleRect = aBuilder->GetVisibleRect() +
7446 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
7449 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager,
7450 nsDisplayListBuilder* aBuilder) const {
7451 auto* f = static_cast<nsTextFrame*>(mFrame);
7453 if (f->IsSelected()) {
7454 return false;
7457 const nsStyleText* textStyle = f->StyleText();
7458 if (textStyle->HasTextShadow()) {
7459 return false;
7462 nsTextFrame::TextDecorations decorations;
7463 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7464 decorations);
7465 return !decorations.HasDecorationLines();
7468 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
7469 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
7470 // We don't pass mVisibleRect here, since this can be called from within
7471 // the WebRender fallback painting path, and we don't want to issue
7472 // recorded commands that are dependent on the visible/building rect.
7473 RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
7475 auto* textFrame = static_cast<nsTextFrame*>(mFrame);
7476 LCPTextFrameHelper::MaybeUnionTextFrame(textFrame,
7477 mBounds - ToReferenceFrame());
7480 bool nsDisplayText::CreateWebRenderCommands(
7481 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7482 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7483 nsDisplayListBuilder* aDisplayListBuilder) {
7484 auto* f = static_cast<nsTextFrame*>(mFrame);
7485 auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
7487 nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
7488 // Bug 748228
7489 bounds.Inflate(appUnitsPerDevPixel);
7491 if (bounds.IsEmpty()) {
7492 return true;
7495 // For large font sizes, punt to a blob image, to avoid the blurry rendering
7496 // that results from WR clamping the glyph size used for rasterization.
7498 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7500 // This is not strictly accurate, as final used font sizes might not be the
7501 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7502 // altering the used size of the font actually used).
7503 // It also fails to consider how transforms might affect the device-font-size
7504 // that webrender uses (and clamps).
7505 // But it should be near enough for practical purposes; the limitations just
7506 // mean we might sometimes end up with webrender still applying some bitmap
7507 // scaling, or bail out when we didn't really need to.
7508 constexpr float kWebRenderFontSizeLimit = 320.0;
7509 f->EnsureTextRun(nsTextFrame::eInflated);
7510 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7511 if (textRun &&
7512 textRun->GetFontGroup()->GetStyle()->size > kWebRenderFontSizeLimit) {
7513 return false;
7516 gfx::Point deviceOffset =
7517 LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
7518 .ToUnknownPoint();
7520 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7521 // frames) lets us early reject a bunch of things.
7522 nsRect visible = mVisibleRect;
7524 // Add the "source rect" area from which the given shadows could intersect
7525 // with mVisibleRect, and which therefore needs to included in the paint
7526 // operation, to the `visible` rect that we will use to limit the bounds of
7527 // what we send to the renderer.
7528 auto addShadowSourceToVisible = [&](Span<const StyleSimpleShadow> aShadows) {
7529 for (const auto& shadow : aShadows) {
7530 nsRect sourceRect = mVisibleRect;
7531 // Negate the offsets, because we're looking for the "source" rect that
7532 // could cast a shadow into the visible rect, rather than a "target" area
7533 // onto which the visible rect would cast a shadow.
7534 sourceRect.MoveBy(-shadow.horizontal.ToAppUnits(),
7535 -shadow.vertical.ToAppUnits());
7536 // Inflate to account for the shadow blur.
7537 sourceRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
7538 shadow.blur.ToAppUnits(), appUnitsPerDevPixel));
7539 visible.OrWith(sourceRect);
7543 // Shadows can translate things back into view, so we enlarge the notional
7544 // "visible" rect to ensure we don't skip painting relevant parts that might
7545 // cast a shadow within the visible area.
7546 addShadowSourceToVisible(f->StyleText()->mTextShadow.AsSpan());
7548 // Similarly for shadows that may be cast by ::selection.
7549 if (f->IsSelected()) {
7550 nsTextPaintStyle textPaint(f);
7551 Span<const StyleSimpleShadow> shadows;
7552 f->GetSelectionTextShadow(SelectionType::eNormal, textPaint, &shadows);
7553 addShadowSourceToVisible(shadows);
7556 // Inflate a little extra to allow for potential antialiasing "blur".
7557 visible.Inflate(3 * appUnitsPerDevPixel);
7558 bounds = bounds.Intersect(visible);
7560 gfxContext* textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager,
7561 this, bounds, deviceOffset);
7563 LCPTextFrameHelper::MaybeUnionTextFrame(f, bounds - ToReferenceFrame());
7565 aBuilder.StartGroup(this);
7567 RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
7568 aBuilder.GetInheritedOpacity(), true);
7569 const bool result = textDrawer->GetTextDrawer()->Finish();
7571 if (result) {
7572 aBuilder.FinishGroup();
7573 } else {
7574 aBuilder.CancelGroup(true);
7577 return result;
7580 void nsDisplayText::RenderToContext(gfxContext* aCtx,
7581 nsDisplayListBuilder* aBuilder,
7582 const nsRect& aVisibleRect, float aOpacity,
7583 bool aIsRecording) {
7584 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7586 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7587 // antialiased pixels beyond the measured text extents.
7588 // This is temporary until we do this in the actual calculation of text
7589 // extents.
7590 auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
7591 LayoutDeviceRect extraVisible =
7592 LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D);
7593 extraVisible.Inflate(1);
7595 gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width,
7596 extraVisible.height);
7597 pixelVisible.Inflate(2);
7598 pixelVisible.RoundOut();
7600 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7601 if (!aBuilder->IsForGenerateGlyphMask() && !aIsRecording) {
7602 autoSaveClip.Clip(pixelVisible);
7605 NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
7606 NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
7608 gfxContextMatrixAutoSaveRestore matrixSR;
7610 nsPoint framePt = ToReferenceFrame();
7611 if (f->Style()->IsTextCombined()) {
7612 float scaleFactor = nsTextFrame::GetTextCombineScaleFactor(f);
7613 if (scaleFactor != 1.0f) {
7614 if (auto* textDrawer = aCtx->GetTextDrawer()) {
7615 // WebRender doesn't support scaling text like this yet
7616 textDrawer->FoundUnsupportedFeature();
7617 return;
7619 matrixSR.SetContext(aCtx);
7620 // Setup matrix to compress text for text-combine-upright if
7621 // necessary. This is done here because we want selection be
7622 // compressed at the same time as text.
7623 gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
7624 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7625 if (textRun && textRun->IsRightToLeft()) {
7626 pt.x += gfxFloat(f->GetSize().width) / A2D;
7628 gfxMatrix mat = aCtx->CurrentMatrixDouble()
7629 .PreTranslate(pt)
7630 .PreScale(scaleFactor, 1.0)
7631 .PreTranslate(-pt);
7632 aCtx->SetMatrixDouble(mat);
7635 nsTextFrame::PaintTextParams params(aCtx);
7636 params.framePt = gfx::Point(framePt.x, framePt.y);
7637 params.dirtyRect = extraVisible;
7639 if (aBuilder->IsForGenerateGlyphMask()) {
7640 params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
7641 } else {
7642 params.state = nsTextFrame::PaintTextParams::PaintText;
7645 f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
7646 f->IsSelected(), aOpacity);
7649 // This could go to nsDisplayListInvalidation.h, but
7650 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7651 // would produce circular dependencies.
7652 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
7653 public:
7654 nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
7655 : nsDisplayItemGenericGeometry(aItem, aBuilder),
7656 mVisIStartEdge(aItem->VisIStartEdge()),
7657 mVisIEndEdge(aItem->VisIEndEdge()) {
7658 nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
7659 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7660 mDecorations);
7664 * We store the computed text decorations here since they are
7665 * computed using style data from parent frames. Any changes to these
7666 * styles will only invalidate the parent frame and not this frame.
7668 nsTextFrame::TextDecorations mDecorations;
7669 nscoord mVisIStartEdge;
7670 nscoord mVisIEndEdge;
7673 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry(
7674 nsDisplayListBuilder* aBuilder) {
7675 return new nsDisplayTextGeometry(this, aBuilder);
7678 void nsDisplayText::ComputeInvalidationRegion(
7679 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7680 nsRegion* aInvalidRegion) const {
7681 const nsDisplayTextGeometry* geometry =
7682 static_cast<const nsDisplayTextGeometry*>(aGeometry);
7683 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7685 nsTextFrame::TextDecorations decorations;
7686 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7687 decorations);
7689 bool snap;
7690 const nsRect& newRect = geometry->mBounds;
7691 nsRect oldRect = GetBounds(aBuilder, &snap);
7692 if (decorations != geometry->mDecorations ||
7693 mVisIStartEdge != geometry->mVisIStartEdge ||
7694 mVisIEndEdge != geometry->mVisIEndEdge ||
7695 !oldRect.IsEqualInterior(newRect) ||
7696 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
7697 aInvalidRegion->Or(oldRect, newRect);
7701 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) {
7702 #ifdef DEBUG
7703 aStream << " (\"";
7705 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7706 nsCString buf;
7707 f->ToCString(buf);
7709 aStream << buf.get() << "\")";
7710 #endif
7713 nsDisplayEffectsBase::nsDisplayEffectsBase(
7714 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7715 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
7716 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
7717 aClearClipChain) {
7718 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7721 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
7722 nsIFrame* aFrame,
7723 nsDisplayList* aList)
7724 : nsDisplayWrapList(aBuilder, aFrame, aList) {
7725 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7728 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7729 bool* aSnap) const {
7730 *aSnap = false;
7731 return nsRegion();
7734 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder* aBuilder,
7735 const nsRect& aRect, HitTestState* aState,
7736 nsTArray<nsIFrame*>* aOutFrames) {
7737 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
7738 if (SVGIntegrationUtils::HitTestFrameForEffects(
7739 mFrame, rectCenter - ToReferenceFrame())) {
7740 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
7744 gfxRect nsDisplayEffectsBase::BBoxInUserSpace() const {
7745 return SVGUtils::GetBBox(mFrame);
7748 gfxPoint nsDisplayEffectsBase::UserSpaceOffset() const {
7749 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
7752 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7753 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7754 nsRegion* aInvalidRegion) const {
7755 const auto* geometry =
7756 static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
7757 bool snap;
7758 nsRect bounds = GetBounds(aBuilder, &snap);
7759 if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
7760 geometry->mUserSpaceOffset != UserSpaceOffset() ||
7761 !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
7762 // Filter and mask output can depend on the location of the frame's user
7763 // space and on the frame's BBox. We need to invalidate if either of these
7764 // change relative to the reference frame.
7765 // Invalidations from our inactive layer manager are not enough to catch
7766 // some of these cases because filters can produce output even if there's
7767 // nothing in the filter input.
7768 aInvalidRegion->Or(bounds, geometry->mBounds);
7772 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7773 if (mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
7774 ISVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame);
7775 if (!svgFrame) {
7776 return false;
7778 if (auto* svgElement = SVGElement::FromNode(mFrame->GetContent())) {
7779 // The SVG spec says only to draw filters if the element
7780 // has valid dimensions.
7781 return svgElement->HasValidDimensions();
7783 return false;
7786 return true;
7789 using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams;
7791 static void ComputeMaskGeometry(PaintFramesParams& aParams) {
7792 // Properties are added lazily and may have been removed by a restyle, so
7793 // make sure all applicable ones are set again.
7794 nsIFrame* firstFrame =
7795 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
7797 const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
7799 nsTArray<SVGMaskFrame*> maskFrames;
7800 // XXX check return value?
7801 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
7803 if (maskFrames.Length() == 0) {
7804 return;
7807 gfxContext& ctx = aParams.ctx;
7808 nsIFrame* frame = aParams.frame;
7810 nsPoint offsetToUserSpace =
7811 nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame);
7813 auto cssToDevScale = frame->PresContext()->CSSToDevPixelScale();
7814 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
7816 gfxPoint devPixelOffsetToUserSpace =
7817 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, appUnitsPerDevPixel);
7819 gfxContextMatrixAutoSaveRestore matSR(&ctx);
7820 ctx.SetMatrixDouble(
7821 ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace));
7823 // Convert boaderArea and dirtyRect to user space.
7824 nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
7825 nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
7827 // Union all mask layer rectangles in user space.
7828 LayoutDeviceRect maskInUserSpace;
7829 for (size_t i = 0; i < maskFrames.Length(); i++) {
7830 SVGMaskFrame* maskFrame = maskFrames[i];
7831 LayoutDeviceRect currentMaskSurfaceRect;
7833 if (maskFrame) {
7834 auto rect = maskFrame->GetMaskArea(aParams.frame);
7835 currentMaskSurfaceRect =
7836 CSSRect::FromUnknownRect(ToRect(rect)) * cssToDevScale;
7837 } else {
7838 nsCSSRendering::ImageLayerClipState clipState;
7839 nsCSSRendering::GetImageLayerClip(
7840 svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(),
7841 userSpaceBorderArea, userSpaceDirtyRect,
7842 /* aWillPaintBorder = */ false, appUnitsPerDevPixel, &clipState);
7843 currentMaskSurfaceRect = LayoutDeviceRect::FromUnknownRect(
7844 ToRect(clipState.mDirtyRectInDevPx));
7847 maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
7850 if (!maskInUserSpace.IsEmpty()) {
7851 aParams.maskRect = Some(maskInUserSpace);
7852 } else {
7853 aParams.maskRect = Nothing();
7857 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7858 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7859 const ActiveScrolledRoot* aActiveScrolledRoot, bool aWrapsBackdropFilter)
7860 : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
7861 mWrapsBackdropFilter(aWrapsBackdropFilter) {
7862 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
7864 nsPresContext* presContext = mFrame->PresContext();
7865 uint32_t flags =
7866 aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
7867 const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
7868 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7869 const auto& layer = svgReset->mMask.mLayers[i];
7870 if (!layer.mImage.IsResolved()) {
7871 continue;
7873 const nsRect& borderArea = mFrame->GetRectRelativeToSelf();
7874 // NOTE(emilio): We only care about the dest rect so we don't bother
7875 // computing a clip.
7876 bool isTransformedFixed = false;
7877 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
7878 presContext, aFrame, flags, borderArea, borderArea, layer,
7879 &isTransformedFixed);
7880 mDestRects.AppendElement(state.mDestArea);
7884 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) {
7885 // Do not merge items for box-decoration-break:clone elements,
7886 // since each box should have its own mask in that case.
7887 if (aFrame->StyleBorder()->mBoxDecorationBreak ==
7888 StyleBoxDecorationBreak::Clone) {
7889 return false;
7892 // Do not merge if either frame has a mask. Continuation frames should apply
7893 // the mask independently (just like nsDisplayBackgroundImage).
7894 if (aFrame->StyleSVGReset()->HasMask()) {
7895 return false;
7898 return true;
7901 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem* aItem) const {
7902 // Items for the same content element should be merged into a single
7903 // compositing group.
7904 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
7905 !HasSameContent(aItem)) {
7906 return false;
7909 return CanMergeDisplayMaskFrame(mFrame) &&
7910 CanMergeDisplayMaskFrame(aItem->Frame());
7913 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7914 if (!ValidateSVGFrame()) {
7915 return false;
7918 return SVGUtils::DetermineMaskUsage(mFrame, false).UsingMaskOrClipPath();
7921 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder,
7922 gfxContext* aMaskContext,
7923 bool aHandleOpacity,
7924 bool* aMaskPainted) {
7925 MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
7927 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7928 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7929 PaintFramesParams params(*aMaskContext, mFrame, mBounds, borderArea, aBuilder,
7930 aHandleOpacity, imgParams);
7931 ComputeMaskGeometry(params);
7932 bool maskIsComplete = false;
7933 bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete);
7934 if (aMaskPainted) {
7935 *aMaskPainted = painted;
7938 return maskIsComplete &&
7939 (imgParams.result == ImgDrawResult::SUCCESS ||
7940 imgParams.result == ImgDrawResult::SUCCESS_NOT_COMPLETE ||
7941 imgParams.result == ImgDrawResult::WRONG_SIZE);
7944 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7945 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7946 nsRegion* aInvalidRegion) const {
7947 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
7948 aInvalidRegion);
7950 const auto* geometry =
7951 static_cast<const nsDisplayMasksAndClipPathsGeometry*>(aGeometry);
7952 bool snap;
7953 nsRect bounds = GetBounds(aBuilder, &snap);
7955 if (mDestRects.Length() != geometry->mDestRects.Length()) {
7956 aInvalidRegion->Or(bounds, geometry->mBounds);
7957 } else {
7958 for (size_t i = 0; i < mDestRects.Length(); i++) {
7959 if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) {
7960 aInvalidRegion->Or(bounds, geometry->mBounds);
7961 break;
7967 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7968 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
7969 const std::function<void()>& aPaintChildren) {
7970 // Clip the drawing target by mVisibleRect, which contains the visible
7971 // region of the target frame and its out-of-flow and inflow descendants.
7972 Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx),
7973 mFrame->PresContext()->AppUnitsPerDevPixel());
7974 bounds.RoundOut();
7975 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7976 autoSaveClip.Clip(bounds);
7978 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7979 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7980 PaintFramesParams params(*aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
7981 borderArea, aBuilder, false, imgParams);
7983 ComputeMaskGeometry(params);
7985 SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren);
7988 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder* aBuilder,
7989 gfxContext* aCtx) {
7990 if (!IsValidMask()) {
7991 return;
7993 PaintWithContentsPaintCallback(aBuilder, aCtx, [&] {
7994 GetChildren()->Paint(aBuilder, aCtx,
7995 mFrame->PresContext()->AppUnitsPerDevPixel());
7999 static Maybe<wr::WrClipChainId> CreateSimpleClipRegion(
8000 const nsDisplayMasksAndClipPaths& aDisplayItem,
8001 wr::DisplayListBuilder& aBuilder) {
8002 nsIFrame* frame = aDisplayItem.Frame();
8003 const auto* style = frame->StyleSVGReset();
8004 MOZ_ASSERT(style->HasClipPath() || style->HasMask());
8005 if (!SVGUtils::DetermineMaskUsage(frame, false).IsSimpleClipShape()) {
8006 return Nothing();
8009 const auto& clipPath = style->mClipPath;
8010 const auto& shape = *clipPath.AsShape()._0;
8012 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8013 const nsRect refBox =
8014 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8016 wr::WrClipId clipId{};
8018 switch (shape.tag) {
8019 case StyleBasicShape::Tag::Rect: {
8020 const nsRect rect =
8021 ShapeUtils::ComputeInsetRect(shape.AsRect().rect, refBox) +
8022 aDisplayItem.ToReferenceFrame();
8024 nscoord radii[8] = {0};
8025 if (ShapeUtils::ComputeRectRadii(shape.AsRect().round, refBox, rect,
8026 radii)) {
8027 clipId = aBuilder.DefineRoundedRectClip(
8028 Nothing(),
8029 wr::ToComplexClipRegion(rect, radii, appUnitsPerDevPixel));
8030 } else {
8031 clipId = aBuilder.DefineRectClip(
8032 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8033 rect, appUnitsPerDevPixel)));
8036 break;
8038 case StyleBasicShape::Tag::Ellipse:
8039 case StyleBasicShape::Tag::Circle: {
8040 nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox);
8042 nsSize radii;
8043 if (shape.IsEllipse()) {
8044 radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox);
8045 } else {
8046 nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox);
8047 radii = {radius, radius};
8050 nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center -
8051 nsPoint(radii.width, radii.height),
8052 radii * 2);
8054 nscoord ellipseRadii[8];
8055 for (const auto corner : AllPhysicalHalfCorners()) {
8056 ellipseRadii[corner] =
8057 HalfCornerIsX(corner) ? radii.width : radii.height;
8060 clipId = aBuilder.DefineRoundedRectClip(
8061 Nothing(), wr::ToComplexClipRegion(ellipseRect, ellipseRadii,
8062 appUnitsPerDevPixel));
8064 break;
8066 default:
8067 // Please don't add more exceptions, try to find a way to define the clip
8068 // without using a mask image.
8070 // And if you _really really_ need to add an exception, add it to
8071 // SVGUtils::DetermineMaskUsage
8072 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8073 return Nothing();
8076 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8078 return Some(clipChainId);
8081 static void FillPolygonDataForDisplayItem(
8082 const nsDisplayMasksAndClipPaths& aDisplayItem,
8083 nsTArray<wr::LayoutPoint>& aPoints, wr::FillRule& aFillRule) {
8084 nsIFrame* frame = aDisplayItem.Frame();
8085 const auto* style = frame->StyleSVGReset();
8086 bool isPolygon = style->HasClipPath() && style->mClipPath.IsShape() &&
8087 style->mClipPath.AsShape()._0->IsPolygon();
8088 if (!isPolygon) {
8089 return;
8092 const auto& clipPath = style->mClipPath;
8093 const auto& shape = *clipPath.AsShape()._0;
8094 const nsRect refBox =
8095 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8097 // We only fill polygon data for polygons that are below a complexity
8098 // limit.
8099 nsTArray<nsPoint> vertices =
8100 ShapeUtils::ComputePolygonVertices(shape, refBox);
8101 if (vertices.Length() > wr::POLYGON_CLIP_VERTEX_MAX) {
8102 return;
8105 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8107 for (size_t i = 0; i < vertices.Length(); ++i) {
8108 wr::LayoutPoint point = wr::ToLayoutPoint(
8109 LayoutDevicePoint::FromAppUnits(vertices[i], appUnitsPerDevPixel));
8110 aPoints.AppendElement(point);
8113 aFillRule = (shape.AsPolygon().fill == StyleFillRule::Nonzero)
8114 ? wr::FillRule::Nonzero
8115 : wr::FillRule::Evenodd;
8118 static Maybe<wr::WrClipChainId> CreateWRClipPathAndMasks(
8119 nsDisplayMasksAndClipPaths* aDisplayItem, const LayoutDeviceRect& aBounds,
8120 wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder,
8121 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8122 nsDisplayListBuilder* aDisplayListBuilder) {
8123 if (auto clip = CreateSimpleClipRegion(*aDisplayItem, aBuilder)) {
8124 return clip;
8127 Maybe<wr::ImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(
8128 aDisplayItem, aBuilder, aResources, aSc, aDisplayListBuilder, aBounds);
8129 if (!mask) {
8130 return Nothing();
8133 // We couldn't create a simple clip region, but before we create an image
8134 // mask clip, see if we can get a polygon clip to add to it.
8135 nsTArray<wr::LayoutPoint> points;
8136 wr::FillRule fillRule = wr::FillRule::Nonzero;
8137 FillPolygonDataForDisplayItem(*aDisplayItem, points, fillRule);
8139 wr::WrClipId clipId =
8140 aBuilder.DefineImageMaskClip(mask.ref(), points, fillRule);
8142 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8144 return Some(clipChainId);
8147 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8148 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8149 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8150 nsDisplayListBuilder* aDisplayListBuilder) {
8151 bool snap;
8152 auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8153 nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
8154 LayoutDeviceRect bounds =
8155 LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel);
8157 Maybe<wr::WrClipChainId> clip = CreateWRClipPathAndMasks(
8158 this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder);
8160 float oldOpacity = aBuilder.GetInheritedOpacity();
8162 Maybe<StackingContextHelper> layer;
8163 const StackingContextHelper* sc = &aSc;
8164 if (clip) {
8165 // Create a new stacking context to attach the mask to, ensuring the mask is
8166 // applied to the aggregate, and not the individual elements.
8168 // The stacking context shouldn't have any offset.
8169 bounds.MoveTo(0, 0);
8171 Maybe<float> opacity =
8172 (SVGUtils::DetermineMaskUsage(mFrame, false).IsSimpleClipShape() &&
8173 aBuilder.GetInheritedOpacity() != 1.0f)
8174 ? Some(aBuilder.GetInheritedOpacity())
8175 : Nothing();
8177 wr::StackingContextParams params;
8178 params.clip = wr::WrStackingContextClip::ClipChain(clip->id);
8179 params.opacity = opacity.ptrOr(nullptr);
8180 if (mWrapsBackdropFilter) {
8181 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8183 layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params,
8184 bounds);
8185 sc = layer.ptr();
8188 aBuilder.SetInheritedOpacity(1.0f);
8189 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8190 aBuilder.SetInheritedClipChain(nullptr);
8191 CreateWebRenderCommandsNewClipListOption(aBuilder, aResources, *sc, aManager,
8192 aDisplayListBuilder, layer.isSome());
8193 aBuilder.SetInheritedOpacity(oldOpacity);
8194 aBuilder.SetInheritedClipChain(oldClipChain);
8196 return true;
8199 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8200 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
8201 if (const DisplayItemClip* clip =
8202 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
8203 return Some(clip->GetClipRect());
8205 // This item does not have a clip with respect to |aASR|. However, we
8206 // might still have finite bounds with respect to |aASR|. Check our
8207 // children.
8208 nsDisplayList* childList = GetSameCoordinateSystemChildren();
8209 if (childList) {
8210 return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR));
8212 #ifdef DEBUG
8213 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
8214 #endif
8215 return Nothing();
8218 #ifdef MOZ_DUMP_PAINTING
8219 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) {
8220 nsIFrame* firstFrame =
8221 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8222 bool first = true;
8223 aTo += " effects=(";
8224 SVGClipPathFrame* clipPathFrame;
8225 // XXX Check return value?
8226 SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
8227 if (clipPathFrame) {
8228 if (!first) {
8229 aTo += ", ";
8231 aTo += nsPrintfCString(
8232 "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
8233 first = false;
8234 } else if (mFrame->StyleSVGReset()->HasClipPath()) {
8235 if (!first) {
8236 aTo += ", ";
8238 aTo += "clip(basic-shape)";
8239 first = false;
8242 nsTArray<SVGMaskFrame*> masks;
8243 // XXX check return value?
8244 SVGObserverUtils::GetAndObserveMasks(firstFrame, &masks);
8245 if (!masks.IsEmpty() && masks[0]) {
8246 if (!first) {
8247 aTo += ", ";
8249 aTo += "mask";
8251 aTo += ")";
8253 #endif
8255 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8256 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8257 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8258 nsDisplayListBuilder* aDisplayListBuilder) {
8259 WrFiltersHolder wrFilters;
8260 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8261 auto filterChain = style.StyleEffects()->mBackdropFilters.AsSpan();
8262 bool initialized = true;
8263 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8264 wrFilters) &&
8265 !SVGIntegrationUtils::BuildWebRenderFilters(
8266 mFrame, filterChain, StyleFilterType::BackdropFilter, wrFilters,
8267 initialized)) {
8268 // TODO: If painting backdrop-filters on the content side is implemented,
8269 // consider returning false to fall back to that.
8270 wrFilters = {};
8273 if (!initialized) {
8274 wrFilters = {};
8277 nsCSSRendering::ImageLayerClipState clip;
8278 nsCSSRendering::GetImageLayerClip(
8279 style.StyleBackground()->BottomLayer(), mFrame, *style.StyleBorder(),
8280 mBackdropRect, mBackdropRect, false,
8281 mFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
8283 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
8284 mBackdropRect, mFrame->PresContext()->AppUnitsPerDevPixel());
8286 wr::ComplexClipRegion region =
8287 wr::ToComplexClipRegion(clip.mBGClipArea, clip.mRadii,
8288 mFrame->PresContext()->AppUnitsPerDevPixel());
8290 aBuilder.PushBackdropFilter(wr::ToLayoutRect(bounds), region,
8291 wrFilters.filters, wrFilters.filter_datas,
8292 !BackfaceIsHidden());
8294 wr::StackingContextParams params;
8295 params.clip =
8296 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8297 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8298 params);
8300 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8301 aDisplayListBuilder);
8302 return true;
8305 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder* aBuilder,
8306 gfxContext* aCtx) {
8307 // TODO: Implement backdrop filters
8308 GetChildren()->Paint(aBuilder, aCtx,
8309 mFrame->PresContext()->AppUnitsPerDevPixel());
8312 nsRect nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder* aBuilder,
8313 bool* aSnap) const {
8314 nsRect childBounds = nsDisplayWrapList::GetBounds(aBuilder, aSnap);
8316 *aSnap = false;
8318 return mBackdropRect.Union(childBounds);
8321 /* static */
8322 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder,
8323 nsIFrame* aFrame, nsDisplayList* aList,
8324 nsIFrame* aStyleFrame,
8325 bool aWrapsBackdropFilter)
8326 : nsDisplayEffectsBase(aBuilder, aFrame, aList),
8327 mStyle(aFrame == aStyleFrame ? nullptr : aStyleFrame->Style()),
8328 mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()),
8329 mWrapsBackdropFilter(aWrapsBackdropFilter) {
8330 MOZ_COUNT_CTOR(nsDisplayFilters);
8331 mVisibleRect = aBuilder->GetVisibleRect() +
8332 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
8335 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8336 PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) {
8337 GetChildren()->Paint(aBuilder, aContext,
8338 mFrame->PresContext()->AppUnitsPerDevPixel());
8342 void nsDisplayFilters::PaintWithContentsPaintCallback(
8343 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
8344 const std::function<void(gfxContext* aContext)>& aPaintChildren) {
8345 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
8346 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8347 PaintFramesParams params(*aCtx, mFrame, mVisibleRect, borderArea, aBuilder,
8348 false, imgParams);
8350 gfxPoint userSpaceToFrameSpaceOffset =
8351 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params);
8353 auto filterChain = mStyle ? mStyle->StyleEffects()->mFilters.AsSpan()
8354 : mFrame->StyleEffects()->mFilters.AsSpan();
8355 SVGIntegrationUtils::PaintFilter(
8356 params, filterChain,
8357 [&](gfxContext& aContext, imgDrawingParams&, const gfxMatrix*,
8358 const nsIntRect*) {
8359 gfxContextMatrixAutoSaveRestore autoSR(&aContext);
8360 aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(
8361 -userSpaceToFrameSpaceOffset));
8362 aPaintChildren(&aContext);
8366 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8367 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame);
8370 bool nsDisplayFilters::CreateWebRenderCommands(
8371 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8372 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8373 nsDisplayListBuilder* aDisplayListBuilder) {
8374 WrFiltersHolder wrFilters;
8375 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8376 auto filterChain = style.StyleEffects()->mFilters.AsSpan();
8377 bool initialized = true;
8378 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8379 wrFilters) &&
8380 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame, filterChain,
8381 StyleFilterType::Filter,
8382 wrFilters, initialized)) {
8383 if (mStyle) {
8384 // TODO(bug 1769223): Support fallback filters in the root code-path,
8385 // perhaps. For now treat it the same way as invalid filters.
8386 wrFilters = {};
8387 } else {
8388 // Draw using fallback.
8389 return false;
8393 if (!initialized) {
8394 // https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
8396 // If the filter references a non-existent object or the referenced object
8397 // is not a filter element, then the whole filter chain is ignored. No
8398 // filter is applied to the object.
8400 // Note that other engines have a weird discrepancy between SVG and HTML
8401 // content here, but the spec is clear.
8402 wrFilters = {};
8405 uint64_t clipChainId;
8406 if (wrFilters.post_filters_clip) {
8407 auto devPxRect = LayoutDeviceRect::FromAppUnits(
8408 wrFilters.post_filters_clip.value() + ToReferenceFrame(),
8409 mFrame->PresContext()->AppUnitsPerDevPixel());
8410 auto clipId =
8411 aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect));
8412 clipChainId = aBuilder.DefineClipChain({clipId}, true).id;
8413 } else {
8414 clipChainId = aBuilder.CurrentClipChainId();
8416 wr::WrStackingContextClip clip =
8417 wr::WrStackingContextClip::ClipChain(clipChainId);
8419 float opacity = aBuilder.GetInheritedOpacity();
8420 aBuilder.SetInheritedOpacity(1.0f);
8421 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8422 aBuilder.SetInheritedClipChain(nullptr);
8423 wr::StackingContextParams params;
8424 params.mFilters = std::move(wrFilters.filters);
8425 params.mFilterDatas = std::move(wrFilters.filter_datas);
8426 params.opacity = opacity != 1.0f ? &opacity : nullptr;
8427 params.clip = clip;
8428 if (mWrapsBackdropFilter) {
8429 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8431 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8432 params);
8434 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
8435 aManager, aDisplayListBuilder);
8436 aBuilder.SetInheritedOpacity(opacity);
8437 aBuilder.SetInheritedClipChain(oldClipChain);
8439 return true;
8442 #ifdef MOZ_DUMP_PAINTING
8443 void nsDisplayFilters::PrintEffects(nsACString& aTo) {
8444 nsIFrame* firstFrame =
8445 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8446 bool first = true;
8447 aTo += " effects=(";
8448 // We may exist for a mix of CSS filter functions and/or references to SVG
8449 // filters. If we have invalid references to SVG filters then we paint
8450 // nothing, but otherwise we will apply one or more filters.
8451 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) !=
8452 SVGObserverUtils::eHasRefsSomeInvalid) {
8453 if (!first) {
8454 aTo += ", ";
8456 aTo += "filter";
8458 aTo += ")";
8460 #endif
8462 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder,
8463 nsIFrame* aFrame, nsDisplayList* aList)
8464 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8465 MOZ_COUNT_CTOR(nsDisplaySVGWrapper);
8468 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8469 return !aBuilder->GetWidgetLayerManager();
8472 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8473 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8474 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8475 nsDisplayListBuilder* aDisplayListBuilder) {
8476 return CreateWebRenderCommandsNewClipListOption(
8477 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8480 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder* aBuilder,
8481 nsIFrame* aFrame,
8482 nsDisplayList* aList)
8483 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8484 MOZ_COUNT_CTOR(nsDisplayForeignObject);
8487 #ifdef NS_BUILD_REFCNT_LOGGING
8488 nsDisplayForeignObject::~nsDisplayForeignObject() {
8489 MOZ_COUNT_DTOR(nsDisplayForeignObject);
8491 #endif
8493 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8494 return !aBuilder->GetWidgetLayerManager();
8497 bool nsDisplayForeignObject::CreateWebRenderCommands(
8498 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8499 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8500 nsDisplayListBuilder* aDisplayListBuilder) {
8501 AutoRestore<bool> restoreDoGrouping(aManager->CommandBuilder().mDoGrouping);
8502 aManager->CommandBuilder().mDoGrouping = false;
8503 return CreateWebRenderCommandsNewClipListOption(
8504 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8507 void nsDisplayLink::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8508 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8509 aCtx->GetDrawTarget()->Link(
8510 mLinkSpec.get(), NSRectToRect(GetPaintRect(aBuilder, aCtx), appPerDev));
8513 void nsDisplayDestination::Paint(nsDisplayListBuilder* aBuilder,
8514 gfxContext* aCtx) {
8515 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8516 aCtx->GetDrawTarget()->Destination(
8517 mDestinationName.get(),
8518 NSPointToPoint(GetPaintRect(aBuilder, aCtx).TopLeft(), appPerDev));
8521 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8522 nsDisplayList* aOutResultList, nsIContent* aContent) {
8523 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
8524 // in content document order and SortByZOrder is a stable sort which
8525 // guarantees that boxes produced by the same element are placed together
8526 // in the sort. Consider a position:relative inline element that breaks
8527 // across lines and has absolutely positioned children; all the abs-pos
8528 // children should be z-ordered after all the boxes for the position:relative
8529 // element itself.
8530 PositionedDescendants()->SortByZOrder();
8532 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8533 // 1,2: backgrounds and borders
8534 aOutResultList->AppendToTop(BorderBackground());
8535 // 3: negative z-index children.
8536 for (auto* item : PositionedDescendants()->TakeItems()) {
8537 if (item->ZIndex() < 0) {
8538 aOutResultList->AppendToTop(item);
8539 } else {
8540 PositionedDescendants()->AppendToTop(item);
8544 // 4: block backgrounds
8545 aOutResultList->AppendToTop(BlockBorderBackgrounds());
8546 // 5: floats
8547 aOutResultList->AppendToTop(Floats());
8548 // 7: general content
8549 aOutResultList->AppendToTop(Content());
8550 // 7.5: outlines, in content tree order. We need to sort by content order
8551 // because an element with outline that breaks and has children with outline
8552 // might have placed child outline items between its own outline items.
8553 // The element's outline items need to all come before any child outline
8554 // items.
8555 if (aContent) {
8556 Outlines()->SortByContentOrder(aContent);
8558 aOutResultList->AppendToTop(Outlines());
8559 // 8, 9: non-negative z-index children
8560 aOutResultList->AppendToTop(PositionedDescendants());
8563 uint32_t PaintTelemetry::sPaintLevel = 0;
8565 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8566 // Don't record nested paints.
8567 if (sPaintLevel++ > 0) {
8568 return;
8571 mStart = TimeStamp::Now();
8574 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8575 MOZ_ASSERT(sPaintLevel != 0);
8576 if (--sPaintLevel > 0) {
8577 return;
8580 // If we're in multi-process mode, don't include paint times for the parent
8581 // process.
8582 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8583 return;
8586 // Record the total time.
8587 mozilla::glean::gfx_content::paint_time.AccumulateRawDuration(
8588 TimeStamp::Now() - mStart);
8591 static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
8592 if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
8593 return aFrame;
8596 if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
8597 !aFrame->GetPrevInFlow()) {
8598 return aFrame->GetPlaceholderFrame();
8601 return aFrame;
8604 static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
8605 nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
8606 MOZ_ASSERT(f);
8607 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
8610 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8611 nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
8612 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
8613 const bool aIsTransformed)
8614 : mBuilder(aBuilder),
8615 mPrevFrame(aBuilder->mCurrentFrame),
8616 mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
8617 mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
8618 mPrevAdditionalOffset(aBuilder->mAdditionalOffset),
8619 mPrevVisibleRect(aBuilder->mVisibleRect),
8620 mPrevDirtyRect(aBuilder->mDirtyRect),
8621 mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo),
8622 mPrevAncestorHasApzAwareEventHandler(
8623 aBuilder->mAncestorHasApzAwareEventHandler),
8624 mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
8625 mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
8626 if (aIsTransformed) {
8627 aBuilder->mCurrentOffsetToReferenceFrame =
8628 aBuilder->AdditionalOffset().refOr(nsPoint());
8629 aBuilder->mCurrentReferenceFrame = aForChild;
8630 } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
8631 aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
8632 } else {
8633 aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
8634 aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
8637 // If aForChild is being visited from a frame other than it's ancestor frame,
8638 // mInInvalidSubtree will need to be recalculated the slow way.
8639 if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) {
8640 aBuilder->mInInvalidSubtree =
8641 aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
8642 } else {
8643 aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
8646 aBuilder->mCurrentFrame = aForChild;
8647 aBuilder->mVisibleRect = aVisibleRect;
8648 aBuilder->mDirtyRect =
8649 aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
8652 } // namespace mozilla