Backed out changeset 496886cb30a5 (bug 1867152) for bc failures on browser_user_input...
[gecko.git] / layout / painting / nsDisplayList.cpp
blobd65402b3659cf2c7683d9b1b4e55d069b2e3c02b
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/HashTable.h"
73 #include "mozilla/LookAndFeel.h"
74 #include "mozilla/OperatorNewExtensions.h"
75 #include "mozilla/Preferences.h"
76 #include "mozilla/ProfilerLabels.h"
77 #include "mozilla/ProfilerMarkers.h"
78 #include "mozilla/StyleAnimationValue.h"
79 #include "mozilla/ServoBindings.h"
80 #include "mozilla/SVGClipPathFrame.h"
81 #include "mozilla/SVGMaskFrame.h"
82 #include "mozilla/SVGObserverUtils.h"
83 #include "mozilla/Telemetry.h"
84 #include "mozilla/UniquePtr.h"
85 #include "mozilla/Unused.h"
86 #include "mozilla/ViewportFrame.h"
87 #include "mozilla/gfx/gfxVars.h"
88 #include "ActiveLayerTracker.h"
89 #include "nsEscape.h"
90 #include "nsPrintfCString.h"
91 #include "UnitTransforms.h"
92 #include "LayerAnimationInfo.h"
93 #include "mozilla/EventStateManager.h"
94 #include "nsCaret.h"
95 #include "nsDOMTokenList.h"
96 #include "nsCSSProps.h"
97 #include "nsTableCellFrame.h"
98 #include "nsTableColFrame.h"
99 #include "nsTextFrame.h"
100 #include "nsTextPaintStyle.h"
101 #include "nsSliderFrame.h"
102 #include "nsFocusManager.h"
103 #include "TextDrawTarget.h"
104 #include "mozilla/layers/AnimationHelper.h"
105 #include "mozilla/layers/CompositorThread.h"
106 #include "mozilla/layers/InputAPZContext.h"
107 #include "mozilla/layers/RenderRootStateManager.h"
108 #include "mozilla/layers/StackingContextHelper.h"
109 #include "mozilla/layers/TreeTraversal.h"
110 #include "mozilla/layers/WebRenderBridgeChild.h"
111 #include "mozilla/layers/WebRenderLayerManager.h"
112 #include "mozilla/layers/WebRenderMessages.h"
113 #include "mozilla/layers/WebRenderScrollData.h"
115 namespace mozilla {
117 using namespace dom;
118 using namespace gfx;
119 using namespace layout;
120 using namespace layers;
121 using namespace image;
123 LazyLogModule sContentDisplayListLog("dl.content");
124 LazyLogModule sParentDisplayListLog("dl.parent");
126 LazyLogModule& GetLoggerByProcess() {
127 return XRE_IsContentProcess() ? sContentDisplayListLog
128 : sParentDisplayListLog;
131 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
132 void AssertUniqueItem(nsDisplayItem* aItem) {
133 for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) {
134 if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
135 i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
136 if (i->IsPreProcessedItem() || i->IsPreProcessed()) {
137 continue;
139 MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
143 #endif
145 bool ShouldBuildItemForEvents(const DisplayItemType aType) {
146 return aType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO ||
147 (GetDisplayItemFlagsForType(aType) & TYPE_IS_CONTAINER);
150 static bool ItemTypeSupportsHitTesting(const DisplayItemType aType) {
151 switch (aType) {
152 case DisplayItemType::TYPE_BACKGROUND:
153 case DisplayItemType::TYPE_BACKGROUND_COLOR:
154 case DisplayItemType::TYPE_THEMED_BACKGROUND:
155 return true;
156 default:
157 return false;
161 void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder,
162 nsPaintedDisplayItem* aItem,
163 const DisplayItemType aType) {
164 if (ItemTypeSupportsHitTesting(aType)) {
165 aItem->InitializeHitTestInfo(aBuilder);
169 /* static */
170 already_AddRefed<ActiveScrolledRoot> ActiveScrolledRoot::CreateASRForFrame(
171 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame,
172 bool aIsRetained) {
173 nsIFrame* f = do_QueryFrame(aScrollableFrame);
175 RefPtr<ActiveScrolledRoot> asr;
176 if (aIsRetained) {
177 asr = f->GetProperty(ActiveScrolledRootCache());
180 if (!asr) {
181 asr = new ActiveScrolledRoot();
183 if (aIsRetained) {
184 RefPtr<ActiveScrolledRoot> ref = asr;
185 f->SetProperty(ActiveScrolledRootCache(), ref.forget().take());
188 asr->mParent = aParent;
189 asr->mScrollableFrame = aScrollableFrame;
190 asr->mViewId = Nothing();
191 asr->mDepth = aParent ? aParent->mDepth + 1 : 1;
192 asr->mRetained = aIsRetained;
194 return asr.forget();
197 /* static */
198 bool ActiveScrolledRoot::IsAncestor(const ActiveScrolledRoot* aAncestor,
199 const ActiveScrolledRoot* aDescendant) {
200 if (!aAncestor) {
201 // nullptr is the root
202 return true;
204 if (Depth(aAncestor) > Depth(aDescendant)) {
205 return false;
207 const ActiveScrolledRoot* asr = aDescendant;
208 while (asr) {
209 if (asr == aAncestor) {
210 return true;
212 asr = asr->mParent;
214 return false;
217 /* static */
218 bool ActiveScrolledRoot::IsProperAncestor(
219 const ActiveScrolledRoot* aAncestor,
220 const ActiveScrolledRoot* aDescendant) {
221 return aAncestor != aDescendant && IsAncestor(aAncestor, aDescendant);
224 /* static */
225 nsCString ActiveScrolledRoot::ToString(
226 const ActiveScrolledRoot* aActiveScrolledRoot) {
227 nsAutoCString str;
228 for (const auto* asr = aActiveScrolledRoot; asr; asr = asr->mParent) {
229 str.AppendPrintf("<0x%p>", asr->mScrollableFrame);
230 if (asr->mParent) {
231 str.AppendLiteral(", ");
234 return std::move(str);
237 ScrollableLayerGuid::ViewID ActiveScrolledRoot::ComputeViewId() const {
238 nsIContent* content = mScrollableFrame->GetScrolledFrame()->GetContent();
239 return nsLayoutUtils::FindOrCreateIDFor(content);
242 ActiveScrolledRoot::~ActiveScrolledRoot() {
243 if (mScrollableFrame && mRetained) {
244 nsIFrame* f = do_QueryFrame(mScrollableFrame);
245 f->RemoveProperty(ActiveScrolledRootCache());
249 static uint64_t AddAnimationsForWebRender(
250 nsDisplayItem* aItem, RenderRootStateManager* aManager,
251 nsDisplayListBuilder* aDisplayListBuilder,
252 const Maybe<LayoutDevicePoint>& aPosition = Nothing()) {
253 auto* effects = EffectSet::GetForFrame(aItem->Frame(), aItem->GetType());
254 if (!effects || effects->IsEmpty()) {
255 // If there is no animation on the nsIFrame, that means
256 // 1) we've never created any animations on this frame or
257 // 2) the frame was reconstruced or
258 // 3) all animations on the frame have finished
259 // in such cases we don't need do anything here.
261 // Even if there is a WebRenderAnimationData for the display item type on
262 // this frame, it's going to be discarded since it's not marked as being
263 // used.
264 return 0;
267 RefPtr<WebRenderAnimationData> animationData =
268 aManager->CommandBuilder()
269 .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(aItem);
270 AnimationInfo& animationInfo = animationData->GetAnimationInfo();
271 nsIFrame* frame = aItem->Frame();
272 animationInfo.AddAnimationsForDisplayItem(
273 frame, aDisplayListBuilder, aItem, aItem->GetType(),
274 aManager->LayerManager(), aPosition);
275 animationInfo.StartPendingAnimations(
276 frame->PresContext()->RefreshDriver()->MostRecentRefresh(
277 /* aEnsureTimerStarted = */ false));
279 // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
280 // are no active animations.
281 uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
282 if (!animationInfo.GetAnimations().IsEmpty()) {
283 OpAddCompositorAnimations anim(
284 CompositorAnimations(animationInfo.GetAnimations(), animationsId));
285 aManager->WrBridge()->AddWebRenderParentCommand(anim);
286 aManager->AddActiveCompositorAnimationId(animationsId);
287 } else if (animationsId) {
288 aManager->AddCompositorAnimationsIdForDiscard(animationsId);
289 animationsId = 0;
292 return animationsId;
295 static bool GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
296 const nsRect& aFillRect,
297 nsDisplayListBuilder* aBuilder) {
298 if (aBuilder->IsForGenerateGlyphMask()) {
299 return false;
302 SVGObserverUtils::GetAndObserveBackgroundClip(aFrame);
304 // The main function of enabling background-clip:text property value.
305 // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
306 // this function to
307 // 1. Generate a mask by all descendant text frames
308 // 2. Push the generated mask into aContext.
310 gfxContext* sourceCtx = aContext;
311 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
312 aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel());
314 // Create a mask surface.
315 RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget();
316 RefPtr<DrawTarget> maskDT = sourceTarget->CreateClippedDrawTarget(
317 bounds.ToUnknownRect(), SurfaceFormat::A8);
318 if (!maskDT || !maskDT->IsValid()) {
319 return false;
321 gfxContext maskCtx(maskDT, /* aPreserveTransform */ true);
322 maskCtx.Multiply(Matrix::Translation(bounds.TopLeft().ToUnknownPoint()));
324 // Shade text shape into mask A8 surface.
325 nsLayoutUtils::PaintFrame(
326 &maskCtx, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()),
327 NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph);
329 // Push the generated mask into aContext, so that the caller can pop and
330 // blend with it.
332 Matrix currentMatrix = sourceCtx->CurrentMatrix();
333 Matrix invCurrentMatrix = currentMatrix;
334 invCurrentMatrix.Invert();
336 RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
337 sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0,
338 maskSurface, invCurrentMatrix);
340 return true;
343 nsDisplayWrapper* nsDisplayWrapList::CreateShallowCopy(
344 nsDisplayListBuilder* aBuilder) {
345 const nsDisplayWrapList* wrappedItem = AsDisplayWrapList();
346 MOZ_ASSERT(wrappedItem);
348 // Create a new nsDisplayWrapList using a copy-constructor. This is done
349 // to preserve the information about bounds.
350 nsDisplayWrapper* wrapper =
351 new (aBuilder) nsDisplayWrapper(aBuilder, *wrappedItem);
352 wrapper->SetType(nsDisplayWrapper::ItemType());
353 MOZ_ASSERT(wrapper);
355 // Set the display list pointer of the new wrapper item to the display list
356 // of the wrapped item.
357 wrapper->mListPtr = wrappedItem->mListPtr;
358 return wrapper;
361 nsDisplayWrapList* nsDisplayListBuilder::MergeItems(
362 nsTArray<nsDisplayItem*>& aItems) {
363 // For merging, we create a temporary item by cloning the last item of the
364 // mergeable items list. This ensures that the temporary item will have the
365 // correct frame and bounds.
366 nsDisplayWrapList* last = aItems.PopLastElement()->AsDisplayWrapList();
367 MOZ_ASSERT(last);
368 nsDisplayWrapList* merged = last->Clone(this);
369 MOZ_ASSERT(merged);
370 AddTemporaryItem(merged);
372 // Create nsDisplayWrappers that point to the internal display lists of the
373 // items we are merging. These nsDisplayWrappers are added to the display list
374 // of the temporary item.
375 for (nsDisplayItem* item : aItems) {
376 MOZ_ASSERT(item);
377 MOZ_ASSERT(merged->CanMerge(item));
378 merged->Merge(item);
379 MOZ_ASSERT(item->AsDisplayWrapList());
380 merged->GetChildren()->AppendToTop(
381 static_cast<nsDisplayWrapList*>(item)->CreateShallowCopy(this));
384 merged->GetChildren()->AppendToTop(last->CreateShallowCopy(this));
386 return merged;
389 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
390 SetCurrentActiveScrolledRoot(
391 const ActiveScrolledRoot* aActiveScrolledRoot) {
392 MOZ_ASSERT(!mUsed);
394 // Set the builder's mCurrentActiveScrolledRoot.
395 mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;
397 // We also need to adjust the builder's mCurrentContainerASR.
398 // mCurrentContainerASR needs to be an ASR that all the container's
399 // contents have finite bounds with respect to. If aActiveScrolledRoot
400 // is an ancestor ASR of mCurrentContainerASR, that means we need to
401 // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
402 // the items that will be created with aActiveScrolledRoot wouldn't
403 // have finite bounds with respect to mCurrentContainerASR. There's one
404 // exception, in the case where there's a content clip on the builder
405 // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
406 // content clip will clip all items that are created while this
407 // AutoCurrentActiveScrolledRootSetter exists. This means that the items
408 // created during our lifetime will have finite bounds with respect to
409 // the content clip's ASR, even if the items' actual ASR is an ancestor
410 // of that. And it also means that mCurrentContainerASR only needs to be
411 // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
412 // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
413 // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
415 // finiteBoundsASR is the leafmost ASR that all items created during
416 // object's lifetime have finite bounds with respect to.
417 const ActiveScrolledRoot* finiteBoundsASR =
418 ActiveScrolledRoot::PickDescendant(mContentClipASR, aActiveScrolledRoot);
420 // mCurrentContainerASR is adjusted so that it's still an ancestor of
421 // finiteBoundsASR.
422 mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
423 mBuilder->mCurrentContainerASR, finiteBoundsASR);
425 // If we are entering out-of-flow content inside a CSS filter, mark
426 // scroll frames wrt. which the content is fixed as containing such content.
427 if (mBuilder->mFilterASR && ActiveScrolledRoot::IsAncestor(
428 aActiveScrolledRoot, mBuilder->mFilterASR)) {
429 for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
430 asr && asr != aActiveScrolledRoot; asr = asr->mParent) {
431 asr->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
435 mUsed = true;
438 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
439 InsertScrollFrame(nsIScrollableFrame* aScrollableFrame) {
440 MOZ_ASSERT(!mUsed);
441 size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
442 const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot;
443 const ActiveScrolledRoot* asr =
444 mBuilder->AllocateActiveScrolledRoot(parentASR, aScrollableFrame);
445 mBuilder->mCurrentActiveScrolledRoot = asr;
447 // All child ASRs of parentASR that were created while this
448 // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
449 // now. Reparent them to asr.
450 for (size_t i = mDescendantsStartIndex; i < descendantsEndIndex; i++) {
451 ActiveScrolledRoot* descendantASR = mBuilder->mActiveScrolledRoots[i];
452 if (ActiveScrolledRoot::IsAncestor(parentASR, descendantASR)) {
453 descendantASR->IncrementDepth();
454 if (descendantASR->mParent == parentASR) {
455 descendantASR->mParent = asr;
460 mUsed = true;
463 nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
464 nsDisplayListBuilder* aBuilder)
465 : mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) {
466 mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot;
469 nsPresContext* nsDisplayListBuilder::CurrentPresContext() {
470 return CurrentPresShellState()->mPresShell->GetPresContext();
473 /* static */
474 nsRect nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
475 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
476 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
477 nsRect* aOutDirtyRect) {
478 nsRect visible = aVisibleRect;
479 nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
481 bool inPartialUpdate =
482 aBuilder->IsRetainingDisplayList() && aBuilder->IsPartialUpdate();
483 if (StaticPrefs::apz_allow_zooming() &&
484 DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame) &&
485 aBuilder->IsPaintingToWindow() && !inPartialUpdate) {
486 dirtyRectRelativeToDirtyFrame =
487 nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize());
489 // If there's a visual viewport size set, restrict the amount of the
490 // fixed-position element we paint to the visual viewport. (In general
491 // the fixed-position element can be as large as the layout viewport,
492 // which at a high zoom level can cause us to paint too large of an
493 // area.)
494 PresShell* presShell = aFrame->PresShell();
495 if (presShell->IsVisualViewportSizeSet()) {
496 dirtyRectRelativeToDirtyFrame =
497 nsRect(presShell->GetVisualViewportOffsetRelativeToLayoutViewport(),
498 presShell->GetVisualViewportSize());
499 // But if we have a displayport, expand it to the displayport, so
500 // that async-scrolling the visual viewport within the layout viewport
501 // will not checkerboard.
502 if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
503 nsRect displayport;
504 // Note that the displayport here is already in the right coordinate
505 // space: it's relative to the scroll port (= layout viewport), but
506 // covers the visual viewport with some margins around it, which is
507 // exactly what we want.
508 if (DisplayPortUtils::GetDisplayPort(
509 rootScrollFrame->GetContent(), &displayport,
510 DisplayPortOptions().With(ContentGeometryType::Fixed))) {
511 dirtyRectRelativeToDirtyFrame = displayport;
515 visible = dirtyRectRelativeToDirtyFrame;
516 if (StaticPrefs::apz_test_logging_enabled() &&
517 presShell->GetDocument()->IsContentDocument()) {
518 nsLayoutUtils::LogAdditionalTestData(
519 aBuilder, "fixedPosDisplayport",
520 ToString(CSSSize::FromAppUnits(visible)));
524 *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
525 visible -= aFrame->GetPosition();
527 nsRect overflowRect = aFrame->InkOverflowRect();
529 if (aFrame->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
530 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
532 * Add a fuzz factor to the overflow rectangle so that elements only
533 * just out of view are pulled into the display list, so they can be
534 * prerendered if necessary.
536 overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
539 visible.IntersectRect(visible, overflowRect);
540 aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect);
542 return visible;
545 nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder* aBuilder,
546 nsIFrame* aFrame,
547 nsDisplayList* aList)
548 : mList(aList) {
549 // Find the element that we need to check for link-ness, bailing out if
550 // we can't find one.
551 Element* elem = Element::FromNodeOrNull(aFrame->GetContent());
552 if (!elem) {
553 return;
556 // If the element has an id and/or name attribute, generate a destination
557 // for possible internal linking.
558 auto maybeGenerateDest = [&](const nsAtom* aAttr) {
559 nsAutoString attrValue;
560 elem->GetAttr(aAttr, attrValue);
561 if (!attrValue.IsEmpty()) {
562 NS_ConvertUTF16toUTF8 dest(attrValue);
563 // Ensure that we only emit a given destination once, although there may
564 // be multiple frames associated with a given element; we'll simply use
565 // the first of them as the target of any links to it.
566 // XXX(jfkthame) This prevents emitting duplicate destinations *on the
567 // same page*, but does not prevent duplicates on subsequent pages, as
568 // each new page is handled by a new temporary DisplayListBuilder. This
569 // seems to be harmless in practice, though a bit wasteful of space. To
570 // fix, we need to maintain the set of already-seen destinations globally
571 // for the print job, rather than attached to the (per-page) builder.
572 if (aBuilder->mDestinations.EnsureInserted(dest)) {
573 auto* destination = MakeDisplayItem<nsDisplayDestination>(
574 aBuilder, aFrame, dest.get(), aFrame->GetRect().TopLeft());
575 mList->AppendToTop(destination);
580 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
581 if (elem->HasID()) {
582 maybeGenerateDest(nsGkAtoms::id);
584 if (elem->HasName()) {
585 maybeGenerateDest(nsGkAtoms::name);
589 // Links don't nest, so if the builder already has a destination, no need to
590 // check for a link element here.
591 if (!aBuilder->mLinkSpec.IsEmpty()) {
592 return;
595 // Check if we have actually found a link.
596 if (!elem->IsLink()) {
597 return;
600 nsCOMPtr<nsIURI> uri = elem->GetHrefURI();
601 if (!uri) {
602 return;
605 // Is it a local (in-page) destination?
606 bool hasRef, eqExRef;
607 nsIURI* docURI;
608 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
609 NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef &&
610 (docURI = aFrame->PresContext()->Document()->GetDocumentURI()) &&
611 NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &eqExRef)) && eqExRef) {
612 if (NS_FAILED(uri->GetRef(aBuilder->mLinkSpec)) ||
613 aBuilder->mLinkSpec.IsEmpty()) {
614 return;
616 // The destination name is simply a string; we don't want URL-escaping
617 // applied to it.
618 NS_UnescapeURL(aBuilder->mLinkSpec);
619 // Mark the link spec as being an internal destination
620 aBuilder->mLinkSpec.Insert('#', 0);
621 } else {
622 if (NS_FAILED(uri->GetSpec(aBuilder->mLinkSpec)) ||
623 aBuilder->mLinkSpec.IsEmpty()) {
624 return;
628 // Record that we need to reset the builder's state on destruction.
629 mBuilderToReset = aBuilder;
632 void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
633 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
634 // Note that we may generate a link here even if the constructor bailed out
635 // without updating aBuilder->LinkSpec(), because it may have been set by
636 // an ancestor that was associated with a link element.
637 if (!aBuilder->mLinkSpec.IsEmpty()) {
638 auto* link = MakeDisplayItem<nsDisplayLink>(
639 aBuilder, aFrame, aBuilder->mLinkSpec.get(), aFrame->GetRect());
640 mList->AppendToTop(link);
644 uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1);
646 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
647 nsDisplayListBuilderMode aMode,
648 bool aBuildCaret,
649 bool aRetainingDisplayList)
650 : mReferenceFrame(aReferenceFrame),
651 mIgnoreScrollFrame(nullptr),
652 mCurrentActiveScrolledRoot(nullptr),
653 mCurrentContainerASR(nullptr),
654 mCurrentFrame(aReferenceFrame),
655 mCurrentReferenceFrame(aReferenceFrame),
656 mCaretFrame(nullptr),
657 mScrollInfoItemsForHoisting(nullptr),
658 mFirstClipChainToDestroy(nullptr),
659 mTableBackgroundSet(nullptr),
660 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
661 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
662 mFilterASR(nullptr),
663 mDirtyRect(-1, -1, -1, -1),
664 mBuildingExtraPagesForPageNum(0),
665 mMode(aMode),
666 mContainsBlendMode(false),
667 mIsBuildingScrollbar(false),
668 mCurrentScrollbarWillHaveLayer(false),
669 mBuildCaret(aBuildCaret),
670 mRetainingDisplayList(aRetainingDisplayList),
671 mPartialUpdate(false),
672 mIgnoreSuppression(false),
673 mIncludeAllOutOfFlows(false),
674 mDescendIntoSubdocuments(true),
675 mSelectedFramesOnly(false),
676 mAllowMergingAndFlattening(true),
677 mInTransform(false),
678 mInEventsOnly(false),
679 mInFilter(false),
680 mInPageSequence(false),
681 mIsInChromePresContext(false),
682 mSyncDecodeImages(false),
683 mIsPaintingToWindow(false),
684 mUseHighQualityScaling(false),
685 mIsPaintingForWebRender(false),
686 mIsCompositingCheap(false),
687 mAncestorHasApzAwareEventHandler(false),
688 mHaveScrollableDisplayPort(false),
689 mWindowDraggingAllowed(false),
690 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
691 mForceLayerForScrollParent(false),
692 mContainsNonMinimalDisplayPort(false),
693 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
694 mBuildingInvisibleItems(false),
695 mIsBuilding(false),
696 mInInvalidSubtree(false),
697 mDisablePartialUpdates(false),
698 mPartialBuildFailed(false),
699 mIsInActiveDocShell(false),
700 mBuildAsyncZoomContainer(false),
701 mIsRelativeToLayoutViewport(false),
702 mUseOverlayScrollbars(false),
703 mAlwaysLayerizeScrollbars(false) {
704 MOZ_COUNT_CTOR(nsDisplayListBuilder);
706 mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting();
708 ShouldRebuildDisplayListDueToPrefChange();
710 mUseOverlayScrollbars =
711 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
713 mAlwaysLayerizeScrollbars =
714 StaticPrefs::layout_scrollbars_always_layerize_track();
716 static_assert(
717 static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
718 "Check TYPE_MAX should not overflow");
720 mIsReusingStackingContextItems =
721 mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
724 static PresShell* GetFocusedPresShell() {
725 nsPIDOMWindowOuter* focusedWnd =
726 nsFocusManager::GetFocusManager()->GetFocusedWindow();
727 if (!focusedWnd) {
728 return nullptr;
731 nsCOMPtr<nsIDocShell> focusedDocShell = focusedWnd->GetDocShell();
732 if (!focusedDocShell) {
733 return nullptr;
736 return focusedDocShell->GetPresShell();
739 void nsDisplayListBuilder::BeginFrame() {
740 nsCSSRendering::BeginFrameTreesLocked();
742 mIsPaintingToWindow = false;
743 mUseHighQualityScaling = false;
744 mIgnoreSuppression = false;
745 mInTransform = false;
746 mInFilter = false;
747 mSyncDecodeImages = false;
749 if (!mBuildCaret) {
750 return;
753 RefPtr<PresShell> presShell = GetFocusedPresShell();
754 if (presShell) {
755 RefPtr<nsCaret> caret = presShell->GetCaret();
756 mCaretFrame = caret->GetPaintGeometry(&mCaretRect);
758 // The focused pres shell may not be in the document that we're
759 // painting, or be in a popup. Check if the display root for
760 // the caret matches the display root that we're painting, and
761 // only use it if it matches.
762 if (mCaretFrame &&
763 nsLayoutUtils::GetDisplayRootFrame(mCaretFrame) !=
764 nsLayoutUtils::GetDisplayRootFrame(mReferenceFrame)) {
765 mCaretFrame = nullptr;
770 void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser* aBrowser,
771 const dom::EffectsInfo& aUpdate) {
772 dom::EffectsInfo update = aUpdate;
773 // For printing we create one display item for each page that an iframe
774 // appears on, the proper visible rect is the union of all the visible rects
775 // we get from each display item.
776 nsPresContext* pc =
777 mReferenceFrame ? mReferenceFrame->PresContext() : nullptr;
778 if (pc && pc->Type() != nsPresContext::eContext_Galley) {
779 Maybe<dom::EffectsInfo> existing = mEffectsUpdates.MaybeGet(aBrowser);
780 if (existing) {
781 // Only the visible rect should differ, the scales should match.
782 MOZ_ASSERT(existing->mRasterScale == aUpdate.mRasterScale &&
783 existing->mTransformToAncestorScale ==
784 aUpdate.mTransformToAncestorScale);
785 if (existing->mVisibleRect) {
786 if (update.mVisibleRect) {
787 update.mVisibleRect =
788 Some(update.mVisibleRect->Union(*existing->mVisibleRect));
789 } else {
790 update.mVisibleRect = existing->mVisibleRect;
795 mEffectsUpdates.InsertOrUpdate(aBrowser, update);
798 void nsDisplayListBuilder::EndFrame() {
799 NS_ASSERTION(!mInInvalidSubtree,
800 "Someone forgot to cleanup mInInvalidSubtree!");
801 mCurrentContainerASR = nullptr;
802 mActiveScrolledRoots.Clear();
803 mEffectsUpdates.Clear();
804 FreeClipChains();
805 FreeTemporaryItems();
806 nsCSSRendering::EndFrameTreesLocked();
807 mCaretFrame = nullptr;
810 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
811 const nsIFrame* aStopAtFrame) {
812 mFramesMarkedForDisplay.AppendElement(aFrame);
813 for (nsIFrame* f = aFrame; f;
814 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
815 if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
816 return;
818 f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
819 if (f == aStopAtFrame) {
820 // we've reached a frame that we know will be painted, so we can stop.
821 break;
826 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) {
827 mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
830 static void MarkFrameForDisplayIfVisibleInternal(nsIFrame* aFrame,
831 const nsIFrame* aStopAtFrame) {
832 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
833 if (f->ForceDescendIntoIfVisible()) {
834 return;
836 f->SetForceDescendIntoIfVisible(true);
838 // This condition must match the condition in
839 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
840 // nsLayoutUtils::GetDisplayListParent
841 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
842 nsIFrame* parent = f->GetParent();
843 if (parent && !parent->ForceDescendIntoIfVisible()) {
844 // If the GetDisplayListParent call is going to walk to a placeholder,
845 // in rare cases the placeholder might be contained in a different
846 // continuation from the oof. So we have to make sure to mark the oofs
847 // parent. In the common case this doesn't make us do any extra work,
848 // just changes the order in which we visit the frames since walking
849 // through placeholders will walk through the parent, and we stop when
850 // we find a ForceDescendIntoIfVisible bit set.
851 MarkFrameForDisplayIfVisibleInternal(parent, aStopAtFrame);
855 if (f == aStopAtFrame) {
856 // we've reached a frame that we know will be painted, so we can stop.
857 break;
862 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
863 nsIFrame* aFrame, const nsIFrame* aStopAtFrame) {
864 AddFrameMarkedForDisplayIfVisible(aFrame);
866 MarkFrameForDisplayIfVisibleInternal(aFrame, aStopAtFrame);
869 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
870 mIsRelativeToLayoutViewport = true;
871 UpdateShouldBuildAsyncZoomContainer();
874 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
875 const Document* document = mReferenceFrame->PresContext()->Document();
876 mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
877 !document->Fullscreen() &&
878 nsLayoutUtils::AllowZoomingForDocument(document);
881 // Certain prefs may cause display list items to be added or removed when they
882 // are toggled. In those cases, we need to fully rebuild the display list.
883 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
884 // If we transition between wrapping the RCD-RSF contents into an async
885 // zoom container vs. not, we need to rebuild the display list. This only
886 // happens when the zooming or container scrolling prefs are toggled
887 // (manually by the user, or during test setup).
888 bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer;
889 UpdateShouldBuildAsyncZoomContainer();
891 bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars;
892 mUseOverlayScrollbars =
893 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
895 bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars;
896 mAlwaysLayerizeScrollbars =
897 StaticPrefs::layout_scrollbars_always_layerize_track();
899 if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) {
900 return true;
903 if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) {
904 return true;
907 if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) {
908 return true;
911 return false;
914 void nsDisplayListBuilder::AddScrollFrameToNotify(
915 nsIScrollableFrame* aScrollFrame) {
916 mScrollFramesToNotify.insert(aScrollFrame);
919 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
920 for (const auto& it : mScrollFramesToNotify) {
921 it->NotifyApzTransaction();
923 mScrollFramesToNotify.clear();
926 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
927 nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect,
928 const nsRect& aDirtyRect) {
929 MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
930 nsRect dirty;
931 nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
932 this, aFrame, aVisibleRect, aDirtyRect, &dirty);
933 if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
934 visible.IsEmpty()) {
935 return false;
938 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
939 // frame, then it will also mark any outer frames to ensure that building
940 // reaches the dirty feame.
941 if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
942 MarkFrameForDisplay(aFrame, aDirtyFrame);
945 return true;
948 static void UnmarkFrameForDisplay(nsIFrame* aFrame,
949 const nsIFrame* aStopAtFrame) {
950 for (nsIFrame* f = aFrame; f;
951 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
952 if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
953 return;
955 f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
956 if (f == aStopAtFrame) {
957 // we've reached a frame that we know will be painted, so we can stop.
958 break;
963 static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
964 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
965 if (!f->ForceDescendIntoIfVisible()) {
966 return;
968 f->SetForceDescendIntoIfVisible(false);
970 // This condition must match the condition in
971 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
972 // nsLayoutUtils::GetDisplayListParent
973 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
974 nsIFrame* parent = f->GetParent();
975 if (parent && parent->ForceDescendIntoIfVisible()) {
976 // If the GetDisplayListParent call is going to walk to a placeholder,
977 // in rare cases the placeholder might be contained in a different
978 // continuation from the oof. So we have to make sure to mark the oofs
979 // parent. In the common case this doesn't make us do any extra work,
980 // just changes the order in which we visit the frames since walking
981 // through placeholders will walk through the parent, and we stop when
982 // we find a ForceDescendIntoIfVisible bit set.
983 UnmarkFrameForDisplayIfVisible(f);
989 nsDisplayListBuilder::~nsDisplayListBuilder() {
990 NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
991 "All frames should have been unmarked");
992 NS_ASSERTION(mFramesWithOOFData.Length() == 0,
993 "All OOF data should have been removed");
994 NS_ASSERTION(mPresShellStates.Length() == 0,
995 "All presshells should have been exited");
997 DisplayItemClipChain* c = mFirstClipChainToDestroy;
998 while (c) {
999 DisplayItemClipChain* next = c->mNextClipChainToDestroy;
1000 c->DisplayItemClipChain::~DisplayItemClipChain();
1001 c = next;
1004 MOZ_COUNT_DTOR(nsDisplayListBuilder);
1007 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
1008 uint32_t flags = 0;
1009 if (mSyncDecodeImages) {
1010 flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
1012 if (mIsPaintingToWindow) {
1013 flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
1015 if (mUseHighQualityScaling) {
1016 flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING;
1018 return flags;
1021 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
1022 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
1023 uint32_t flags = 0;
1024 if (mSyncDecodeImages) {
1025 flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
1027 if (mIsPaintingToWindow) {
1028 flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
1030 if (mUseHighQualityScaling) {
1031 flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
1033 return flags;
1036 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1037 uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
1038 if (mSyncDecodeImages) {
1039 flags |= imgIContainer::FLAG_SYNC_DECODE;
1040 } else {
1041 flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
1043 if (mIsPaintingToWindow || mUseHighQualityScaling) {
1044 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1046 return flags;
1049 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
1050 const nsRegion& aRegion) {
1051 if (aRegion.IsEmpty()) {
1052 return;
1055 nsRegion tmp;
1056 tmp.Sub(*aVisibleRegion, aRegion);
1057 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1058 // to its bounds either, which can be very bad (see bug 516740).
1059 // Do let aVisibleRegion get more complex if by doing so we reduce its
1060 // area by at least half.
1061 if (tmp.GetNumRects() <= 15 || tmp.Area() <= aVisibleRegion->Area() / 2) {
1062 *aVisibleRegion = tmp;
1066 nsCaret* nsDisplayListBuilder::GetCaret() {
1067 RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
1068 return caret;
1071 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) {
1072 if (mIsPaintingToWindow) {
1073 aPresShell->IncrementPaintCount();
1077 void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
1078 bool aPointerEventsNoneDoc) {
1079 PresShellState* state = mPresShellStates.AppendElement();
1080 state->mPresShell = aReferenceFrame->PresShell();
1081 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
1082 state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
1084 nsIScrollableFrame* sf = state->mPresShell->GetRootScrollFrameAsScrollable();
1085 if (sf && IsInSubdocument()) {
1086 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1087 // that the canvas background color will be set correctly, and that only one
1088 // unscrollable item will be created.
1089 // This is done to avoid, for example, a case where only scrollbar frames
1090 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1091 // and possibly end up with an extra nsDisplaySolidColor item.
1092 // We skip this for the root document, since we don't want to use
1093 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1094 // do it manually there.
1095 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
1096 if (canvasFrame) {
1097 MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
1101 #ifdef DEBUG
1102 state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
1103 nsLayoutPhase::DisplayListBuilding);
1104 #endif
1106 state->mPresShell->UpdateCanvasBackground();
1108 bool buildCaret = mBuildCaret;
1109 if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
1110 state->mIsBackgroundOnly = false;
1111 } else {
1112 state->mIsBackgroundOnly = true;
1113 buildCaret = false;
1116 bool pointerEventsNone = aPointerEventsNoneDoc;
1117 if (IsInSubdocument()) {
1118 pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
1119 .mInsidePointerEventsNoneDoc;
1121 state->mInsidePointerEventsNoneDoc = pointerEventsNone;
1123 state->mPresShellIgnoreScrollFrame =
1124 state->mPresShell->IgnoringViewportScrolling()
1125 ? state->mPresShell->GetRootScrollFrame()
1126 : nullptr;
1128 nsPresContext* pc = aReferenceFrame->PresContext();
1129 mIsInChromePresContext = pc->IsChrome();
1130 nsIDocShell* docShell = pc->GetDocShell();
1132 if (docShell) {
1133 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1136 state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);
1138 if (!buildCaret) {
1139 return;
1142 // Caret frames add visual area to their frame, but we don't update the
1143 // overflow area. Use flags to make sure we build display items for that frame
1144 // instead.
1145 if (mCaretFrame && mCaretFrame->PresShell() == state->mPresShell) {
1146 MarkFrameForDisplay(mCaretFrame, aReferenceFrame);
1150 // A non-blank paint is a paint that does not just contain the canvas
1151 // background.
1152 static bool DisplayListIsNonBlank(nsDisplayList* aList) {
1153 for (nsDisplayItem* i : *aList) {
1154 switch (i->GetType()) {
1155 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
1156 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
1157 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
1158 continue;
1159 case DisplayItemType::TYPE_SOLID_COLOR:
1160 case DisplayItemType::TYPE_BACKGROUND:
1161 case DisplayItemType::TYPE_BACKGROUND_COLOR:
1162 if (i->Frame()->IsCanvasFrame()) {
1163 continue;
1165 return true;
1166 default:
1167 return true;
1170 return false;
1173 // A contentful paint is a paint that does contains DOM content (text,
1174 // images, non-blank canvases, SVG): "First Contentful Paint entry
1175 // contains a DOMHighResTimeStamp reporting the time when the browser
1176 // first rendered any text, image (including background images),
1177 // non-white canvas or SVG. This excludes any content of iframes, but
1178 // includes text with pending webfonts. This is the first time users
1179 // could start consuming page content."
1180 static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
1181 nsDisplayList* aList) {
1182 for (nsDisplayItem* i : *aList) {
1183 DisplayItemType type = i->GetType();
1184 nsDisplayList* children = i->GetChildren();
1186 switch (type) {
1187 case DisplayItemType::TYPE_SUBDOCUMENT: // iframes are ignored
1188 break;
1189 // CANVASes check if they may have been modified (as a stand-in
1190 // actually tracking all modifications)
1191 default:
1192 if (i->IsContentful()) {
1193 bool dummy;
1194 nsRect bound = i->GetBounds(aBuilder, &dummy);
1195 if (!bound.IsEmpty()) {
1196 return true;
1199 if (children) {
1200 if (DisplayListIsContentful(aBuilder, children)) {
1201 return true;
1204 break;
1207 return false;
1210 void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
1211 nsDisplayList* aPaintedContents) {
1212 NS_ASSERTION(
1213 CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
1214 "Presshell mismatch");
1216 if (mIsPaintingToWindow && aPaintedContents) {
1217 nsPresContext* pc = aReferenceFrame->PresContext();
1218 if (!pc->HadNonBlankPaint()) {
1219 if (!CurrentPresShellState()->mIsBackgroundOnly &&
1220 DisplayListIsNonBlank(aPaintedContents)) {
1221 pc->NotifyNonBlankPaint();
1224 nsRootPresContext* rootPresContext = pc->GetRootPresContext();
1225 if (!pc->HasStoppedGeneratingLCP() && rootPresContext) {
1226 if (!CurrentPresShellState()->mIsBackgroundOnly) {
1227 if (pc->HasEverBuiltInvisibleText() ||
1228 DisplayListIsContentful(this, aPaintedContents)) {
1229 pc->NotifyContentfulPaint();
1235 ResetMarkedFramesForDisplayList(aReferenceFrame);
1236 mPresShellStates.RemoveLastElement();
1238 if (!mPresShellStates.IsEmpty()) {
1239 nsPresContext* pc = CurrentPresContext();
1240 nsIDocShell* docShell = pc->GetDocShell();
1241 if (docShell) {
1242 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1244 mIsInChromePresContext = pc->IsChrome();
1245 } else {
1246 for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
1247 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
1249 mFramesMarkedForDisplayIfVisible.SetLength(0);
1253 void nsDisplayListBuilder::FreeClipChains() {
1254 // Iterate the clip chains from newest to oldest (forward
1255 // iteration), so that we destroy descendants first which
1256 // will drop the ref count on their ancestors.
1257 DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;
1259 while (*indirect) {
1260 if (!(*indirect)->mRefCount) {
1261 DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;
1263 mClipDeduplicator.erase(*indirect);
1264 (*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
1265 Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);
1267 *indirect = next;
1268 } else {
1269 indirect = &(*indirect)->mNextClipChainToDestroy;
1274 void nsDisplayListBuilder::FreeTemporaryItems() {
1275 for (nsDisplayItem* i : mTemporaryItems) {
1276 // Temporary display items are not added to the frames.
1277 MOZ_ASSERT(i->Frame());
1278 i->RemoveFrame(i->Frame());
1279 i->Destroy(this);
1282 mTemporaryItems.Clear();
1285 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1286 const nsIFrame* aReferenceFrame) {
1287 // Unmark and pop off the frames marked for display in this pres shell.
1288 uint32_t firstFrameForShell =
1289 CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1290 for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
1291 ++i) {
1292 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
1294 mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1296 firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
1297 for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
1298 mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
1300 mFramesWithOOFData.SetLength(firstFrameForShell);
1303 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1304 CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
1307 void nsDisplayListBuilder::MarkFramesForDisplayList(
1308 nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
1309 nsRect visibleRect = GetVisibleRect();
1310 nsRect dirtyRect = GetDirtyRect();
1312 // If we are entering content that is fixed to the RCD-RSF, we are
1313 // crossing the async zoom container boundary, and need to convert from
1314 // visual to layout coordinates.
1315 if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
1316 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1317 viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
1318 if (viewportFrame->PresShell()->GetRootScrollFrame()) {
1319 #ifdef DEBUG
1320 for (nsIFrame* f : aFrames) {
1321 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
1323 #endif
1324 visibleRect = ViewportUtils::VisualToLayout(visibleRect,
1325 viewportFrame->PresShell());
1326 dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
1327 viewportFrame->PresShell());
1329 #ifdef DEBUG
1330 else {
1331 // This is an edge case that should only happen if we are in a
1332 // document with a XUL root element so that it does not have a root
1333 // scroll frame but it has fixed pos content and all of the frames in
1334 // aFrames are that fixed pos content.
1335 for (nsIFrame* f : aFrames) {
1336 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
1337 f->GetParent() == aDirtyFrame &&
1338 f->StyleDisplay()->mPosition ==
1339 StylePositionProperty::Fixed);
1341 // There's no root scroll frame so there can't be any zooming or async
1342 // panning so we don't need to adjust the visible and dirty rects.
1344 #endif
1348 bool markedFrames = false;
1349 for (nsIFrame* e : aFrames) {
1350 // Skip the AccessibleCaret frame when building no caret.
1351 if (!IsBuildingCaret()) {
1352 nsIContent* content = e->GetContent();
1353 if (content && content->IsInNativeAnonymousSubtree() &&
1354 content->IsElement()) {
1355 const nsAttrValue* classes = content->AsElement()->GetClasses();
1356 if (classes &&
1357 classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) {
1358 continue;
1362 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
1363 markedFrames = true;
1367 if (markedFrames) {
1368 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1369 // to objects on the stack, so we need to clone the chain.
1370 const DisplayItemClipChain* clipChain =
1371 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1372 const DisplayItemClipChain* combinedClipChain =
1373 mClipState.GetCurrentCombinedClipChain(this);
1374 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1376 OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
1377 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1378 visibleRect, dirtyRect);
1379 aDirtyFrame->SetProperty(
1380 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
1381 mFramesWithOOFData.AppendElement(aDirtyFrame);
1384 if (!aDirtyFrame->GetParent()) {
1385 // This is the viewport frame of aDirtyFrame's presshell.
1386 // Store the current display data so that it can be used for fixed
1387 // background images.
1388 NS_ASSERTION(
1389 CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
1390 "Presshell mismatch");
1391 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
1392 "already traversed this presshell's root frame?");
1394 const DisplayItemClipChain* clipChain =
1395 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1396 const DisplayItemClipChain* combinedClipChain =
1397 mClipState.GetCurrentCombinedClipChain(this);
1398 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1399 CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
1400 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1401 GetVisibleRect(), GetDirtyRect());
1406 * Mark all preserve-3d children with
1407 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1408 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1409 * dirty rect for preserve-3d children.
1411 * @param aDirtyFrame is the frame to mark children extending context.
1413 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1414 nsIFrame* aDirtyFrame) {
1415 for (const auto& childList : aDirtyFrame->ChildLists()) {
1416 for (nsIFrame* child : childList.mList) {
1417 if (child->Combines3DTransformWithAncestors()) {
1418 MarkFrameForDisplay(child, aDirtyFrame);
1421 if (child->IsBlockWrapper()) {
1422 // Mark preserve-3d frames inside the block wrapper.
1423 MarkPreserve3DFramesForDisplayList(child);
1429 ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1430 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame) {
1431 RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
1432 aParent, aScrollableFrame, IsRetainingDisplayList());
1433 mActiveScrolledRoots.AppendElement(asr);
1434 return asr;
1437 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1438 const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
1439 const DisplayItemClipChain* aParent) {
1440 MOZ_DIAGNOSTIC_ASSERT(!(aParent && aParent->mOnStack));
1441 void* p = Allocate(sizeof(DisplayItemClipChain),
1442 DisplayListArenaObjectId::CLIPCHAIN);
1443 DisplayItemClipChain* c = new (KnownNotNull, p)
1444 DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy);
1445 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
1446 c->mOnStack = false;
1447 #endif
1448 auto result = mClipDeduplicator.insert(c);
1449 if (!result.second) {
1450 // An equivalent clip chain item was already created, so let's return that
1451 // instead. Destroy the one we just created.
1452 // Note that this can cause clip chains from different coordinate systems to
1453 // collapse into the same clip chain object, because clip chains do not keep
1454 // track of the reference frame that they were created in.
1455 c->DisplayItemClipChain::~DisplayItemClipChain();
1456 Destroy(DisplayListArenaObjectId::CLIPCHAIN, c);
1457 return *(result.first);
1459 mFirstClipChainToDestroy = c;
1460 return c;
1463 struct ClipChainItem {
1464 DisplayItemClip clip;
1465 const ActiveScrolledRoot* asr;
1468 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
1469 const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
1470 for (const ActiveScrolledRoot* asr =
1471 ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
1472 asr; asr = asr->mParent) {
1473 if (aOne == aTwo) {
1474 return aOne;
1476 if (aOne->mASR == asr) {
1477 aOne = aOne->mParent;
1479 if (aTwo->mASR == asr) {
1480 aTwo = aTwo->mParent;
1482 if (!aOne) {
1483 return aTwo;
1485 if (!aTwo) {
1486 return aOne;
1489 return nullptr;
1492 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1493 const DisplayItemClipChain* aAncestor,
1494 const DisplayItemClipChain* aLeafClip1,
1495 const DisplayItemClipChain* aLeafClip2) {
1496 AutoTArray<ClipChainItem, 8> intersectedClips;
1498 const DisplayItemClipChain* clip1 = aLeafClip1;
1499 const DisplayItemClipChain* clip2 = aLeafClip2;
1501 const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
1502 clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);
1504 // Build up the intersection from the leaf to the root and put it into
1505 // intersectedClips. The loop below will convert intersectedClips into an
1506 // actual DisplayItemClipChain.
1507 // (We need to do this in two passes because we need the parent clip in order
1508 // to create the DisplayItemClipChain object, but the parent clip has not
1509 // been created at that point.)
1510 while (!aAncestor || asr != aAncestor->mASR) {
1511 if (clip1 && clip1->mASR == asr) {
1512 if (clip2 && clip2->mASR == asr) {
1513 DisplayItemClip intersection = clip1->mClip;
1514 intersection.IntersectWith(clip2->mClip);
1515 intersectedClips.AppendElement(ClipChainItem{intersection, asr});
1516 clip2 = clip2->mParent;
1517 } else {
1518 intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
1520 clip1 = clip1->mParent;
1521 } else if (clip2 && clip2->mASR == asr) {
1522 intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
1523 clip2 = clip2->mParent;
1525 if (!asr) {
1526 MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
1527 break;
1529 asr = asr->mParent;
1532 // Convert intersectedClips into a DisplayItemClipChain.
1533 const DisplayItemClipChain* parentSC = aAncestor;
1534 for (auto& sc : Reversed(intersectedClips)) {
1535 parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
1537 return parentSC;
1540 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1541 const DisplayItemClipChain* aLeafClip1,
1542 const DisplayItemClipChain* aLeafClip2) {
1543 // aLeafClip2 might be a reference to a clip on the stack. We need to make
1544 // sure that CreateClipChainIntersection will allocate the actual intersected
1545 // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1546 // we supply nullptr as the common ancestor so that
1547 // CreateClipChainIntersection clones the whole chain.
1548 const DisplayItemClipChain* ancestorClip =
1549 aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
1550 : nullptr;
1552 return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2);
1555 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
1556 const DisplayItemClipChain* aClipChain) {
1557 return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
1560 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
1561 const nsIFrame* aFrame, nsPoint* aOffset) const {
1562 auto MaybeApplyAdditionalOffset = [&]() {
1563 if (auto offset = AdditionalOffset()) {
1564 *aOffset += *offset;
1568 if (aFrame == mCurrentFrame) {
1569 if (aOffset) {
1570 *aOffset = mCurrentOffsetToReferenceFrame;
1572 return mCurrentReferenceFrame;
1575 for (const nsIFrame* f = aFrame; f;
1576 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
1577 if (f == mReferenceFrame || f->IsTransformed()) {
1578 if (aOffset) {
1579 *aOffset = aFrame->GetOffsetToCrossDoc(f);
1580 MaybeApplyAdditionalOffset();
1582 return f;
1586 if (aOffset) {
1587 *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1588 MaybeApplyAdditionalOffset();
1591 return mReferenceFrame;
1594 // Sticky frames are active if their nearest scrollable frame is also active.
1595 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
1596 nsIFrame* aFrame) {
1597 MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
1598 StylePositionProperty::Sticky);
1600 StickyScrollContainer* stickyScrollContainer =
1601 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
1602 return stickyScrollContainer &&
1603 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1606 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
1607 nsIFrame** aParent) {
1608 if (aFrame == mReferenceFrame) {
1609 return true;
1612 if (!IsPaintingToWindow()) {
1613 if (aParent) {
1614 *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1616 return false;
1619 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1620 if (!parent) {
1621 return true;
1623 *aParent = parent;
1625 if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
1626 IsStickyFrameActive(this, aFrame)) {
1627 return true;
1630 if (aFrame->IsTransformed()) {
1631 if (EffectCompositor::HasAnimationsForCompositor(
1632 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
1633 return true;
1637 LayoutFrameType parentType = parent->Type();
1638 if (parentType == LayoutFrameType::Scroll ||
1639 parentType == LayoutFrameType::ListControl) {
1640 nsIScrollableFrame* sf = do_QueryFrame(parent);
1641 if (sf->GetScrolledFrame() == aFrame) {
1642 MOZ_ASSERT(!aFrame->IsTransformed());
1643 return sf->IsMaybeAsynchronouslyScrolled();
1647 return false;
1650 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1651 nsIFrame* aFrame) {
1652 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1653 RootReferenceFrame(), aFrame));
1654 nsIFrame* cursor = aFrame;
1655 while (cursor != RootReferenceFrame()) {
1656 nsIFrame* next;
1657 if (IsAnimatedGeometryRoot(cursor, &next)) {
1658 return cursor;
1660 cursor = next;
1662 return cursor;
1665 static nsRect ApplyAllClipNonRoundedIntersection(
1666 const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
1667 nsRect result = aRect;
1668 while (aClipChain) {
1669 result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
1670 aClipChain = aClipChain->mParent;
1672 return result;
1675 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
1676 if (!mWindowDraggingAllowed || !IsForPainting()) {
1677 return;
1680 const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
1681 if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
1682 // This frame has the default value and doesn't influence the window
1683 // dragging region.
1684 return;
1687 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;
1689 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1690 nsIFrame* referenceFrame =
1691 const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1693 if (IsInTransform()) {
1694 // Only support 2d rectilinear transforms. Transform support is needed for
1695 // the horizontal flip transform that's applied to the urlbar textbox in
1696 // RTL mode - it should be able to exclude itself from the draggable region.
1697 referenceFrameToRootReferenceFrame =
1698 ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
1699 nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
1700 RelativeTo{mReferenceFrame})
1701 .GetMatrix());
1702 Matrix referenceFrameToRootReferenceFrame2d;
1703 if (!referenceFrameToRootReferenceFrame.Is2D(
1704 &referenceFrameToRootReferenceFrame2d) ||
1705 !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1706 return;
1708 } else {
1709 MOZ_ASSERT(referenceFrame == mReferenceFrame,
1710 "referenceFrameToRootReferenceFrame needs to be adjusted");
1713 // We do some basic visibility checking on the frame's border box here.
1714 // We intersect it both with the current dirty rect and with the current
1715 // clip. Either one is just a conservative approximation on its own, but
1716 // their intersection luckily works well enough for our purposes, so that
1717 // we don't have to do full-blown visibility computations.
1718 // The most important case we need to handle is the scrolled-off tab:
1719 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1720 // should not be allowed to interfere with the window dragging region. Using
1721 // just the current DisplayItemClip is not enough to cover this case
1722 // completely because clips are reset while building stacking context
1723 // contents, so for example we'd fail to clip frames that have a clip path
1724 // applied to them. But the current dirty rect doesn't get reset in that
1725 // case, so we use it to make this case work.
1726 nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
1727 borderBox += ToReferenceFrame(aFrame);
1728 const DisplayItemClipChain* clip =
1729 ClipState().GetCurrentCombinedClipChain(this);
1730 borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
1731 if (borderBox.IsEmpty()) {
1732 return;
1735 LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
1736 borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1738 LayoutDeviceRect transformedDevPixelBorderBox =
1739 TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1740 transformedDevPixelBorderBox.Round();
1741 LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1743 if (!transformedDevPixelBorderBox.ToIntRect(
1744 &transformedDevPixelBorderBoxInt)) {
1745 return;
1748 LayoutDeviceIntRegion& region =
1749 styleUI->mWindowDragging == StyleWindowDragging::Drag
1750 ? mWindowDraggingRegion
1751 : mWindowNoDraggingRegion;
1753 if (!IsRetainingDisplayList()) {
1754 region.OrWith(transformedDevPixelBorderBoxInt);
1755 return;
1758 gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
1759 if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
1760 mRetainedWindowDraggingRegion.Add(aFrame, rect);
1761 } else {
1762 mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
1766 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
1767 LayoutDeviceIntRegion result;
1768 if (!IsRetainingDisplayList()) {
1769 result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
1770 return result;
1773 LayoutDeviceIntRegion dragRegion =
1774 mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();
1776 LayoutDeviceIntRegion noDragRegion =
1777 mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();
1779 result.Sub(dragRegion, noDragRegion);
1780 return result;
1783 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1784 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes);
1785 aSizes.mLayoutRetainedDisplayListSize +=
1786 aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get());
1789 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1790 mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);
1792 size_t n = 0;
1793 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
1794 n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1795 n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1796 n += mEffectsUpdates.ShallowSizeOfExcludingThis(mallocSizeOf);
1797 n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1798 n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1799 n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
1800 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1802 aSizes.mLayoutRetainedDisplayListSize += n;
1805 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1806 for (nsDisplayItem* item : *this) {
1807 item->AddSizeOfExcludingThis(aSizes);
1808 if (RetainedDisplayList* children = item->GetChildren()) {
1809 children->AddSizeOfExcludingThis(aSizes);
1813 size_t n = 0;
1815 n += mDAG.mDirectPredecessorList.ShallowSizeOfExcludingThis(
1816 aSizes.mState.mMallocSizeOf);
1817 n += mDAG.mNodesInfo.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1818 n += mOldItems.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1820 aSizes.mLayoutRetainedDisplayListSize += n;
1823 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1824 MallocSizeOf aMallocSizeOf) const {
1825 size_t n = 0;
1826 n += mFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
1827 for (const auto& frame : mFrames) {
1828 const UniquePtr<WeakFrame>& weakFrame = frame.mWeakFrame;
1829 n += aMallocSizeOf(weakFrame.get());
1831 n += mRects.ShallowSizeOfExcludingThis(aMallocSizeOf);
1832 return n;
1836 * Removes modified frames and rects from this WeakFrameRegion.
1838 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1839 MOZ_ASSERT(mFrames.Length() == mRects.Length());
1841 uint32_t i = 0;
1842 uint32_t length = mFrames.Length();
1844 while (i < length) {
1845 auto& wrapper = mFrames[i];
1847 if (!wrapper.mWeakFrame->IsAlive() ||
1848 AnyContentAncestorModified(wrapper.mWeakFrame->GetFrame())) {
1849 // To avoid multiple O(n) shifts in the array, move the last element of
1850 // the array to the current position and decrease the array length.
1851 mFrameSet.Remove(wrapper.mFrame);
1852 mFrames[i] = std::move(mFrames[length - 1]);
1853 mRects[i] = std::move(mRects[length - 1]);
1854 length--;
1855 } else {
1856 i++;
1860 mFrames.TruncateLength(length);
1861 mRects.TruncateLength(length);
1864 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1865 mRetainedWindowDraggingRegion.RemoveModifiedFramesAndRects();
1866 mRetainedWindowNoDraggingRegion.RemoveModifiedFramesAndRects();
1867 mRetainedWindowOpaqueRegion.RemoveModifiedFramesAndRects();
1870 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1871 mRetainedWindowDraggingRegion.Clear();
1872 mRetainedWindowNoDraggingRegion.Clear();
1873 mRetainedWindowOpaqueRegion.Clear();
1876 const uint32_t gWillChangeAreaMultiplier = 3;
1877 static uint32_t GetLayerizationCost(const nsSize& aSize) {
1878 // There's significant overhead for each layer created from Gecko
1879 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1880 // Therefore we set a minimum cost threshold of a 64x64 area.
1881 const int minBudgetCost = 64 * 64;
1883 const uint32_t budgetCost = std::max(
1884 minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
1885 nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
1887 return budgetCost;
1890 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
1891 const nsSize& aSize) {
1892 MOZ_ASSERT(IsForPainting());
1894 if (aFrame->MayHaveWillChangeBudget()) {
1895 // The frame is already in the will-change budget.
1896 return true;
1899 const nsPresContext* presContext = aFrame->PresContext();
1900 const nsRect area = presContext->GetVisibleArea();
1901 const uint32_t budgetLimit =
1902 nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1903 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1904 const uint32_t cost = GetLayerizationCost(aSize);
1906 DocumentWillChangeBudget& documentBudget =
1907 mDocumentWillChangeBudgets.LookupOrInsert(presContext);
1909 const bool onBudget =
1910 (documentBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
1912 if (onBudget) {
1913 documentBudget += cost;
1914 mFrameWillChangeBudgets.InsertOrUpdate(
1915 aFrame, FrameWillChangeBudget(presContext, cost));
1916 aFrame->SetMayHaveWillChangeBudget(true);
1919 return onBudget;
1922 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
1923 const nsSize& aSize) {
1924 if (!IsForPainting()) {
1925 // If this nsDisplayListBuilder is not for painting, the layerization should
1926 // not matter. Do the simple thing and return false.
1927 return false;
1930 const bool onBudget = AddToWillChangeBudget(aFrame, aSize);
1931 if (onBudget) {
1932 return true;
1935 auto* pc = aFrame->PresContext();
1936 auto* doc = pc->Document();
1937 if (!doc->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget)) {
1938 AutoTArray<nsString, 2> params;
1939 params.AppendElement()->AppendInt(gWillChangeAreaMultiplier);
1941 nsRect area = pc->GetVisibleArea();
1942 uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1943 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1944 params.AppendElement()->AppendInt(budgetLimit);
1946 doc->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget, false, params);
1949 return false;
1952 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame* aFrame) {
1953 MOZ_ASSERT(IsForPainting());
1955 if (!aFrame->MayHaveWillChangeBudget()) {
1956 return;
1959 aFrame->SetMayHaveWillChangeBudget(false);
1960 RemoveFromWillChangeBudgets(aFrame);
1963 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame* aFrame) {
1964 if (auto entry = mFrameWillChangeBudgets.Lookup(aFrame)) {
1965 const FrameWillChangeBudget& frameBudget = entry.Data();
1967 auto documentBudget =
1968 mDocumentWillChangeBudgets.Lookup(frameBudget.mPresContext);
1970 if (documentBudget) {
1971 *documentBudget -= frameBudget.mUsage;
1974 entry.Remove();
1978 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1979 mFrameWillChangeBudgets.Clear();
1980 mDocumentWillChangeBudgets.Clear();
1983 void nsDisplayListBuilder::EnterSVGEffectsContents(
1984 nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) {
1985 MOZ_ASSERT(aHoistedItemsStorage);
1986 if (mSVGEffectsFrames.IsEmpty()) {
1987 MOZ_ASSERT(!mScrollInfoItemsForHoisting);
1988 mScrollInfoItemsForHoisting = aHoistedItemsStorage;
1990 mSVGEffectsFrames.AppendElement(aEffectsFrame);
1993 void nsDisplayListBuilder::ExitSVGEffectsContents() {
1994 MOZ_ASSERT(!mSVGEffectsFrames.IsEmpty());
1995 mSVGEffectsFrames.RemoveLastElement();
1996 MOZ_ASSERT(mScrollInfoItemsForHoisting);
1997 if (mSVGEffectsFrames.IsEmpty()) {
1998 mScrollInfoItemsForHoisting = nullptr;
2002 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
2004 * Note: if changing the conditions under which scroll info layers
2005 * are created, make a corresponding change to
2006 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
2008 for (nsIFrame* frame : mSVGEffectsFrames) {
2009 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame)) {
2010 return true;
2013 return false;
2016 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
2017 nsDisplayScrollInfoLayer* aScrollInfoItem) {
2018 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2019 MOZ_ASSERT(mScrollInfoItemsForHoisting);
2020 mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
2023 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2024 nsIFrame* aFrame, nsDisplayList* aList) {
2025 MOZ_ASSERT(aFrame);
2026 MOZ_ASSERT(aList);
2028 if (!BuildCompositorHitTestInfo()) {
2029 return;
2032 const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
2033 if (info != CompositorHitTestInvisibleToHit) {
2034 aList->AppendNewToTop<nsDisplayCompositorHitTestInfo>(this, aFrame);
2038 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem* aItem) {
2039 mReuseableItems.Insert(aItem);
2042 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem* aItem) {
2043 MOZ_ASSERT(aItem->IsReusedItem());
2044 mReuseableItems.Remove(aItem);
2047 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2048 const size_t total = mReuseableItems.Count();
2050 size_t reused = 0;
2051 for (auto* item : mReuseableItems) {
2052 if (item->IsReusedItem()) {
2053 reused++;
2054 item->SetReusable();
2055 } else {
2056 item->Destroy(this);
2060 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
2061 mReuseableItems.Clear();
2064 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
2065 const auto* previous = mCurrentContainerASR;
2066 const auto* asr = aItem->GetActiveScrolledRoot();
2067 mCurrentContainerASR =
2068 ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
2070 if (previous != mCurrentContainerASR) {
2071 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
2072 mCurrentContainerASR);
2075 aItem->SetReusedItem();
2078 void nsDisplayListSet::CopyTo(const nsDisplayListSet& aDestination) const {
2079 for (size_t i = 0; i < mLists.size(); ++i) {
2080 auto* from = mLists[i];
2081 auto* to = aDestination.mLists[i];
2083 from->CopyTo(to);
2087 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
2088 aDestination.BorderBackground()->AppendToTop(BorderBackground());
2089 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2090 aDestination.Floats()->AppendToTop(Floats());
2091 aDestination.Content()->AppendToTop(Content());
2092 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
2093 aDestination.Outlines()->AppendToTop(Outlines());
2096 nsRect nsDisplayList::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2097 nsRect bounds;
2098 for (nsDisplayItem* i : *this) {
2099 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
2101 return bounds;
2104 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR(
2105 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
2106 nsRect* aBuildingRect) const {
2107 nsRect bounds;
2108 for (nsDisplayItem* i : *this) {
2109 nsRect r = i->GetClippedBounds(aBuilder);
2110 if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
2111 if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) {
2112 r = clip.ref();
2115 if (aBuildingRect) {
2116 aBuildingRect->UnionRect(*aBuildingRect, i->GetBuildingRect());
2118 bounds.UnionRect(bounds, r);
2120 return bounds;
2123 nsRect nsDisplayList::GetBuildingRect() const {
2124 nsRect result;
2125 for (nsDisplayItem* i : *this) {
2126 result.UnionRect(result, i->GetBuildingRect());
2128 return result;
2131 WindowRenderer* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView** aView) {
2132 if (aView) {
2133 *aView = RootReferenceFrame()->GetView();
2135 if (RootReferenceFrame() !=
2136 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2137 return nullptr;
2139 nsIWidget* window = RootReferenceFrame()->GetNearestWidget();
2140 if (window) {
2141 return window->GetWindowRenderer();
2143 return nullptr;
2146 WebRenderLayerManager* nsDisplayListBuilder::GetWidgetLayerManager(
2147 nsView** aView) {
2148 WindowRenderer* renderer = GetWidgetWindowRenderer();
2149 if (renderer) {
2150 return renderer->AsWebRender();
2152 return nullptr;
2155 void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2156 int32_t aAppUnitsPerDevPixel) {
2157 FlattenedDisplayListIterator iter(aBuilder, this);
2158 while (iter.HasNext()) {
2159 nsPaintedDisplayItem* item = iter.GetNextItem()->AsPaintedDisplayItem();
2160 if (!item) {
2161 continue;
2164 nsRect visible = item->GetClippedBounds(aBuilder);
2165 visible = visible.Intersect(item->GetPaintRect(aBuilder, aCtx));
2166 if (visible.IsEmpty()) {
2167 continue;
2170 DisplayItemClip currentClip = item->GetClip();
2171 if (currentClip.HasClip()) {
2172 aCtx->Save();
2173 if (currentClip.IsRectClippedByRoundedCorner(visible)) {
2174 currentClip.ApplyTo(aCtx, aAppUnitsPerDevPixel);
2175 } else {
2176 currentClip.ApplyRectTo(aCtx, aAppUnitsPerDevPixel);
2179 aCtx->NewPath();
2181 item->Paint(aBuilder, aCtx);
2183 if (currentClip.HasClip()) {
2184 aCtx->Restore();
2190 * We paint by executing a layer manager transaction, constructing a
2191 * single layer representing the display list, and then making it the
2192 * root of the layer manager, drawing into the PaintedLayers.
2194 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2195 uint32_t aFlags,
2196 Maybe<double> aDisplayListBuildTime) {
2197 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS);
2199 RefPtr<WebRenderLayerManager> layerManager;
2200 WindowRenderer* renderer = nullptr;
2201 bool widgetTransaction = false;
2202 bool doBeginTransaction = true;
2203 nsView* view = nullptr;
2204 if (aFlags & PAINT_USE_WIDGET_LAYERS) {
2205 renderer = aBuilder->GetWidgetWindowRenderer(&view);
2206 if (renderer) {
2207 // The fallback renderer doesn't retain any content, so it's
2208 // not meaningful to use it when drawing to an external context.
2209 if (aCtx && renderer->AsFallback()) {
2210 MOZ_ASSERT(!(aFlags & PAINT_EXISTING_TRANSACTION));
2211 renderer = nullptr;
2212 } else {
2213 layerManager = renderer->AsWebRender();
2214 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
2215 widgetTransaction = true;
2220 nsIFrame* frame = aBuilder->RootReferenceFrame();
2221 nsPresContext* presContext = frame->PresContext();
2222 PresShell* presShell = presContext->PresShell();
2223 Document* document = presShell->GetDocument();
2225 ScopeExit g([&]() {
2226 #ifdef DEBUG
2227 MOZ_ASSERT(!layerManager || !layerManager->GetTarget());
2228 #endif
2230 // For layers-free mode, we check the invalidation state bits in the
2231 // EndTransaction. So we clear the invalidation state bits after
2232 // EndTransaction.
2233 if (widgetTransaction ||
2234 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2235 // but they still need the invalidation state bits cleared in order for
2236 // invalidation for CSS/SMIL animation to work properly.
2237 (document && document->IsBeingUsedAsImage())) {
2238 DL_LOGD("Clearing invalidation state bits");
2239 frame->ClearInvalidationStateBits();
2243 if (!renderer) {
2244 if (!aCtx) {
2245 NS_WARNING("Nowhere to paint into");
2246 return;
2248 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(false);
2249 Paint(aBuilder, aCtx, presContext->AppUnitsPerDevPixel());
2251 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2252 return;
2255 if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) {
2256 MOZ_ASSERT(layerManager);
2257 if (doBeginTransaction) {
2258 if (aCtx) {
2259 if (!layerManager->BeginTransactionWithTarget(aCtx, nsCString())) {
2260 return;
2262 } else {
2263 if (!layerManager->BeginTransaction(nsCString())) {
2264 return;
2269 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(true);
2270 layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
2272 bool sent = false;
2273 if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
2274 MOZ_ASSERT(!aCtx);
2275 sent = layerManager->EndEmptyTransaction();
2278 if (!sent) {
2279 auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
2281 nsIDocShell* docShell = presContext->GetDocShell();
2282 WrFiltersHolder wrFilters;
2283 gfx::Matrix5x4* colorMatrix =
2284 nsDocShell::Cast(docShell)->GetColorMatrix();
2285 if (colorMatrix) {
2286 wrFilters.filters.AppendElement(
2287 wr::FilterOp::ColorMatrix(colorMatrix->components));
2290 wrManager->EndTransactionWithoutLayer(this, aBuilder,
2291 std::move(wrFilters), nullptr,
2292 aDisplayListBuildTime.valueOr(0.0));
2295 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2296 if (presContext->RefreshDriver()->HasScheduleFlush()) {
2297 presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2298 frame->GetRect());
2301 return;
2304 FallbackRenderer* fallback = renderer->AsFallback();
2305 MOZ_ASSERT(fallback);
2307 if (doBeginTransaction) {
2308 MOZ_ASSERT(!aCtx);
2309 if (!fallback->BeginTransaction()) {
2310 return;
2314 bool temp = aBuilder->SetIsCompositingCheap(renderer->IsCompositingCheap());
2315 fallback->EndTransactionWithList(aBuilder, this,
2316 presContext->AppUnitsPerDevPixel(),
2317 WindowRenderer::END_DEFAULT);
2319 aBuilder->SetIsCompositingCheap(temp);
2322 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
2323 for (auto* item : TakeItems()) {
2324 item->Destroy(aBuilder);
2328 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) {
2329 return aFrame->Style()->PointerEvents() != StylePointerEvents::None;
2332 // A list of frames, and their z depth. Used for sorting
2333 // the results of hit testing.
2334 struct FramesWithDepth {
2335 explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {}
2337 bool operator<(const FramesWithDepth& aOther) const {
2338 if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
2339 // We want to sort so that the shallowest item (highest depth value) is
2340 // first
2341 return mDepth > aOther.mDepth;
2343 return false;
2345 bool operator==(const FramesWithDepth& aOther) const {
2346 return this == &aOther;
2349 float mDepth;
2350 nsTArray<nsIFrame*> mFrames;
2353 // Sort the frames by depth and then moves all the contained frames to the
2354 // destination
2355 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource,
2356 nsTArray<nsIFrame*>* aDest) {
2357 if (aSource.IsEmpty()) {
2358 return;
2360 aSource.StableSort();
2361 uint32_t length = aSource.Length();
2362 for (uint32_t i = 0; i < length; i++) {
2363 aDest->AppendElements(std::move(aSource[i].mFrames));
2365 aSource.Clear();
2368 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2369 nsDisplayItem::HitTestState* aState,
2370 nsTArray<nsIFrame*>* aOutFrames) const {
2371 nsDisplayItem* item;
2373 if (aState->mInPreserves3D) {
2374 // Collect leaves of the current 3D rendering context.
2375 for (nsDisplayItem* item : *this) {
2376 auto itemType = item->GetType();
2377 if (itemType != DisplayItemType::TYPE_TRANSFORM ||
2378 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2379 item->HitTest(aBuilder, aRect, aState, aOutFrames);
2380 } else {
2381 // One of leaves in the current 3D rendering context.
2382 aState->mItemBuffer.AppendElement(item);
2385 return;
2388 int32_t itemBufferStart = aState->mItemBuffer.Length();
2389 for (nsDisplayItem* item : *this) {
2390 aState->mItemBuffer.AppendElement(item);
2393 AutoTArray<FramesWithDepth, 16> temp;
2394 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart;
2395 --i) {
2396 // Pop element off the end of the buffer. We want to shorten the buffer
2397 // so that recursive calls to HitTest have more buffer space.
2398 item = aState->mItemBuffer[i];
2399 aState->mItemBuffer.SetLength(i);
2401 bool snap;
2402 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
2403 auto itemType = item->GetType();
2404 bool same3DContext =
2405 (itemType == DisplayItemType::TYPE_TRANSFORM &&
2406 static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
2407 (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
2408 item->Frame()->Extend3DContext());
2409 if (same3DContext &&
2410 (itemType != DisplayItemType::TYPE_TRANSFORM ||
2411 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
2412 if (!item->GetClip().MayIntersect(aRect)) {
2413 continue;
2415 AutoTArray<nsIFrame*, 1> neverUsed;
2416 // Start gathering leaves of the 3D rendering context, and
2417 // append leaves at the end of mItemBuffer. Leaves are
2418 // processed at following iterations.
2419 aState->mInPreserves3D = true;
2420 item->HitTest(aBuilder, aRect, aState, &neverUsed);
2421 aState->mInPreserves3D = false;
2422 i = aState->mItemBuffer.Length();
2423 continue;
2425 if (same3DContext || item->GetClip().MayIntersect(r)) {
2426 AutoTArray<nsIFrame*, 16> outFrames;
2427 item->HitTest(aBuilder, aRect, aState, &outFrames);
2429 // For 3d transforms with preserve-3d we add hit frames into the temp list
2430 // so we can sort them later, otherwise we add them directly to the output
2431 // list.
2432 nsTArray<nsIFrame*>* writeFrames = aOutFrames;
2433 if (item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
2434 static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2435 if (outFrames.Length()) {
2436 nsDisplayTransform* transform =
2437 static_cast<nsDisplayTransform*>(item);
2438 nsPoint point = aRect.TopLeft();
2439 // A 1x1 rect means a point, otherwise use the center of the rect
2440 if (aRect.width != 1 || aRect.height != 1) {
2441 point = aRect.Center();
2443 temp.AppendElement(
2444 FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
2445 writeFrames = &temp[temp.Length() - 1].mFrames;
2447 } else {
2448 // We may have just finished a run of consecutive preserve-3d
2449 // transforms, so flush these into the destination array before
2450 // processing our frame list.
2451 FlushFramesArray(temp, aOutFrames);
2454 for (uint32_t j = 0; j < outFrames.Length(); j++) {
2455 nsIFrame* f = outFrames.ElementAt(j);
2456 // Filter out some frames depending on the type of hittest
2457 // we are doing. For visibility tests, pass through all frames.
2458 // For pointer tests, only pass through frames that are styled
2459 // to receive pointer events.
2460 if (aBuilder->HitTestIsForVisibility() ||
2461 IsFrameReceivingPointerEvents(f)) {
2462 writeFrames->AppendElement(f);
2466 if (aBuilder->HitTestIsForVisibility()) {
2467 aState->mHitOccludingItem = [&] {
2468 if (aState->mHitOccludingItem) {
2469 // We already hit something before.
2470 return true;
2472 if (aState->mCurrentOpacity == 1.0f &&
2473 item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) {
2474 // An opaque item always occludes everything. Note that we need to
2475 // check wrapping opacity and such as well.
2476 return true;
2478 float threshold = aBuilder->VisibilityThreshold();
2479 if (threshold == 1.0f) {
2480 return false;
2482 float itemOpacity = [&] {
2483 switch (item->GetType()) {
2484 case DisplayItemType::TYPE_OPACITY:
2485 return static_cast<nsDisplayOpacity*>(item)->GetOpacity();
2486 case DisplayItemType::TYPE_BACKGROUND_COLOR:
2487 return static_cast<nsDisplayBackgroundColor*>(item)
2488 ->GetOpacity();
2489 default:
2490 // Be conservative and assume it won't occlude other items.
2491 return 0.0f;
2493 }();
2494 return itemOpacity * aState->mCurrentOpacity >= threshold;
2495 }();
2497 if (aState->mHitOccludingItem) {
2498 // We're exiting early, so pop the remaining items off the buffer.
2499 aState->mItemBuffer.TruncateLength(itemBufferStart);
2500 break;
2505 // Clear any remaining preserve-3d transforms.
2506 FlushFramesArray(temp, aOutFrames);
2507 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
2508 "How did we forget to pop some elements?");
2511 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, Document* aDoc) {
2512 nsIFrame* f = aItem->Frame();
2513 while (f) {
2514 nsPresContext* pc = f->PresContext();
2515 if (pc->Document() == aDoc) {
2516 return f->GetContent();
2518 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(
2519 pc->PresShell()->GetRootFrame());
2521 return nullptr;
2524 struct ZSortItem {
2525 nsDisplayItem* item;
2526 int32_t zIndex;
2528 explicit ZSortItem(nsDisplayItem* aItem)
2529 : item(aItem), zIndex(aItem->ZIndex()) {}
2531 operator nsDisplayItem*() { return item; }
2534 struct ZOrderComparator {
2535 bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const {
2536 // Note that we can't just take the difference of the two
2537 // z-indices here, because that might overflow a 32-bit int.
2538 return aLeft.zIndex < aRight.zIndex;
2542 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); }
2544 struct ContentComparator {
2545 nsIContent* mCommonAncestor;
2547 explicit ContentComparator(nsIContent* aCommonAncestor)
2548 : mCommonAncestor(aCommonAncestor) {}
2550 bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const {
2551 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2552 // subdocument of commonAncestor, because display items for subdocuments
2553 // have been mixed into the same list. Ensure that we're looking at content
2554 // in commonAncestor's document.
2555 Document* commonAncestorDoc = mCommonAncestor->OwnerDoc();
2556 nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc);
2557 nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc);
2558 if (!content1 || !content2) {
2559 NS_ERROR("Document trees are mixed up!");
2560 // Something weird going on
2561 return true;
2563 return nsLayoutUtils::CompareTreePosition(content1, content2,
2564 mCommonAncestor) < 0;
2568 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) {
2569 Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor));
2572 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2573 static_assert(sizeof(nsDisplayItem) <= 176, "nsDisplayItem has grown");
2574 #endif
2576 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2577 : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
2579 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2580 const ActiveScrolledRoot* aActiveScrolledRoot)
2581 : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) {
2582 MOZ_COUNT_CTOR(nsDisplayItem);
2583 MOZ_ASSERT(mFrame);
2584 if (aBuilder->IsRetainingDisplayList()) {
2585 mFrame->AddDisplayItem(this);
2588 aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
2589 NS_ASSERTION(
2590 aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
2591 "visible rect not set");
2593 mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2595 // The visible rect is for mCurrentFrame, so we have to use
2596 // mCurrentOffsetToReferenceFrame
2597 nsRect visible = aBuilder->GetVisibleRect() +
2598 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
2599 SetBuildingRect(visible);
2601 const nsStyleDisplay* disp = mFrame->StyleDisplay();
2602 if (mFrame->BackfaceIsHidden(disp)) {
2603 mItemFlags += ItemFlag::BackfaceHidden;
2605 if (mFrame->Combines3DTransformWithAncestors()) {
2606 mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
2610 void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
2612 bool nsDisplayItem::HasDeletedFrame() const {
2613 bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) ||
2614 (GetType() == DisplayItemType::TYPE_REMOTE &&
2615 !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader());
2616 MOZ_ASSERT(retval || mFrame);
2617 return retval;
2620 /* static */
2621 bool nsDisplayItem::ForceActiveLayers() {
2622 return StaticPrefs::layers_force_active();
2625 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); }
2627 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
2628 bool aStore) {
2629 mClipChain = aClipChain;
2632 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
2633 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2634 if (const DisplayItemClip* clip =
2635 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
2636 return Some(clip->GetClipRect());
2638 #ifdef DEBUG
2639 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
2640 #endif
2641 return Nothing();
2644 const DisplayItemClip& nsDisplayItem::GetClip() const {
2645 const DisplayItemClip* clip =
2646 DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot);
2647 return clip ? *clip : DisplayItemClip::NoClip();
2650 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
2651 const DisplayItemClipChain* aOther,
2652 bool aStore) {
2653 if (!aOther || mClipChain == aOther) {
2654 return;
2657 // aOther might be a reference to a clip on the stack. We need to make sure
2658 // that CreateClipChainIntersection will allocate the actual intersected
2659 // clip in the builder's arena, so for the mClipChain == nullptr case,
2660 // we supply nullptr as the common ancestor so that
2661 // CreateClipChainIntersection clones the whole chain.
2662 const DisplayItemClipChain* ancestorClip =
2663 mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther)
2664 : nullptr;
2666 SetClipChain(
2667 aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther),
2668 aStore);
2671 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2672 bool snap;
2673 nsRect r = GetBounds(aBuilder, &snap);
2674 return GetClip().ApplyNonRoundedIntersection(r);
2677 nsDisplayContainer::nsDisplayContainer(
2678 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2679 const ActiveScrolledRoot* aActiveScrolledRoot, nsDisplayList* aList)
2680 : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
2681 mChildren(aBuilder) {
2682 MOZ_COUNT_CTOR(nsDisplayContainer);
2683 mChildren.AppendToTop(aList);
2684 UpdateBounds(aBuilder);
2686 // Clear and store the clip chain set by nsDisplayItem constructor.
2687 nsDisplayItem::SetClipChain(nullptr, true);
2690 nsRect nsDisplayItem::GetPaintRect(nsDisplayListBuilder* aBuilder,
2691 gfxContext* aCtx) {
2692 bool dummy;
2693 nsRect result = GetBounds(aBuilder, &dummy);
2694 if (aCtx) {
2695 result.IntersectRect(result,
2696 nsLayoutUtils::RoundGfxRectToAppRect(
2697 aCtx->GetClipExtents(),
2698 mFrame->PresContext()->AppUnitsPerDevPixel()));
2700 return result;
2703 bool nsDisplayContainer::CreateWebRenderCommands(
2704 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2705 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2706 nsDisplayListBuilder* aDisplayListBuilder) {
2707 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2708 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
2709 false);
2710 return true;
2713 nsRect nsDisplayContainer::GetBounds(nsDisplayListBuilder* aBuilder,
2714 bool* aSnap) const {
2715 *aSnap = false;
2716 return mBounds;
2719 nsRect nsDisplayContainer::GetComponentAlphaBounds(
2720 nsDisplayListBuilder* aBuilder) const {
2721 return mChildren.GetComponentAlphaBounds(aBuilder);
2724 static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2725 nsDisplayList* aList,
2726 const nsRect& aListBounds) {
2727 return aList->GetOpaqueRegion(aBuilder);
2730 nsRegion nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2731 bool* aSnap) const {
2732 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
2733 GetBounds(aBuilder, aSnap));
2736 Maybe<nsRect> nsDisplayContainer::GetClipWithRespectToASR(
2737 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2738 // Our children should have finite bounds with respect to |aASR|.
2739 if (aASR == mActiveScrolledRoot) {
2740 return Some(mBounds);
2743 return Some(mChildren.GetClippedBoundsWithRespectToASR(aBuilder, aASR));
2746 void nsDisplayContainer::HitTest(nsDisplayListBuilder* aBuilder,
2747 const nsRect& aRect, HitTestState* aState,
2748 nsTArray<nsIFrame*>* aOutFrames) {
2749 mChildren.HitTest(aBuilder, aRect, aState, aOutFrames);
2752 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder* aBuilder) {
2753 // Container item bounds are expected to be clipped.
2754 mBounds =
2755 mChildren.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
2758 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder,
2759 bool* aSnap) const {
2760 *aSnap = true;
2761 return mBounds;
2764 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
2765 gfxContext* aCtx) {
2766 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2767 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2768 Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx),
2769 appUnitsPerDevPixel, *drawTarget);
2770 drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
2773 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) {
2774 aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
2775 << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
2776 << ")";
2779 bool nsDisplaySolidColor::CreateWebRenderCommands(
2780 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2781 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2782 nsDisplayListBuilder* aDisplayListBuilder) {
2783 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
2784 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
2785 wr::LayoutRect r = wr::ToLayoutRect(bounds);
2786 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, mIsCheckerboardBackground,
2787 wr::ToColorF(ToDeviceColor(mColor)));
2789 return true;
2792 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
2793 bool* aSnap) const {
2794 *aSnap = true;
2795 return mRegion.GetBounds();
2798 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
2799 gfxContext* aCtx) {
2800 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2801 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2802 ColorPattern color(ToDeviceColor(mColor));
2803 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2804 Rect rect =
2805 NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
2806 drawTarget->FillRect(rect, color);
2810 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) {
2811 aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255)
2812 << "," << int(mColor.b * 255) << "," << mColor.a << ")";
2815 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2816 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2817 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2818 nsDisplayListBuilder* aDisplayListBuilder) {
2819 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2820 nsRect rect = iter.Get();
2821 LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
2822 rect, mFrame->PresContext()->AppUnitsPerDevPixel());
2823 wr::LayoutRect r = wr::ToLayoutRect(layerRects);
2824 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
2825 wr::ToColorF(ToDeviceColor(mColor)));
2828 return true;
2831 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder,
2832 nsDisplayItem* aItem, nsIFrame* aFrame,
2833 nsITheme::ThemeGeometryType aType) {
2834 if (aBuilder->IsInChromeDocumentOrPopup()) {
2835 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2836 bool preservesAxisAlignedRectangles = false;
2837 nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(
2838 aFrame, aFrame->GetRectRelativeToSelf(), displayRoot,
2839 &preservesAxisAlignedRectangles);
2840 if (preservesAxisAlignedRectangles) {
2841 aBuilder->RegisterThemeGeometry(
2842 aType, aItem,
2843 LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels(
2844 aFrame->PresContext()->AppUnitsPerDevPixel())));
2849 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2850 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
2851 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
2852 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
2853 nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
2854 nsRect rootRect = rootFrame->GetRectRelativeToSelf();
2855 if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) ==
2856 nsLayoutUtils::TRANSFORM_SUCCEEDED) {
2857 return Some(rootRect + aBuilder->ToReferenceFrame(aFrame));
2859 return Nothing();
2862 /* static */ nsDisplayBackgroundImage::InitData
2863 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder,
2864 nsIFrame* aFrame, uint16_t aLayer,
2865 const nsRect& aBackgroundRect,
2866 const ComputedStyle* aBackgroundStyle) {
2867 nsPresContext* presContext = aFrame->PresContext();
2868 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2869 const nsStyleImageLayers::Layer& layer =
2870 aBackgroundStyle->StyleBackground()->mImage.mLayers[aLayer];
2872 bool isTransformedFixed;
2873 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
2874 presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer,
2875 &isTransformedFixed);
2877 // background-attachment:fixed is treated as background-attachment:scroll
2878 // if it's affected by a transform.
2879 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2880 bool shouldTreatAsFixed =
2881 layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2882 !isTransformedFixed;
2884 bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsNone();
2885 bool isRasterImage = state.mImageRenderer.IsRasterImage();
2886 nsCOMPtr<imgIContainer> image;
2887 if (isRasterImage) {
2888 image = state.mImageRenderer.GetImage();
2890 return InitData{aBuilder, aBackgroundStyle, image,
2891 aBackgroundRect, state.mFillArea, state.mDestArea,
2892 aLayer, isRasterImage, shouldFixToViewport};
2895 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2896 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aInitData,
2897 nsIFrame* aFrameForBounds)
2898 : nsPaintedDisplayItem(aBuilder, aFrame),
2899 mBackgroundStyle(aInitData.backgroundStyle),
2900 mImage(aInitData.image),
2901 mDependentFrame(nullptr),
2902 mBackgroundRect(aInitData.backgroundRect),
2903 mFillRect(aInitData.fillArea),
2904 mDestRect(aInitData.destArea),
2905 mLayer(aInitData.layer),
2906 mIsRasterImage(aInitData.isRasterImage),
2907 mShouldFixToViewport(aInitData.shouldFixToViewport) {
2908 MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
2909 #ifdef DEBUG
2910 if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) {
2911 // If this changes, then you also need to adjust css::ImageLoader to
2912 // invalidate mFrame as needed.
2913 MOZ_ASSERT(mFrame->IsCanvasFrame() || mFrame->IsTablePart());
2915 #endif
2917 mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
2918 if (mShouldFixToViewport) {
2919 // Expand the item's visible rect to cover the entire bounds, limited to the
2920 // viewport rect. This is necessary because the background's clip can move
2921 // asynchronously.
2922 if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
2923 aInitData.builder, mFrame)) {
2924 SetBuildingRect(mBounds.Intersect(*viewportRect));
2929 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2930 MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
2931 if (mDependentFrame) {
2932 mDependentFrame->RemoveDisplayItem(this);
2936 static void SetBackgroundClipRegion(
2937 DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame,
2938 const nsStyleImageLayers::Layer& aLayer, const nsRect& aBackgroundRect,
2939 bool aWillPaintBorder) {
2940 nsCSSRendering::ImageLayerClipState clip;
2941 nsCSSRendering::GetImageLayerClip(
2942 aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect,
2943 aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
2945 if (clip.mHasAdditionalBGClipArea) {
2946 aClipState.ClipContentDescendants(
2947 clip.mAdditionalBGClipArea, clip.mBGClipArea,
2948 clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2949 } else {
2950 aClipState.ClipContentDescendants(
2951 clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2956 * This is used for the find bar highlighter overlay. It's only accessible
2957 * through the AnonymousContent API, so it's not exposed to general web pages.
2959 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
2960 nsIFrame* aFrame,
2961 const nsRect& aBackgroundRect,
2962 nsDisplayList* aList, nscolor aColor) {
2963 nsIContent* content = aFrame->GetContent();
2964 if (!content) {
2965 return false;
2968 void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
2969 if (!cutoutRegion) {
2970 return false;
2973 if (NS_GET_A(aColor) == 0) {
2974 return true;
2977 nsRegion region;
2978 region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
2979 region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
2980 aList->AppendNewToTop<nsDisplaySolidColorRegion>(aBuilder, aFrame, region,
2981 aColor);
2983 return true;
2986 enum class TableType : uint8_t {
2987 Table,
2988 TableCol,
2989 TableColGroup,
2990 TableRow,
2991 TableRowGroup,
2992 TableCell,
2994 MAX,
2997 enum class TableTypeBits : uint8_t { Count = 3 };
2999 static_assert(static_cast<uint8_t>(TableType::MAX) <
3000 (1 << (static_cast<uint8_t>(TableTypeBits::Count) + 1)),
3001 "TableType cannot fit with TableTypeBits::Count");
3002 TableType GetTableTypeFromFrame(nsIFrame* aFrame);
3004 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex,
3005 const TableType aType) {
3006 const uint32_t key = (aIndex << static_cast<uint8_t>(TableTypeBits::Count)) |
3007 static_cast<uint8_t>(aType);
3009 return static_cast<uint16_t>(key);
3012 static nsDisplayBackgroundImage* CreateBackgroundImage(
3013 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3014 const nsDisplayBackgroundImage::InitData& aBgData) {
3015 const auto index = aBgData.layer;
3017 if (aSecondaryFrame) {
3018 const auto tableType = GetTableTypeFromFrame(aFrame);
3019 const uint16_t tableItemIndex = CalculateTablePerFrameKey(index, tableType);
3021 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundImage>(
3022 aBuilder, aSecondaryFrame, tableItemIndex, aBgData, aFrame);
3025 return MakeDisplayItemWithIndex<nsDisplayBackgroundImage>(aBuilder, aFrame,
3026 index, aBgData);
3029 static nsDisplayThemedBackground* CreateThemedBackground(
3030 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3031 const nsRect& aBgRect) {
3032 if (aSecondaryFrame) {
3033 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3034 return MakeDisplayItemWithIndex<nsDisplayTableThemedBackground>(
3035 aBuilder, aSecondaryFrame, index, aBgRect, aFrame);
3038 return MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, aBgRect);
3041 static nsDisplayBackgroundColor* CreateBackgroundColor(
3042 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3043 nsRect& aBgRect, const ComputedStyle* aBgSC, nscolor aColor) {
3044 if (aSecondaryFrame) {
3045 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3046 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>(
3047 aBuilder, aSecondaryFrame, index, aBgRect, aBgSC, aColor, aFrame);
3050 return MakeDisplayItem<nsDisplayBackgroundColor>(aBuilder, aFrame, aBgRect,
3051 aBgSC, aColor);
3054 static void DealWithWindowsAppearanceHacks(nsIFrame* aFrame,
3055 nsDisplayListBuilder* aBuilder) {
3056 const auto& disp = *aFrame->StyleDisplay();
3058 // We use default appearance rather than effective appearance because we want
3059 // to handle when titlebar buttons that have appearance: none.
3060 const auto defaultAppearance = disp.mDefaultAppearance;
3061 if (MOZ_LIKELY(defaultAppearance == StyleAppearance::None)) {
3062 return;
3065 if (auto type = disp.GetWindowButtonType()) {
3066 if (auto* widget = aFrame->GetNearestWidget()) {
3067 auto rect = LayoutDevicePixel::FromAppUnitsToNearest(
3068 nsRect(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize()),
3069 aFrame->PresContext()->AppUnitsPerDevPixel());
3070 widget->SetWindowButtonRect(*type, rect);
3075 /*static*/
3076 AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3077 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3078 const nsRect& aBackgroundRect, nsDisplayList* aList,
3079 bool aAllowWillPaintBorderOptimization, const nsRect& aBackgroundOriginRect,
3080 nsIFrame* aSecondaryReferenceFrame,
3081 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
3082 aAutoBuildingDisplayList) {
3083 MOZ_ASSERT(!aFrame->IsCanvasFrame(),
3084 "We don't expect propagated canvas backgrounds here");
3085 #ifdef DEBUG
3087 nsIFrame* bgFrame = nsCSSRendering::FindBackgroundFrame(aFrame);
3088 MOZ_ASSERT(
3089 !bgFrame || bgFrame == aFrame,
3090 "Should only suppress backgrounds, never propagate to another frame");
3092 #endif
3094 DealWithWindowsAppearanceHacks(aFrame, aBuilder);
3096 const bool isThemed = aFrame->IsThemed();
3098 const ComputedStyle* bgSC = aFrame->Style();
3099 const nsStyleBackground* bg = bgSC->StyleBackground();
3100 const bool needsBackgroundColor =
3101 aBuilder->IsForEventDelivery() ||
3102 (EffectCompositor::HasAnimationsForCompositor(
3103 aFrame, DisplayItemType::TYPE_BACKGROUND_COLOR) &&
3104 !isThemed);
3105 if (!needsBackgroundColor && !isThemed && bg->IsTransparent(bgSC)) {
3106 return AppendedBackgroundType::None;
3109 bool drawBackgroundColor = false;
3110 bool drawBackgroundImage = false;
3111 nscolor color = NS_RGBA(0, 0, 0, 0);
3112 // Don't get background color / images if we propagated our background to the
3113 // canvas (that is, if FindBackgroundFrame is null). But don't early return
3114 // yet, since we might still need a background-color item for hit-testing.
3115 if (!isThemed && nsCSSRendering::FindBackgroundFrame(aFrame)) {
3116 color = nsCSSRendering::DetermineBackgroundColor(
3117 aFrame->PresContext(), bgSC, aFrame, drawBackgroundImage,
3118 drawBackgroundColor);
3121 if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
3122 color)) {
3123 return AppendedBackgroundType::None;
3126 const nsStyleBorder& border = *aFrame->StyleBorder();
3127 const bool willPaintBorder =
3128 aAllowWillPaintBorderOptimization && !isThemed &&
3129 !aFrame->StyleEffects()->HasBoxShadowWithInset(true) &&
3130 border.HasBorder();
3132 auto EnsureBuildingDisplayList = [&] {
3133 if (!aAutoBuildingDisplayList || *aAutoBuildingDisplayList) {
3134 return;
3136 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3137 aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3138 aBuilder->GetVisibleRect() + offset,
3139 aBuilder->GetDirtyRect() + offset);
3142 // An auxiliary list is necessary in case we have background blending; if that
3143 // is the case, background items need to be wrapped by a blend container to
3144 // isolate blending to the background
3145 nsDisplayList bgItemList(aBuilder);
3146 // Even if we don't actually have a background color to paint, we may still
3147 // need to create an item for hit testing and we still need to create an item
3148 // for background-color animations.
3149 if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) ||
3150 needsBackgroundColor) {
3151 EnsureBuildingDisplayList();
3152 Maybe<DisplayListClipState::AutoSaveRestore> clipState;
3153 nsRect bgColorRect = aBackgroundRect;
3154 if (!isThemed && !aBuilder->IsForEventDelivery()) {
3155 // Disable the will-paint-border optimization for background
3156 // colors with no border-radius. Enabling it for background colors
3157 // doesn't help much (there are no tiling issues) and clipping the
3158 // background breaks detection of the element's border-box being
3159 // opaque. For nonzero border-radius we still need it because we
3160 // want to inset the background if possible to avoid antialiasing
3161 // artifacts along the rounded corners.
3162 const bool useWillPaintBorderOptimization =
3163 willPaintBorder &&
3164 nsLayoutUtils::HasNonZeroCorner(border.mBorderRadius);
3166 nsCSSRendering::ImageLayerClipState clip;
3167 nsCSSRendering::GetImageLayerClip(
3168 bg->BottomLayer(), aFrame, border, aBackgroundRect, aBackgroundRect,
3169 useWillPaintBorderOptimization,
3170 aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3172 bgColorRect = bgColorRect.Intersect(clip.mBGClipArea);
3173 if (clip.mHasAdditionalBGClipArea) {
3174 bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea);
3176 if (clip.mHasRoundedCorners) {
3177 clipState.emplace(aBuilder);
3178 clipState->ClipContentDescendants(clip.mBGClipArea, clip.mRadii);
3182 nsDisplayBackgroundColor* bgItem = CreateBackgroundColor(
3183 aBuilder, aFrame, aSecondaryReferenceFrame, bgColorRect, bgSC,
3184 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0));
3186 if (bgItem) {
3187 bgItemList.AppendToTop(bgItem);
3191 if (isThemed) {
3192 nsDisplayThemedBackground* bgItem = CreateThemedBackground(
3193 aBuilder, aFrame, aSecondaryReferenceFrame, aBackgroundRect);
3195 if (bgItem) {
3196 bgItem->Init(aBuilder);
3197 bgItemList.AppendToTop(bgItem);
3200 if (!bgItemList.IsEmpty()) {
3201 aList->AppendToTop(&bgItemList);
3202 return AppendedBackgroundType::ThemedBackground;
3205 return AppendedBackgroundType::None;
3208 if (!drawBackgroundImage) {
3209 if (!bgItemList.IsEmpty()) {
3210 aList->AppendToTop(&bgItemList);
3211 return AppendedBackgroundType::Background;
3214 return AppendedBackgroundType::None;
3217 const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
3219 bool needBlendContainer = false;
3220 const nsRect& bgOriginRect =
3221 aBackgroundOriginRect.IsEmpty() ? aBackgroundRect : aBackgroundOriginRect;
3223 // Passing bg == nullptr in this macro will result in one iteration with
3224 // i = 0.
3225 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
3226 if (bg->mImage.mLayers[i].mImage.IsNone()) {
3227 continue;
3230 EnsureBuildingDisplayList();
3232 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3233 needBlendContainer = true;
3236 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3237 if (!aBuilder->IsForEventDelivery()) {
3238 const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
3239 SetBackgroundClipRegion(clipState, aFrame, layer, aBackgroundRect,
3240 willPaintBorder);
3243 nsDisplayList thisItemList(aBuilder);
3244 nsDisplayBackgroundImage::InitData bgData =
3245 nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect,
3246 bgSC);
3248 if (bgData.shouldFixToViewport) {
3249 auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
3250 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3251 aBuilder, aFrame, aBuilder->GetVisibleRect(),
3252 aBuilder->GetDirtyRect());
3254 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
3255 aBuilder);
3256 if (displayData) {
3257 asrSetter.SetCurrentActiveScrolledRoot(
3258 displayData->mContainingBlockActiveScrolledRoot);
3259 asrSetter.SetCurrentScrollParentId(displayData->mScrollParentId);
3260 if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
3261 // Override the dirty rect on the builder to be the dirty rect of
3262 // the viewport.
3263 // displayData->mDirtyRect is relative to the presshell's viewport
3264 // frame (the root frame), and we need it to be relative to aFrame.
3265 nsIFrame* rootFrame =
3266 aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
3267 // There cannot be any transforms between aFrame and rootFrame
3268 // because then bgData.shouldFixToViewport would have been false.
3269 nsRect visibleRect =
3270 displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame);
3271 aBuilder->SetVisibleRect(visibleRect);
3272 nsRect dirtyRect =
3273 displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
3274 aBuilder->SetDirtyRect(dirtyRect);
3278 nsDisplayBackgroundImage* bgItem = nullptr;
3280 // The clip is captured by the nsDisplayFixedPosition, so clear the
3281 // clip for the nsDisplayBackgroundImage inside.
3282 DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
3283 bgImageClip.Clear();
3284 bgItem = CreateBackgroundImage(aBuilder, aFrame,
3285 aSecondaryReferenceFrame, bgData);
3287 if (bgItem) {
3288 thisItemList.AppendToTop(
3289 nsDisplayFixedPosition::CreateForFixedBackground(
3290 aBuilder, aFrame, aSecondaryReferenceFrame, bgItem, i, asr));
3292 } else { // bgData.shouldFixToViewport == false
3293 nsDisplayBackgroundImage* bgItem = CreateBackgroundImage(
3294 aBuilder, aFrame, aSecondaryReferenceFrame, bgData);
3295 if (bgItem) {
3296 thisItemList.AppendToTop(bgItem);
3300 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3301 // asr is scrolled. Even if we wrap a fixed background layer, that's
3302 // fine, because the item will have a scrolled clip that limits the
3303 // item with respect to asr.
3304 if (aSecondaryReferenceFrame) {
3305 const auto tableType = GetTableTypeFromFrame(aFrame);
3306 const uint16_t index = CalculateTablePerFrameKey(i + 1, tableType);
3308 thisItemList.AppendNewToTopWithIndex<nsDisplayTableBlendMode>(
3309 aBuilder, aSecondaryReferenceFrame, index, &thisItemList,
3310 bg->mImage.mLayers[i].mBlendMode, asr, aFrame, true);
3311 } else {
3312 thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
3313 aBuilder, aFrame, i + 1, &thisItemList,
3314 bg->mImage.mLayers[i].mBlendMode, asr, true);
3317 bgItemList.AppendToTop(&thisItemList);
3320 if (needBlendContainer) {
3321 bgItemList.AppendToTop(
3322 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3323 aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr));
3326 if (!bgItemList.IsEmpty()) {
3327 aList->AppendToTop(&bgItemList);
3328 return AppendedBackgroundType::Background;
3331 return AppendedBackgroundType::None;
3334 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3335 // intersects aRect. Assumes that the unrounded border has already
3336 // been checked for intersection.
3337 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame,
3338 const nsPoint& aFrameToReferenceFrame,
3339 const nsRect& aTestRect) {
3340 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize())
3341 .Intersects(aTestRect)) {
3342 return false;
3345 nscoord radii[8];
3346 return !aFrame->GetBorderRadii(radii) ||
3347 nsLayoutUtils::RoundedRectIntersectsRect(
3348 nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii,
3349 aTestRect);
3352 // Returns TRUE if aContainedRect is guaranteed to be contained in
3353 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3354 // handled conservatively by returning FALSE in some situations where
3355 // a more thorough analysis could return TRUE.
3357 // See also RoundedRectIntersectsRect.
3358 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
3359 const nscoord aRadii[8],
3360 const nsRect& aContainedRect) {
3361 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii,
3362 aContainedRect);
3363 return rgn.Contains(aContainedRect);
3366 bool nsDisplayBackgroundImage::CanApplyOpacity(
3367 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3368 return CanBuildWebRenderDisplayItems(aManager, aBuilder);
3371 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3372 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3373 return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip !=
3374 StyleGeometryBox::Text &&
3375 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3376 aManager, *StyleFrame()->PresContext(), StyleFrame(),
3377 mBackgroundStyle->StyleBackground(), mLayer,
3378 aBuilder->GetBackgroundPaintFlags());
3381 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3382 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3383 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3384 nsDisplayListBuilder* aDisplayListBuilder) {
3385 if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(),
3386 aDisplayListBuilder)) {
3387 return false;
3390 uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
3391 bool dummy;
3392 nsCSSRendering::PaintBGParams params =
3393 nsCSSRendering::PaintBGParams::ForSingleLayer(
3394 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy),
3395 mBackgroundRect, StyleFrame(), paintFlags, mLayer,
3396 CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity());
3397 params.bgClipRect = &mBounds;
3398 ImgDrawResult result =
3399 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3400 params, aBuilder, aResources, aSc, aManager, this);
3401 if (result == ImgDrawResult::NOT_SUPPORTED) {
3402 return false;
3405 if (nsIContent* content = StyleFrame()->GetContent()) {
3406 if (imgRequestProxy* requestProxy = mBackgroundStyle->StyleBackground()
3407 ->mImage.mLayers[mLayer]
3408 .mImage.GetImageRequest()) {
3409 // LCP don't consider gradient backgrounds.
3410 LCPHelpers::FinalizeLCPEntryForImage(content->AsElement(), requestProxy,
3411 mBounds - ToReferenceFrame());
3415 return true;
3418 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
3419 const nsRect& aRect,
3420 HitTestState* aState,
3421 nsTArray<nsIFrame*>* aOutFrames) {
3422 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3423 aOutFrames->AppendElement(mFrame);
3427 static nsRect GetInsideClipRect(const nsDisplayItem* aItem,
3428 StyleGeometryBox aClip, const nsRect& aRect,
3429 const nsRect& aBackgroundRect) {
3430 if (aRect.IsEmpty()) {
3431 return {};
3434 nsIFrame* frame = aItem->Frame();
3436 nsRect clipRect = aBackgroundRect;
3437 if (frame->IsCanvasFrame()) {
3438 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3439 clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
3440 } else if (aClip == StyleGeometryBox::PaddingBox ||
3441 aClip == StyleGeometryBox::ContentBox) {
3442 nsMargin border = frame->GetUsedBorder();
3443 if (aClip == StyleGeometryBox::ContentBox) {
3444 border += frame->GetUsedPadding();
3446 border.ApplySkipSides(frame->GetSkipSides());
3447 clipRect.Deflate(border);
3450 return clipRect.Intersect(aRect);
3453 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
3454 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3455 nsRegion result;
3456 *aSnap = false;
3458 if (!mBackgroundStyle) {
3459 return result;
3462 *aSnap = true;
3464 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3465 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3466 // which expects frames to be sent to it in content order, not reverse
3467 // content order which we'll produce here.
3468 // Of course, if there's only one frame in the flow, it doesn't matter.
3469 if (mFrame->StyleBorder()->mBoxDecorationBreak ==
3470 StyleBoxDecorationBreak::Clone ||
3471 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
3472 const nsStyleImageLayers::Layer& layer =
3473 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3474 if (layer.mImage.IsOpaque() && layer.mBlendMode == StyleBlend::Normal &&
3475 layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
3476 layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
3477 layer.mClip != StyleGeometryBox::Text) {
3478 result = GetInsideClipRect(this, layer.mClip, mBounds, mBackgroundRect);
3482 return result;
3485 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform(
3486 nsDisplayListBuilder* aBuilder) const {
3487 if (!mBackgroundStyle) {
3488 return Some(NS_RGBA(0, 0, 0, 0));
3490 return Nothing();
3493 nsRect nsDisplayBackgroundImage::GetPositioningArea() const {
3494 if (!mBackgroundStyle) {
3495 return nsRect();
3497 nsIFrame* attachedToFrame;
3498 bool transformedFixed;
3499 return nsCSSRendering::ComputeImageLayerPositioningArea(
3500 mFrame->PresContext(), mFrame, mBackgroundRect,
3501 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer],
3502 &attachedToFrame, &transformedFixed) +
3503 ToReferenceFrame();
3506 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3507 const {
3508 if (!mBackgroundStyle) {
3509 return false;
3512 nscoord radii[8];
3513 if (mFrame->GetBorderRadii(radii)) {
3514 // A change in the size of the positioning area might change the position
3515 // of the rounded corners.
3516 return true;
3519 const nsStyleImageLayers::Layer& layer =
3520 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3521 return layer.RenderingMightDependOnPositioningAreaSizeChange();
3524 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
3525 gfxContext* aCtx) {
3526 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &mBounds);
3529 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
3530 gfxContext* aCtx,
3531 const nsRect& aBounds,
3532 nsRect* aClipRect) {
3533 gfxContext* ctx = aCtx;
3534 StyleGeometryBox clip =
3535 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip;
3537 if (clip == StyleGeometryBox::Text) {
3538 if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect,
3539 aBuilder)) {
3540 return;
3544 nsCSSRendering::PaintBGParams params =
3545 nsCSSRendering::PaintBGParams::ForSingleLayer(
3546 *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
3547 aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER,
3548 1.0f);
3549 params.bgClipRect = aClipRect;
3550 Unused << nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
3552 if (clip == StyleGeometryBox::Text) {
3553 ctx->PopGroupAndBlend();
3557 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3558 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3559 nsRegion* aInvalidRegion) const {
3560 if (!mBackgroundStyle) {
3561 return;
3564 const auto* geometry =
3565 static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
3567 bool snap;
3568 nsRect bounds = GetBounds(aBuilder, &snap);
3569 nsRect positioningArea = GetPositioningArea();
3570 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
3571 (positioningArea.Size() != geometry->mPositioningArea.Size() &&
3572 RenderingMightDependOnPositioningAreaSizeChange())) {
3573 // Positioning area changed in a way that could cause everything to change,
3574 // so invalidate everything (both old and new painting areas).
3575 aInvalidRegion->Or(bounds, geometry->mBounds);
3576 return;
3578 if (!mDestRect.IsEqualInterior(geometry->mDestRect)) {
3579 // Dest area changed in a way that could cause everything to change,
3580 // so invalidate everything (both old and new painting areas).
3581 aInvalidRegion->Or(bounds, geometry->mBounds);
3582 return;
3584 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3585 // Positioning area is unchanged, so invalidate just the change in the
3586 // painting area.
3587 aInvalidRegion->Xor(bounds, geometry->mBounds);
3591 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder,
3592 bool* aSnap) const {
3593 *aSnap = true;
3594 return mBounds;
3597 nsRect nsDisplayBackgroundImage::GetBoundsInternal(
3598 nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) {
3599 // This allows nsDisplayTableBackgroundImage to change the frame used for
3600 // bounds calculation.
3601 nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame;
3603 nsPresContext* presContext = frame->PresContext();
3605 if (!mBackgroundStyle) {
3606 return nsRect();
3609 nsRect clipRect = mBackgroundRect;
3610 if (frame->IsCanvasFrame()) {
3611 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3612 clipRect = canvasFrame->CanvasArea() + ToReferenceFrame();
3614 const nsStyleImageLayers::Layer& layer =
3615 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3616 return nsCSSRendering::GetBackgroundLayerRect(
3617 presContext, frame, mBackgroundRect, clipRect, layer,
3618 aBuilder->GetBackgroundPaintFlags());
3621 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3622 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aData,
3623 nsIFrame* aCellFrame)
3624 : nsDisplayBackgroundImage(aBuilder, aFrame, aData, aCellFrame),
3625 mStyleFrame(aCellFrame) {
3626 if (aBuilder->IsRetainingDisplayList()) {
3627 mStyleFrame->AddDisplayItem(this);
3631 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3632 if (mStyleFrame) {
3633 mStyleFrame->RemoveDisplayItem(this);
3637 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const {
3638 bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
3639 aRect += ToReferenceFrame();
3640 return result;
3643 nsDisplayThemedBackground::nsDisplayThemedBackground(
3644 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3645 const nsRect& aBackgroundRect)
3646 : nsPaintedDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) {
3647 MOZ_COUNT_CTOR(nsDisplayThemedBackground);
3650 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) {
3651 const nsStyleDisplay* disp = StyleFrame()->StyleDisplay();
3652 mAppearance = disp->EffectiveAppearance();
3653 StyleFrame()->IsThemed(disp, &mThemeTransparency);
3655 // Perform necessary RegisterThemeGeometry
3656 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3657 nsITheme::ThemeGeometryType type =
3658 theme->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance);
3659 if (type != nsITheme::eThemeGeometryTypeUnknown) {
3660 RegisterThemeGeometry(aBuilder, this, StyleFrame(), type);
3663 mBounds = GetBoundsInternal();
3666 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) {
3667 aStream << " (themed, appearance:" << (int)mAppearance << ")";
3670 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
3671 const nsRect& aRect,
3672 HitTestState* aState,
3673 nsTArray<nsIFrame*>* aOutFrames) {
3674 // Assume that any point in our background rect is a hit.
3675 if (mBackgroundRect.Intersects(aRect)) {
3676 aOutFrames->AppendElement(mFrame);
3680 nsRegion nsDisplayThemedBackground::GetOpaqueRegion(
3681 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3682 nsRegion result;
3683 *aSnap = false;
3685 if (mThemeTransparency == nsITheme::eOpaque) {
3686 *aSnap = true;
3687 result = mBackgroundRect;
3689 return result;
3692 Maybe<nscolor> nsDisplayThemedBackground::IsUniform(
3693 nsDisplayListBuilder* aBuilder) const {
3694 return Nothing();
3697 nsRect nsDisplayThemedBackground::GetPositioningArea() const {
3698 return mBackgroundRect;
3701 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
3702 gfxContext* aCtx) {
3703 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), nullptr);
3706 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
3707 gfxContext* aCtx,
3708 const nsRect& aBounds,
3709 nsRect* aClipRect) {
3710 // XXXzw this ignores aClipRect.
3711 nsPresContext* presContext = StyleFrame()->PresContext();
3712 nsITheme* theme = presContext->Theme();
3713 nsRect drawing(mBackgroundRect);
3714 theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(),
3715 mAppearance, &drawing);
3716 drawing.IntersectRect(drawing, aBounds);
3717 theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect,
3718 drawing);
3721 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3722 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3723 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3724 nsDisplayListBuilder* aDisplayListBuilder) {
3725 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3726 return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc,
3727 aManager, StyleFrame(),
3728 mAppearance, mBackgroundRect);
3731 bool nsDisplayThemedBackground::IsWindowActive() const {
3732 return !mFrame->PresContext()->Document()->IsTopLevelWindowInactive();
3735 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3736 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3737 nsRegion* aInvalidRegion) const {
3738 const auto* geometry =
3739 static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
3741 bool snap;
3742 nsRect bounds = GetBounds(aBuilder, &snap);
3743 nsRect positioningArea = GetPositioningArea();
3744 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
3745 // Invalidate everything (both old and new painting areas).
3746 aInvalidRegion->Or(bounds, geometry->mBounds);
3747 return;
3749 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3750 // Positioning area is unchanged, so invalidate just the change in the
3751 // painting area.
3752 aInvalidRegion->Xor(bounds, geometry->mBounds);
3754 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3755 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
3756 IsWindowActive() != geometry->mWindowIsActive) {
3757 aInvalidRegion->Or(*aInvalidRegion, bounds);
3761 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder,
3762 bool* aSnap) const {
3763 *aSnap = true;
3764 return mBounds;
3767 nsRect nsDisplayThemedBackground::GetBoundsInternal() {
3768 nsPresContext* presContext = mFrame->PresContext();
3770 nsRect r = mBackgroundRect - ToReferenceFrame();
3771 presContext->Theme()->GetWidgetOverflow(
3772 presContext->DeviceContext(), mFrame,
3773 mFrame->StyleDisplay()->EffectiveAppearance(), &r);
3774 return r + ToReferenceFrame();
3777 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
3778 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder,
3779 gfxContext* aCtx) {
3780 mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(),
3781 mFrame, ToReferenceFrame(), mColor);
3783 #endif
3785 bool nsDisplayBackgroundColor::CanApplyOpacity(
3786 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3787 // Don't apply opacity if the background color is animated since the color is
3788 // going to be changed on the compositor.
3789 return !EffectCompositor::HasAnimationsForCompositor(
3790 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR);
3793 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3794 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3795 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3796 nsDisplayListBuilder* aDisplayListBuilder) {
3797 gfx::sRGBColor color = mColor;
3798 color.a *= aBuilder.GetInheritedOpacity();
3800 if (color == sRGBColor() &&
3801 !EffectCompositor::HasAnimationsForCompositor(
3802 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3803 return true;
3806 if (HasBackgroundClipText()) {
3807 return false;
3810 uint64_t animationsId = 0;
3811 // We don't support background-color animations on table elements yet.
3812 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
3813 animationsId =
3814 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
3817 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
3818 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3819 wr::LayoutRect r = wr::ToLayoutRect(bounds);
3821 if (animationsId) {
3822 wr::WrAnimationProperty prop{
3823 wr::WrAnimationType::BackgroundColor,
3824 animationsId,
3826 aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(),
3827 wr::ToColorF(ToDeviceColor(color)), &prop);
3828 } else {
3829 aBuilder.StartGroup(this);
3830 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
3831 wr::ToColorF(ToDeviceColor(color)));
3832 aBuilder.FinishGroup();
3835 return true;
3838 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder,
3839 gfxContext* aCtx,
3840 const DisplayItemClip& aClip) {
3841 MOZ_ASSERT(!HasBackgroundClipText());
3843 if (mColor == sRGBColor()) {
3844 return;
3847 nsRect fillRect = mBackgroundRect;
3848 if (aClip.HasClip()) {
3849 fillRect.IntersectRect(fillRect, aClip.GetClipRect());
3852 DrawTarget* dt = aCtx->GetDrawTarget();
3853 int32_t A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
3854 Rect bounds = ToRect(nsLayoutUtils::RectToGfxRect(fillRect, A2D));
3855 MaybeSnapToDevicePixels(bounds, *dt);
3856 ColorPattern fill(ToDeviceColor(mColor));
3858 if (aClip.GetRoundedRectCount()) {
3859 MOZ_ASSERT(aClip.GetRoundedRectCount() == 1);
3861 AutoTArray<DisplayItemClip::RoundedRect, 1> roundedRect;
3862 aClip.AppendRoundedRects(&roundedRect);
3864 bool pushedClip = false;
3865 if (!fillRect.Contains(roundedRect[0].mRect)) {
3866 dt->PushClipRect(bounds);
3867 pushedClip = true;
3870 RectCornerRadii pixelRadii;
3871 nsCSSRendering::ComputePixelRadii(roundedRect[0].mRadii, A2D, &pixelRadii);
3872 dt->FillRoundedRect(
3873 RoundedRect(NSRectToSnappedRect(roundedRect[0].mRect, A2D, *dt),
3874 pixelRadii),
3875 fill);
3876 if (pushedClip) {
3877 dt->PopClip();
3879 } else {
3880 dt->FillRect(bounds, fill);
3884 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
3885 gfxContext* aCtx) {
3886 if (mColor == sRGBColor()) {
3887 return;
3890 #if 0
3891 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3892 // results in a precision induced rounding issue that makes the rect one
3893 // pixel shorter in rare cases. Disabled in favor of the old code for now.
3894 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3895 // reproduce the bug.
3897 // TODO:
3898 // This new path does not include support for background-clip:text; need to
3899 // be fixed if/when we switch to this new code path.
3901 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
3903 Rect rect = NSRectToSnappedRect(mBackgroundRect,
3904 mFrame->PresContext()->AppUnitsPerDevPixel(),
3905 aDrawTarget);
3906 ColorPattern color(ToDeviceColor(mColor));
3907 aDrawTarget.FillRect(rect, color);
3908 #else
3909 gfxContext* ctx = aCtx;
3910 gfxRect bounds = nsLayoutUtils::RectToGfxRect(
3911 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3913 if (HasBackgroundClipText()) {
3914 if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
3915 return;
3918 ctx->SetColor(mColor);
3919 ctx->NewPath();
3920 ctx->SnappedRectangle(bounds);
3921 ctx->Fill();
3922 ctx->PopGroupAndBlend();
3923 return;
3926 ctx->SetColor(mColor);
3927 ctx->NewPath();
3928 ctx->SnappedRectangle(bounds);
3929 ctx->Fill();
3930 #endif
3933 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion(
3934 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3935 *aSnap = false;
3937 if (mColor.a != 1 ||
3938 // Even if the current alpha channel is 1, we treat this item as if it's
3939 // non-opaque if there is a background-color animation since the animation
3940 // might change the alpha channel.
3941 EffectCompositor::HasAnimationsForCompositor(
3942 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3943 return nsRegion();
3946 if (!mHasStyle || HasBackgroundClipText()) {
3947 return nsRegion();
3950 *aSnap = true;
3951 return GetInsideClipRect(this, mBottomLayerClip, mBackgroundRect,
3952 mBackgroundRect);
3955 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform(
3956 nsDisplayListBuilder* aBuilder) const {
3957 return Some(mColor.ToABGR());
3960 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
3961 const nsRect& aRect,
3962 HitTestState* aState,
3963 nsTArray<nsIFrame*>* aOutFrames) {
3964 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3965 // aRect doesn't intersect our border-radius curve.
3966 return;
3969 aOutFrames->AppendElement(mFrame);
3972 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) {
3973 aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << ","
3974 << mColor.a << ")";
3975 aStream << " backgroundRect" << mBackgroundRect;
3978 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
3979 bool* aSnap) const {
3980 *aSnap = false;
3981 return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
3984 nsRect nsDisplayOutline::GetInnerRect() const {
3985 if (nsRect* savedOutlineInnerRect =
3986 mFrame->GetProperty(nsIFrame::OutlineInnerRectProperty())) {
3987 return *savedOutlineInnerRect;
3989 return mFrame->GetRectRelativeToSelf();
3992 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
3993 // TODO join outlines together
3994 MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(),
3995 "Should have not created a nsDisplayOutline!");
3997 nsRect rect = GetInnerRect() + ToReferenceFrame();
3998 nsPresContext* pc = mFrame->PresContext();
3999 if (IsThemedOutline()) {
4000 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
4001 pc->Theme()->DrawWidgetBackground(aCtx, mFrame,
4002 StyleAppearance::FocusOutline, rect,
4003 GetPaintRect(aBuilder, aCtx));
4004 return;
4007 nsCSSRendering::PaintNonThemedOutline(
4008 pc, *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), rect, mFrame->Style());
4011 bool nsDisplayOutline::IsThemedOutline() const {
4012 #ifdef DEBUG
4013 nsPresContext* pc = mFrame->PresContext();
4014 MOZ_ASSERT(
4015 pc->Theme()->ThemeSupportsWidget(pc, mFrame,
4016 StyleAppearance::FocusOutline),
4017 "All of our supported platforms have support for themed focus-outlines");
4018 #endif
4019 return mFrame->StyleOutline()->mOutlineStyle.IsAuto();
4022 bool nsDisplayOutline::CreateWebRenderCommands(
4023 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4024 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4025 nsDisplayListBuilder* aDisplayListBuilder) {
4026 nsPresContext* pc = mFrame->PresContext();
4027 nsRect rect = GetInnerRect() + ToReferenceFrame();
4028 if (IsThemedOutline()) {
4029 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
4030 return pc->Theme()->CreateWebRenderCommandsForWidget(
4031 aBuilder, aResources, aSc, aManager, mFrame,
4032 StyleAppearance::FocusOutline, rect);
4035 bool dummy;
4036 Maybe<nsCSSBorderRenderer> borderRenderer =
4037 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4038 pc, /* aDrawTarget = */ nullptr, mFrame,
4039 GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style());
4041 if (!borderRenderer) {
4042 // No border renderer means "there is no outline".
4043 // Paint nothing and return success.
4044 return true;
4047 borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
4048 return true;
4051 bool nsDisplayOutline::HasRadius() const {
4052 const auto& radius = mFrame->StyleBorder()->mBorderRadius;
4053 return !nsLayoutUtils::HasNonZeroCorner(radius);
4056 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const {
4057 const nsStyleOutline* outline = mFrame->StyleOutline();
4058 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
4059 if (borderBox.Contains(aRect) && !HasRadius() &&
4060 outline->mOutlineOffset.ToCSSPixels() >= 0.0f) {
4061 // aRect is entirely inside the border-rect, and the outline isn't rendered
4062 // inside the border-rect, so the outline is not visible.
4063 return true;
4065 return false;
4068 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
4069 const nsRect& aRect, HitTestState* aState,
4070 nsTArray<nsIFrame*>* aOutFrames) {
4071 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4072 // aRect doesn't intersect our border-radius curve.
4073 return;
4076 aOutFrames->AppendElement(mFrame);
4079 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4080 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4081 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4082 nsDisplayListBuilder* aDisplayListBuilder) {
4083 return true;
4086 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4087 return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
4090 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) {
4091 mOverrideZIndex = Some(aZIndex);
4094 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
4095 nsIFrame* aCaretFrame)
4096 : nsPaintedDisplayItem(aBuilder, aCaretFrame),
4097 mCaret(aBuilder->GetCaret()),
4098 mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) {
4099 MOZ_COUNT_CTOR(nsDisplayCaret);
4100 // The presence of a caret doesn't change the overflow rect
4101 // of the owning frame, so the normal building rect might not
4102 // include the caret at all. We use MarkFrameForDisplay to ensure
4103 // we build this item, and here we override the building rect
4104 // to cover the pixels we're going to draw.
4105 SetBuildingRect(mBounds);
4108 #ifdef NS_BUILD_REFCNT_LOGGING
4109 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret); }
4110 #endif
4112 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder,
4113 bool* aSnap) const {
4114 *aSnap = true;
4115 // The caret returns a rect in the coordinates of mFrame.
4116 return mBounds;
4119 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4120 // Note: Because we exist, we know that the caret is visible, so we don't
4121 // need to check for the caret's visibility.
4122 mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
4125 bool nsDisplayCaret::CreateWebRenderCommands(
4126 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4127 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4128 nsDisplayListBuilder* aDisplayListBuilder) {
4129 using namespace layers;
4130 nsRect caretRect;
4131 nsRect hookRect;
4132 nscolor caretColor;
4133 nsIFrame* frame =
4134 mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor);
4135 MOZ_ASSERT(frame == mFrame, "We're referring different frame");
4136 if (!frame) {
4137 return true;
4140 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
4141 gfx::DeviceColor color = ToDeviceColor(caretColor);
4142 LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
4143 caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
4144 LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
4145 hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
4147 wr::LayoutRect caret = wr::ToLayoutRect(devCaretRect);
4148 wr::LayoutRect hook = wr::ToLayoutRect(devHookRect);
4150 // Note, WR will pixel snap anything that is layout aligned.
4151 aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), false, false,
4152 wr::ToColorF(color));
4154 if (!devHookRect.IsEmpty()) {
4155 aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), false, false,
4156 wr::ToColorF(color));
4158 return true;
4161 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder,
4162 nsIFrame* aFrame)
4163 : nsPaintedDisplayItem(aBuilder, aFrame) {
4164 MOZ_COUNT_CTOR(nsDisplayBorder);
4166 mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
4169 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const {
4170 nsRect paddingRect = GetPaddingRect();
4171 const nsStyleBorder* styleBorder;
4172 if (paddingRect.Contains(aRect) &&
4173 !(styleBorder = mFrame->StyleBorder())->IsBorderImageSizeAvailable() &&
4174 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
4175 // aRect is entirely inside the content rect, and no part
4176 // of the border is rendered inside the content rect, so we are not
4177 // visible
4178 // Skip this if there's a border-image (which draws a background
4179 // too) or if there is a border-radius (which makes the border draw
4180 // further in).
4181 return true;
4184 return false;
4187 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry(
4188 nsDisplayListBuilder* aBuilder) {
4189 return new nsDisplayBorderGeometry(this, aBuilder);
4192 void nsDisplayBorder::ComputeInvalidationRegion(
4193 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4194 nsRegion* aInvalidRegion) const {
4195 const auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
4196 bool snap;
4198 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
4199 // We can probably get away with only invalidating the difference
4200 // between the border and padding rects, but the XUL ui at least
4201 // is apparently painting a background with this?
4202 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4206 bool nsDisplayBorder::CreateWebRenderCommands(
4207 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4208 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4209 nsDisplayListBuilder* aDisplayListBuilder) {
4210 nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
4212 ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder(
4213 this, mFrame, rect, aBuilder, aResources, aSc, aManager,
4214 aDisplayListBuilder);
4216 if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
4217 return false;
4219 return true;
4222 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4223 nsPoint offset = ToReferenceFrame();
4225 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
4226 ? PaintBorderFlags::SyncDecodeImages
4227 : PaintBorderFlags();
4229 Unused << nsCSSRendering::PaintBorder(
4230 mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
4231 nsRect(offset, mFrame->GetSize()), mFrame->Style(), flags,
4232 mFrame->GetSkipSides());
4235 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder,
4236 bool* aSnap) const {
4237 *aSnap = true;
4238 return mBounds;
4241 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
4242 gfxContext* aCtx) {
4243 nsPoint offset = ToReferenceFrame();
4244 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4245 nsPresContext* presContext = mFrame->PresContext();
4247 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
4249 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
4250 GetPaintRect(aBuilder, aCtx), 1.0f);
4253 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
4254 bool* aSnap) const {
4255 *aSnap = false;
4256 return mBounds;
4259 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() {
4260 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
4261 ToReferenceFrame();
4264 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const {
4265 nsPoint origin = ToReferenceFrame();
4266 nsRect frameRect(origin, mFrame->GetSize());
4267 if (!frameRect.Contains(aRect)) {
4268 return false;
4271 // the visible region is entirely inside the border-rect, and box shadows
4272 // never render within the border-rect (unless there's a border radius).
4273 nscoord twipsRadii[8];
4274 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
4275 if (!hasBorderRadii) {
4276 return true;
4279 return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
4282 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4283 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4284 if (shadows.IsEmpty()) {
4285 return false;
4288 bool hasBorderRadius;
4289 // We don't support native themed things yet like box shadows around
4290 // input buttons.
4292 // TODO(emilio): The non-native theme could provide the right rect+radius
4293 // instead relatively painlessly, if we find this causes performance issues or
4294 // what not.
4295 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4298 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4299 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4300 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4301 nsDisplayListBuilder* aDisplayListBuilder) {
4302 if (!CanBuildWebRenderDisplayItems()) {
4303 return false;
4306 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4307 nsPoint offset = ToReferenceFrame();
4308 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4309 bool snap;
4310 nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
4312 bool hasBorderRadius;
4313 bool nativeTheme =
4314 nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4316 // Don't need the full size of the shadow rect like we do in
4317 // nsCSSRendering since WR takes care of calculations for blur
4318 // and spread radius.
4319 nsRect frameRect =
4320 nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
4322 RectCornerRadii borderRadii;
4323 if (hasBorderRadius) {
4324 hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
4325 mFrame, borderRadii);
4328 // Everything here is in app units, change to device units.
4329 LayoutDeviceRect clipRect =
4330 LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel);
4331 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4332 MOZ_ASSERT(!shadows.IsEmpty());
4334 for (const auto& shadow : Reversed(shadows)) {
4335 if (shadow.inset) {
4336 continue;
4339 float blurRadius =
4340 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4341 gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor(
4342 shadow.base, mFrame, aBuilder.GetInheritedOpacity());
4344 // We don't move the shadow rect here since WR does it for us
4345 // Now translate everything to device pixels.
4346 const nsRect& shadowRect = frameRect;
4347 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4348 nsPoint(shadow.base.horizontal.ToAppUnits(),
4349 shadow.base.vertical.ToAppUnits()),
4350 appUnitsPerDevPixel);
4352 LayoutDeviceRect deviceBox =
4353 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4354 wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
4355 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4357 LayoutDeviceSize zeroSize;
4358 wr::BorderRadius borderRadius =
4359 wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
4360 if (hasBorderRadius) {
4361 borderRadius = wr::ToBorderRadius(
4362 LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
4363 LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
4364 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
4365 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
4368 float spreadRadius =
4369 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4371 aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
4372 deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
4373 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius,
4374 spreadRadius, borderRadius,
4375 wr::BoxShadowClipMode::Outset);
4378 return true;
4381 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4382 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4383 nsRegion* aInvalidRegion) const {
4384 const auto* geometry =
4385 static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
4386 bool snap;
4387 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
4388 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
4389 nsRegion oldShadow, newShadow;
4390 nscoord dontCare[8];
4391 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
4392 if (hasBorderRadius) {
4393 // If we have rounded corners then we need to invalidate the frame area
4394 // too since we paint into it.
4395 oldShadow = geometry->mBounds;
4396 newShadow = GetBounds(aBuilder, &snap);
4397 } else {
4398 oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
4399 newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
4401 aInvalidRegion->Or(oldShadow, newShadow);
4405 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
4406 gfxContext* aCtx) {
4407 nsPoint offset = ToReferenceFrame();
4408 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4409 nsPresContext* presContext = mFrame->PresContext();
4411 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS);
4413 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
4416 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4417 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4418 const nsPoint& aReferenceOffset) {
4419 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4420 if (shadows.IsEmpty()) {
4421 // Means we don't have to paint anything
4422 return true;
4425 bool hasBorderRadius;
4426 bool nativeTheme =
4427 nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
4429 // We don't support native themed things yet like box shadows around
4430 // input buttons.
4431 return !nativeTheme;
4434 /* static */
4435 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4436 wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
4437 nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect) {
4438 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) {
4439 return;
4442 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
4444 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4446 LayoutDeviceRect clipRect =
4447 LayoutDeviceRect::FromAppUnits(aVisibleRect, appUnitsPerDevPixel);
4449 for (const auto& shadow : Reversed(shadows)) {
4450 if (!shadow.inset) {
4451 continue;
4454 nsRect shadowRect =
4455 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
4456 RectCornerRadii innerRadii;
4457 nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
4459 // Now translate everything to device pixels.
4460 LayoutDeviceRect deviceBoxRect =
4461 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4462 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4463 sRGBColor shadowColor =
4464 nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0);
4466 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4467 nsPoint(shadow.base.horizontal.ToAppUnits(),
4468 shadow.base.vertical.ToAppUnits()),
4469 appUnitsPerDevPixel);
4471 float blurRadius =
4472 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4474 wr::BorderRadius borderRadius = wr::ToBorderRadius(
4475 LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
4476 LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
4477 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
4478 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
4479 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4480 float spreadRadius =
4481 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4483 aBuilder.PushBoxShadow(
4484 wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
4485 !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
4486 wr::ToLayoutVector2D(shadowOffset),
4487 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, spreadRadius,
4488 borderRadius, wr::BoxShadowClipMode::Inset);
4492 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4493 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4494 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4495 nsDisplayListBuilder* aDisplayListBuilder) {
4496 if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame,
4497 ToReferenceFrame())) {
4498 return false;
4501 bool snap;
4502 nsRect visible = GetBounds(aDisplayListBuilder, &snap);
4503 nsPoint offset = ToReferenceFrame();
4504 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4505 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4506 aBuilder, aSc, visible, mFrame, borderRect);
4508 return true;
4511 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4512 nsIFrame* aFrame, nsDisplayList* aList)
4513 : nsDisplayWrapList(aBuilder, aFrame, aList,
4514 aBuilder->CurrentActiveScrolledRoot(), false) {}
4516 nsDisplayWrapList::nsDisplayWrapList(
4517 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4518 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
4519 : nsPaintedDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
4520 mList(aBuilder),
4521 mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
4522 mOverrideZIndex(0),
4523 mHasZIndexOverride(false),
4524 mClearingClipChain(aClearClipChain) {
4525 MOZ_COUNT_CTOR(nsDisplayWrapList);
4527 mBaseBuildingRect = GetBuildingRect();
4529 mListPtr = &mList;
4530 mListPtr->AppendToTop(aList);
4531 mOriginalClipChain = mClipChain;
4532 nsDisplayWrapList::UpdateBounds(aBuilder);
4535 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4536 nsIFrame* aFrame, nsDisplayItem* aItem)
4537 : nsPaintedDisplayItem(aBuilder, aFrame,
4538 aBuilder->CurrentActiveScrolledRoot()),
4539 mList(aBuilder),
4540 mOverrideZIndex(0),
4541 mHasZIndexOverride(false) {
4542 MOZ_COUNT_CTOR(nsDisplayWrapList);
4544 mBaseBuildingRect = GetBuildingRect();
4546 mListPtr = &mList;
4547 mListPtr->AppendToTop(aItem);
4548 mOriginalClipChain = mClipChain;
4549 nsDisplayWrapList::UpdateBounds(aBuilder);
4551 if (!aFrame || !aFrame->IsTransformed()) {
4552 return;
4555 // See the previous nsDisplayWrapList constructor
4556 if (aItem->Frame() == aFrame) {
4557 mToReferenceFrame = aItem->ToReferenceFrame();
4560 nsRect visible = aBuilder->GetVisibleRect() +
4561 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
4563 SetBuildingRect(visible);
4566 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList); }
4568 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder,
4569 const nsRect& aRect, HitTestState* aState,
4570 nsTArray<nsIFrame*>* aOutFrames) {
4571 mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames);
4574 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder,
4575 bool* aSnap) const {
4576 *aSnap = false;
4577 return mBounds;
4580 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4581 bool* aSnap) const {
4582 *aSnap = false;
4583 bool snap;
4584 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
4585 GetBounds(aBuilder, &snap));
4588 Maybe<nscolor> nsDisplayWrapList::IsUniform(
4589 nsDisplayListBuilder* aBuilder) const {
4590 // We could try to do something but let's conservatively just return Nothing.
4591 return Nothing();
4594 void nsDisplayWrapper::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4595 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4598 nsRect nsDisplayWrapList::GetComponentAlphaBounds(
4599 nsDisplayListBuilder* aBuilder) const {
4600 return mListPtr->GetComponentAlphaBounds(aBuilder);
4603 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4604 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4605 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4606 nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList) {
4607 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4608 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
4609 aNewClipList);
4610 return true;
4613 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder,
4614 nsIFrame* aFrame, nsDisplayList* aList,
4615 nsDisplayItemWrapper* aWrapper) {
4616 if (!aList->GetTop()) {
4617 return NS_OK;
4619 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
4620 if (!item) {
4621 return NS_ERROR_OUT_OF_MEMORY;
4623 // aList was emptied
4624 aList->AppendToTop(item);
4625 return NS_OK;
4628 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
4629 nsDisplayList* aList,
4630 nsDisplayItemWrapper* aWrapper) {
4631 for (nsDisplayItem* item : aList->TakeItems()) {
4632 item = aWrapper->WrapItem(aBuilder, item);
4633 if (!item) {
4634 return NS_ERROR_OUT_OF_MEMORY;
4636 aList->AppendToTop(item);
4638 // aList was emptied
4639 return NS_OK;
4642 nsresult nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
4643 nsIFrame* aFrame,
4644 const nsDisplayListSet& aIn,
4645 const nsDisplayListSet& aOut) {
4646 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
4647 NS_ENSURE_SUCCESS(rv, rv);
4649 if (&aOut == &aIn) {
4650 return NS_OK;
4652 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
4653 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
4654 aOut.Floats()->AppendToTop(aIn.Floats());
4655 aOut.Content()->AppendToTop(aIn.Content());
4656 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
4657 aOut.Outlines()->AppendToTop(aIn.Outlines());
4658 return NS_OK;
4661 nsresult nsDisplayItemWrapper::WrapListsInPlace(
4662 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4663 const nsDisplayListSet& aLists) {
4664 nsresult rv;
4665 if (WrapBorderBackground()) {
4666 // Our border-backgrounds are in-flow
4667 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
4668 NS_ENSURE_SUCCESS(rv, rv);
4670 // Our block border-backgrounds are in-flow
4671 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
4672 NS_ENSURE_SUCCESS(rv, rv);
4673 // The floats are not in flow
4674 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
4675 NS_ENSURE_SUCCESS(rv, rv);
4676 // Our child content is in flow
4677 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
4678 NS_ENSURE_SUCCESS(rv, rv);
4679 // The positioned descendants may not be in-flow
4680 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
4681 NS_ENSURE_SUCCESS(rv, rv);
4682 // The outlines may not be in-flow
4683 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
4686 nsDisplayOpacity::nsDisplayOpacity(
4687 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4688 const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsOnly,
4689 bool aNeedsActiveLayer, bool aWrapsBackdropFilter)
4690 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4691 mOpacity(aFrame->StyleEffects()->mOpacity),
4692 mForEventsOnly(aForEventsOnly),
4693 mNeedsActiveLayer(aNeedsActiveLayer),
4694 mChildOpacityState(ChildOpacityState::Unknown),
4695 mWrapsBackdropFilter(aWrapsBackdropFilter) {
4696 MOZ_COUNT_CTOR(nsDisplayOpacity);
4699 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder,
4700 const nsRect& aRect,
4701 nsDisplayItem::HitTestState* aState,
4702 nsTArray<nsIFrame*>* aOutFrames) {
4703 AutoRestore<float> opacity(aState->mCurrentOpacity);
4704 aState->mCurrentOpacity *= mOpacity;
4706 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4707 // only consider fully opaque items? Or make this configurable somehow?
4708 if (aBuilder->HitTestIsForVisibility() && mOpacity == 0.0f) {
4709 return;
4711 nsDisplayWrapList::HitTest(aBuilder, aRect, aState, aOutFrames);
4714 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4715 bool* aSnap) const {
4716 *aSnap = false;
4717 // The only time where mOpacity == 1.0 should be when we have will-change.
4718 // We could report this as opaque then but when the will-change value starts
4719 // animating the element would become non opaque and could cause repaints.
4720 return nsRegion();
4723 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4724 if (GetOpacity() == 0.0f) {
4725 return;
4728 if (GetOpacity() == 1.0f) {
4729 GetChildren()->Paint(aBuilder, aCtx,
4730 mFrame->PresContext()->AppUnitsPerDevPixel());
4731 return;
4734 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4735 // allocation.
4736 aCtx->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4737 GetChildren()->Paint(aBuilder, aCtx,
4738 mFrame->PresContext()->AppUnitsPerDevPixel());
4739 aCtx->GetDrawTarget()->PopLayer();
4742 /* static */
4743 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
4744 nsIFrame* aFrame) {
4745 return EffectCompositor::HasAnimationsForCompositor(
4746 aFrame, DisplayItemType::TYPE_OPACITY) ||
4747 (ActiveLayerTracker::IsStyleAnimated(
4748 aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()));
4751 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager,
4752 nsDisplayListBuilder* aBuilder) const {
4753 return !EffectCompositor::HasAnimationsForCompositor(
4754 mFrame, DisplayItemType::TYPE_OPACITY);
4757 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4758 // children that don't overlap and can all apply the opacity to themselves.
4759 static const size_t kOpacityMaxChildCount = 3;
4761 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4762 // are likely have more child items than |kOpacityMaxChildCount|.
4763 static const size_t kOpacityMaxListSize = kOpacityMaxChildCount * 2;
4766 * Recursively iterates through |aList| and collects at most
4767 * |kOpacityMaxChildCount| display item pointers to items that return true for
4768 * CanApplyOpacity(). The item pointers are added to |aArray|.
4770 * LayerEventRegions and WrapList items are ignored.
4772 * We need to do this recursively, because the child display items might contain
4773 * nested nsDisplayWrapLists.
4775 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4776 * item that returns false for CanApplyOpacity() is encountered.
4777 * Otherwise returns true.
4779 static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager,
4780 nsDisplayListBuilder* aBuilder,
4781 nsDisplayList* aList,
4782 nsTArray<nsPaintedDisplayItem*>& aArray) {
4783 if (aList->Length() > kOpacityMaxListSize) {
4784 // Exit early, since |aList| will likely contain more than
4785 // |kOpacityMaxChildCount| items.
4786 return false;
4789 for (nsDisplayItem* i : *aList) {
4790 const DisplayItemType type = i->GetType();
4792 if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
4793 continue;
4796 // Descend only into wraplists.
4797 if (type == DisplayItemType::TYPE_WRAP_LIST ||
4798 type == DisplayItemType::TYPE_CONTAINER) {
4799 // The current display item has children, process them first.
4800 if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(),
4801 aArray)) {
4802 return false;
4805 continue;
4808 if (aArray.Length() == kOpacityMaxChildCount) {
4809 return false;
4812 auto* item = i->AsPaintedDisplayItem();
4813 if (!item || !item->CanApplyOpacity(aManager, aBuilder)) {
4814 return false;
4817 aArray.AppendElement(item);
4820 return true;
4823 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager,
4824 nsDisplayListBuilder* aBuilder) {
4825 if (mChildOpacityState == ChildOpacityState::Deferred) {
4826 return false;
4829 // Iterate through the child display list and copy at most
4830 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4831 AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items;
4832 if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) {
4833 mChildOpacityState = ChildOpacityState::Deferred;
4834 return false;
4837 struct {
4838 nsPaintedDisplayItem* item{};
4839 nsRect bounds;
4840 } children[kOpacityMaxChildCount];
4842 bool snap;
4843 size_t childCount = 0;
4844 for (nsPaintedDisplayItem* item : items) {
4845 children[childCount].item = item;
4846 children[childCount].bounds = item->GetBounds(aBuilder, &snap);
4847 childCount++;
4850 for (size_t i = 0; i < childCount; i++) {
4851 for (size_t j = i + 1; j < childCount; j++) {
4852 if (children[i].bounds.Intersects(children[j].bounds)) {
4853 mChildOpacityState = ChildOpacityState::Deferred;
4854 return false;
4859 mChildOpacityState = ChildOpacityState::Applied;
4860 return true;
4864 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4865 * that has the same frame as the opacity item, and that supports painting with
4866 * opacity. In this case the opacity item can be optimized away.
4868 bool nsDisplayOpacity::ApplyToMask() {
4869 if (mList.Length() != 1) {
4870 return false;
4873 nsDisplayItem* item = mList.GetBottom();
4874 if (item->Frame() != mFrame) {
4875 // The effect item needs to have the same frame as the opacity item.
4876 return false;
4879 const DisplayItemType type = item->GetType();
4880 if (type == DisplayItemType::TYPE_MASK) {
4881 return true;
4884 return false;
4887 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4888 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder,
4889 float aInheritedOpacity) {
4890 if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
4891 mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
4892 // If we've been split, then we might need to merge, so
4893 // don't flatten us away.
4894 return false;
4897 if (mNeedsActiveLayer || mOpacity == 0.0) {
4898 // If our opacity is zero then we'll discard all descendant display items
4899 // except for layer event regions, so there's no point in doing this
4900 // optimization (and if we do do it, then invalidations of those descendants
4901 // might trigger repainting).
4902 return false;
4905 if (mList.IsEmpty()) {
4906 return false;
4909 // We can only flatten opacity items into a mask if we haven't
4910 // already flattened an earlier ancestor, since the SVG code pulls the opacity
4911 // from style directly, and won't know about the outer opacity value.
4912 if (aInheritedOpacity == 1.0f && ApplyToMask()) {
4913 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame));
4914 mChildOpacityState = ChildOpacityState::Applied;
4915 return true;
4918 // Return true if we successfully applied opacity to child items.
4919 return CanApplyToChildren(aManager, aBuilder);
4922 void nsDisplayOpacity::ComputeInvalidationRegion(
4923 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4924 nsRegion* aInvalidRegion) const {
4925 const auto* geometry =
4926 static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
4928 bool snap;
4929 if (mOpacity != geometry->mOpacity) {
4930 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4934 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) {
4935 aStream << " (opacity " << mOpacity << ", mChildOpacityState: ";
4936 switch (mChildOpacityState) {
4937 case ChildOpacityState::Unknown:
4938 aStream << "Unknown";
4939 break;
4940 case ChildOpacityState::Applied:
4941 aStream << "Applied";
4942 break;
4943 case ChildOpacityState::Deferred:
4944 aStream << "Deferred";
4945 break;
4946 default:
4947 break;
4950 aStream << ")";
4953 bool nsDisplayOpacity::CreateWebRenderCommands(
4954 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4955 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4956 nsDisplayListBuilder* aDisplayListBuilder) {
4957 MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied);
4958 float oldOpacity = aBuilder.GetInheritedOpacity();
4959 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
4960 aBuilder.SetInheritedOpacity(1.0f);
4961 aBuilder.SetInheritedClipChain(nullptr);
4962 float opacity = mOpacity * oldOpacity;
4963 float* opacityForSC = &opacity;
4965 uint64_t animationsId =
4966 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
4967 wr::WrAnimationProperty prop{
4968 wr::WrAnimationType::Opacity,
4969 animationsId,
4972 wr::StackingContextParams params;
4973 params.animation = animationsId ? &prop : nullptr;
4974 params.opacity = opacityForSC;
4975 params.clip =
4976 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
4977 if (mWrapsBackdropFilter) {
4978 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
4980 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
4981 params);
4983 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4984 &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
4985 aBuilder.SetInheritedOpacity(oldOpacity);
4986 aBuilder.SetInheritedClipChain(oldClipChain);
4987 return true;
4990 nsDisplayBlendMode::nsDisplayBlendMode(
4991 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4992 StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
4993 const bool aIsForBackground)
4994 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4995 mBlendMode(aBlendMode),
4996 mIsForBackground(aIsForBackground) {
4997 MOZ_COUNT_CTOR(nsDisplayBlendMode);
5000 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5001 bool* aSnap) const {
5002 *aSnap = false;
5003 // We are never considered opaque
5004 return nsRegion();
5007 bool nsDisplayBlendMode::CreateWebRenderCommands(
5008 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5009 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5010 nsDisplayListBuilder* aDisplayListBuilder) {
5011 wr::StackingContextParams params;
5012 params.mix_blend_mode =
5013 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
5014 params.clip =
5015 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5016 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5017 params);
5019 return nsDisplayWrapList::CreateWebRenderCommands(
5020 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5023 void nsDisplayBlendMode::Paint(nsDisplayListBuilder* aBuilder,
5024 gfxContext* aCtx) {
5025 // This should be switched to use PushLayerWithBlend, once it's
5026 // been implemented for all DrawTarget backends.
5027 DrawTarget* dt = aCtx->GetDrawTarget();
5028 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5029 Rect rect = NSRectToRect(GetPaintRect(aBuilder, aCtx), appUnitsPerDevPixel);
5030 rect.RoundOut();
5032 // Create a temporary DrawTarget that is clipped to the area that
5033 // we're going to draw to. This will include the same transform as
5034 // is currently on |dt|.
5035 RefPtr<DrawTarget> temp =
5036 dt->CreateClippedDrawTarget(rect, SurfaceFormat::B8G8R8A8);
5037 if (!temp) {
5038 return;
5041 gfxContext ctx(temp, /* aPreserveTransform */ true);
5043 GetChildren()->Paint(aBuilder, &ctx,
5044 mFrame->PresContext()->AppUnitsPerDevPixel());
5046 // Draw the temporary DT to the real destination, applying the blend mode, but
5047 // no transform.
5048 temp->Flush();
5049 RefPtr<SourceSurface> surface = temp->Snapshot();
5050 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
5051 dt->SetTransform(Matrix());
5052 dt->DrawSurface(
5053 surface, Rect(surface->GetRect()), Rect(surface->GetRect()),
5054 DrawSurfaceOptions(),
5055 DrawOptions(1.0f, nsCSSRendering::GetGFXBlendMode(mBlendMode)));
5058 gfx::CompositionOp nsDisplayBlendMode::BlendMode() {
5059 return nsCSSRendering::GetGFXBlendMode(mBlendMode);
5062 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const {
5063 // Items for the same content element should be merged into a single
5064 // compositing group.
5065 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
5066 !HasSameContent(aItem)) {
5067 return false;
5070 const auto* item = static_cast<const nsDisplayBlendMode*>(aItem);
5071 if (mIsForBackground || item->mIsForBackground) {
5072 // Don't merge background-blend-mode items
5073 return false;
5076 return true;
5079 /* static */
5080 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForMixBlendMode(
5081 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5082 const ActiveScrolledRoot* aActiveScrolledRoot) {
5083 return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
5084 aActiveScrolledRoot, false);
5087 /* static */
5088 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5089 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5090 nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot) {
5091 if (aSecondaryFrame) {
5092 auto type = GetTableTypeFromFrame(aFrame);
5093 auto index = static_cast<uint16_t>(type);
5095 return MakeDisplayItemWithIndex<nsDisplayTableBlendContainer>(
5096 aBuilder, aSecondaryFrame, index, aList, aActiveScrolledRoot, true,
5097 aFrame);
5100 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>(
5101 aBuilder, aFrame, 1, aList, aActiveScrolledRoot, true);
5104 nsDisplayBlendContainer::nsDisplayBlendContainer(
5105 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5106 const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground)
5107 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5108 mIsForBackground(aIsForBackground) {
5109 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
5112 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder* aBuilder,
5113 gfxContext* aCtx) {
5114 aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5115 GetChildren()->Paint(aBuilder, aCtx,
5116 mFrame->PresContext()->AppUnitsPerDevPixel());
5117 aCtx->GetDrawTarget()->PopLayer();
5120 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5121 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5122 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5123 nsDisplayListBuilder* aDisplayListBuilder) {
5124 wr::StackingContextParams params;
5125 params.flags |= wr::StackingContextFlags::IS_BLEND_CONTAINER;
5126 params.clip =
5127 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5128 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5129 params);
5131 return nsDisplayWrapList::CreateWebRenderCommands(
5132 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5135 nsDisplayOwnLayer::nsDisplayOwnLayer(
5136 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5137 const ActiveScrolledRoot* aActiveScrolledRoot,
5138 nsDisplayOwnLayerFlags aFlags, const ScrollbarData& aScrollbarData,
5139 bool aForceActive, bool aClearClipChain)
5140 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
5141 aClearClipChain),
5142 mFlags(aFlags),
5143 mScrollbarData(aScrollbarData),
5144 mForceActive(aForceActive),
5145 mWrAnimationId(0) {
5146 MOZ_COUNT_CTOR(nsDisplayOwnLayer);
5149 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5150 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb;
5153 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5154 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container;
5157 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5158 return IsScrollbarContainer() && IsScrollbarLayerForRoot();
5161 bool nsDisplayOwnLayer::IsScrollbarLayerForRoot() const {
5162 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5163 mScrollbarData.mTargetViewId ==
5164 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5167 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5168 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM;
5171 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5172 return GetType() == DisplayItemType::TYPE_FIXED_POSITION;
5175 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5176 return GetType() == DisplayItemType::TYPE_STICKY_POSITION;
5179 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5180 if (!mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
5181 return false;
5183 return mFrame->PresContext()->HasDynamicToolbar() ||
5184 // For tests on Android, this pref is set to simulate the dynamic
5185 // toolbar
5186 StaticPrefs::apz_fixed_margin_override_enabled();
5189 bool nsDisplayOwnLayer::ShouldFixedAndStickyContentGetAnimationIds() const {
5190 #if defined(MOZ_WIDGET_ANDROID)
5191 return mFrame->PresContext()->IsRootContentDocumentCrossProcess();
5192 #else
5193 return false;
5194 #endif
5197 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5198 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5199 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5200 nsDisplayListBuilder* aDisplayListBuilder) {
5201 Maybe<wr::WrAnimationProperty> prop;
5202 bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() &&
5203 (IsScrollThumbLayer() || IsZoomingLayer() ||
5204 (IsFixedPositionLayer() &&
5205 ShouldFixedAndStickyContentGetAnimationIds()) ||
5206 (IsStickyPositionLayer() &&
5207 ShouldFixedAndStickyContentGetAnimationIds()) ||
5208 (IsRootScrollbarContainer() && HasDynamicToolbar()));
5210 if (needsProp) {
5211 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5212 // to create and set an animation id. That way APZ can adjust the position/
5213 // zoom of this content asynchronously as needed.
5214 RefPtr<WebRenderAPZAnimationData> animationData =
5215 aManager->CommandBuilder()
5216 .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(this);
5217 mWrAnimationId = animationData->GetAnimationId();
5219 prop.emplace();
5220 prop->id = mWrAnimationId;
5221 prop->key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5222 wr::SpatialKeyKind::APZ);
5223 prop->effect_type = wr::WrAnimationType::Transform;
5226 wr::StackingContextParams params;
5227 params.animation = prop.ptrOr(nullptr);
5228 params.clip =
5229 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5230 if (IsScrollbarContainer() && IsRootScrollbarContainer()) {
5231 params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER;
5233 if (IsZoomingLayer() ||
5234 ((IsFixedPositionLayer() &&
5235 ShouldFixedAndStickyContentGetAnimationIds()) ||
5236 (IsStickyPositionLayer() &&
5237 ShouldFixedAndStickyContentGetAnimationIds()) ||
5238 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5239 params.is_2d_scale_translation = true;
5240 params.should_snap = true;
5243 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5244 params);
5246 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
5247 aDisplayListBuilder);
5248 return true;
5251 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData,
5252 WebRenderLayerScrollData* aLayerData) {
5253 bool isRelevantToApz =
5254 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5255 (IsFixedPositionLayer() &&
5256 ShouldFixedAndStickyContentGetAnimationIds()) ||
5257 (IsStickyPositionLayer() &&
5258 ShouldFixedAndStickyContentGetAnimationIds()));
5260 if (!isRelevantToApz) {
5261 return false;
5264 if (!aLayerData) {
5265 return true;
5268 if (IsZoomingLayer()) {
5269 aLayerData->SetZoomAnimationId(mWrAnimationId);
5270 return true;
5273 if (IsFixedPositionLayer() && ShouldFixedAndStickyContentGetAnimationIds()) {
5274 aLayerData->SetFixedPositionAnimationId(mWrAnimationId);
5275 return true;
5278 if (IsStickyPositionLayer() && ShouldFixedAndStickyContentGetAnimationIds()) {
5279 aLayerData->SetStickyPositionAnimationId(mWrAnimationId);
5280 return true;
5283 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5285 aLayerData->SetScrollbarData(mScrollbarData);
5287 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5288 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5289 return true;
5292 if (IsScrollThumbLayer()) {
5293 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5294 LayoutDeviceRect bounds = LayoutDeviceIntRect::FromAppUnits(
5295 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
5296 // Subframe scrollbars are subject to the pinch-zoom scale,
5297 // but root scrollbars are not because they are outside of the
5298 // region that is zoomed.
5299 const float resolution =
5300 IsScrollbarLayerForRoot()
5301 ? 1.0f
5302 : mFrame->PresContext()->PresShell()->GetCumulativeResolution();
5303 LayerIntRect layerBounds =
5304 RoundedOut(bounds * LayoutDeviceToLayerScale(resolution));
5305 aLayerData->SetVisibleRegion(LayerIntRegion(layerBounds));
5307 return true;
5310 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) {
5311 aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
5312 (int)mFlags, mScrollbarData.mTargetViewId)
5313 .get();
5316 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
5317 nsIFrame* aFrame,
5318 nsSubDocumentFrame* aSubDocFrame,
5319 nsDisplayList* aList,
5320 nsDisplayOwnLayerFlags aFlags)
5321 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5322 aBuilder->CurrentActiveScrolledRoot(), aFlags),
5323 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5324 mShouldFlatten(false),
5325 mSubDocFrame(aSubDocFrame) {
5326 MOZ_COUNT_CTOR(nsDisplaySubDocument);
5328 if (mSubDocFrame && mSubDocFrame != mFrame) {
5329 mSubDocFrame->AddDisplayItem(this);
5333 nsDisplaySubDocument::~nsDisplaySubDocument() {
5334 MOZ_COUNT_DTOR(nsDisplaySubDocument);
5335 if (mSubDocFrame) {
5336 mSubDocFrame->RemoveDisplayItem(this);
5340 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const {
5341 return mSubDocFrame ? mSubDocFrame : mFrame;
5344 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) {
5345 if (aFrame == mSubDocFrame) {
5346 mSubDocFrame = nullptr;
5347 SetDeletedFrame();
5349 nsDisplayOwnLayer::RemoveFrame(aFrame);
5352 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder,
5353 nsIFrame* aFrame) {
5354 return aBuilder->IsPaintingToWindow() &&
5355 DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext());
5358 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder,
5359 bool* aSnap) const {
5360 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5362 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5363 usingDisplayPort) {
5364 *aSnap = false;
5365 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
5368 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
5371 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5372 bool* aSnap) const {
5373 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5375 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5376 usingDisplayPort) {
5377 *aSnap = false;
5378 return nsRegion();
5381 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
5384 /* static */
5385 nsDisplayFixedPosition* nsDisplayFixedPosition::CreateForFixedBackground(
5386 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5387 nsDisplayBackgroundImage* aImage, const uint16_t aIndex,
5388 const ActiveScrolledRoot* aScrollTargetASR) {
5389 nsDisplayList temp(aBuilder);
5390 temp.AppendToTop(aImage);
5392 if (aSecondaryFrame) {
5393 auto tableType = GetTableTypeFromFrame(aFrame);
5394 const uint16_t index = CalculateTablePerFrameKey(aIndex + 1, tableType);
5395 return MakeDisplayItemWithIndex<nsDisplayTableFixedPosition>(
5396 aBuilder, aSecondaryFrame, index, &temp, aFrame, aScrollTargetASR);
5399 return MakeDisplayItemWithIndex<nsDisplayFixedPosition>(
5400 aBuilder, aFrame, aIndex + 1, &temp, aScrollTargetASR);
5403 nsDisplayFixedPosition::nsDisplayFixedPosition(
5404 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5405 const ActiveScrolledRoot* aActiveScrolledRoot,
5406 const ActiveScrolledRoot* aScrollTargetASR)
5407 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5408 mScrollTargetASR(aScrollTargetASR),
5409 mIsFixedBackground(false) {
5410 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5413 nsDisplayFixedPosition::nsDisplayFixedPosition(
5414 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5415 const ActiveScrolledRoot* aScrollTargetASR)
5416 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5417 aBuilder->CurrentActiveScrolledRoot()),
5418 // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5419 mScrollTargetASR(aScrollTargetASR),
5420 mIsFixedBackground(true) {
5421 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5424 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() {
5425 if (mScrollTargetASR &&
5426 (mIsFixedBackground || !nsLayoutUtils::IsReallyFixedPos(mFrame))) {
5427 return mScrollTargetASR->GetViewId();
5429 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5432 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5433 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5434 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5435 nsDisplayListBuilder* aDisplayListBuilder) {
5436 SideBits sides = SideBits::eNone;
5437 if (!mIsFixedBackground) {
5438 sides = nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5441 // We install this RAII scrolltarget tracker so that any
5442 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5443 // share the same ASR as this item) use the correct scroll target. That way
5444 // attempts to scroll on those items will scroll the root scroll frame.
5445 wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker(
5446 aBuilder, GetActiveScrolledRoot(), GetScrollTargetId(), sides);
5447 return nsDisplayOwnLayer::CreateWebRenderCommands(
5448 aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
5451 bool nsDisplayFixedPosition::UpdateScrollData(
5452 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5453 if (aLayerData) {
5454 if (!mIsFixedBackground) {
5455 aLayerData->SetFixedPositionSides(
5456 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame));
5458 aLayerData->SetFixedPositionScrollContainerId(GetScrollTargetId());
5460 nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5461 return true;
5464 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream& aStream) {
5465 aStream << nsPrintfCString(
5466 " (containerASR %s) (scrolltarget %" PRIu64 ")",
5467 ActiveScrolledRoot::ToString(mScrollTargetASR).get(),
5468 GetScrollTargetId())
5469 .get();
5472 TableType GetTableTypeFromFrame(nsIFrame* aFrame) {
5473 if (aFrame->IsTableFrame()) {
5474 return TableType::Table;
5477 if (aFrame->IsTableColFrame()) {
5478 return TableType::TableCol;
5481 if (aFrame->IsTableColGroupFrame()) {
5482 return TableType::TableColGroup;
5485 if (aFrame->IsTableRowFrame()) {
5486 return TableType::TableRow;
5489 if (aFrame->IsTableRowGroupFrame()) {
5490 return TableType::TableRowGroup;
5493 if (aFrame->IsTableCellFrame()) {
5494 return TableType::TableCell;
5497 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5498 return TableType::Table;
5501 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5502 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5503 nsIFrame* aAncestorFrame, const ActiveScrolledRoot* aScrollTargetASR)
5504 : nsDisplayFixedPosition(aBuilder, aFrame, aList, aScrollTargetASR),
5505 mAncestorFrame(aAncestorFrame) {
5506 if (aBuilder->IsRetainingDisplayList()) {
5507 mAncestorFrame->AddDisplayItem(this);
5511 nsDisplayStickyPosition::nsDisplayStickyPosition(
5512 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5513 const ActiveScrolledRoot* aActiveScrolledRoot,
5514 const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
5515 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5516 mContainerASR(aContainerASR),
5517 mClippedToDisplayPort(aClippedToDisplayPort),
5518 mShouldFlatten(false) {
5519 MOZ_COUNT_CTOR(nsDisplayStickyPosition);
5522 // Returns the smallest distance from "0" to the range [min, max] where
5523 // min <= max. Despite the name, the return value is actually a 1-D vector,
5524 // and so may be negative if max < 0.
5525 static nscoord DistanceToRange(nscoord min, nscoord max) {
5526 MOZ_ASSERT(min <= max);
5527 if (max < 0) {
5528 return max;
5530 if (min > 0) {
5531 return min;
5533 MOZ_ASSERT(min <= 0 && max >= 0);
5534 return 0;
5537 // Returns the magnitude of the part of the range [min, max] that is greater
5538 // than zero. The return value is always non-negative.
5539 static nscoord PositivePart(nscoord min, nscoord max) {
5540 MOZ_ASSERT(min <= max);
5541 if (min >= 0) {
5542 return max - min;
5544 if (max > 0) {
5545 return max;
5547 return 0;
5550 // Returns the magnitude of the part of the range [min, max] that is less
5551 // than zero. The return value is always non-negative.
5552 static nscoord NegativePart(nscoord min, nscoord max) {
5553 MOZ_ASSERT(min <= max);
5554 if (max <= 0) {
5555 return max - min;
5557 if (min < 0) {
5558 return 0 - min;
5560 return 0;
5563 StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() {
5564 StickyScrollContainer* stickyScrollContainer =
5565 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
5566 if (stickyScrollContainer) {
5567 // If there's no ASR for the scrollframe that this sticky item is attached
5568 // to, then don't create a WR sticky item for it either. Trying to do so
5569 // will end in sadness because WR will interpret some coordinates as
5570 // relative to the nearest enclosing scrollframe, which will correspond
5571 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5572 // same as the scrollframe this sticky item is actually supposed to be
5573 // attached to, thus the sadness.
5574 // Not sending WR the sticky item is ok, because the enclosing scrollframe
5575 // will never be asynchronously scrolled. Instead we will always position
5576 // the sticky items correctly on the gecko side and WR will never need to
5577 // adjust their position itself.
5578 MOZ_ASSERT(
5579 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5580 if (!stickyScrollContainer->ScrollFrame()
5581 ->IsMaybeAsynchronouslyScrolled()) {
5582 stickyScrollContainer = nullptr;
5585 return stickyScrollContainer;
5588 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5589 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5590 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5591 nsDisplayListBuilder* aDisplayListBuilder) {
5592 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5594 Maybe<wr::SpaceAndClipChainHelper> saccHelper;
5596 if (stickyScrollContainer) {
5597 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5599 bool snap;
5600 nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
5602 Maybe<float> topMargin;
5603 Maybe<float> rightMargin;
5604 Maybe<float> bottomMargin;
5605 Maybe<float> leftMargin;
5606 wr::StickyOffsetBounds vBounds = {0.0, 0.0};
5607 wr::StickyOffsetBounds hBounds = {0.0, 0.0};
5608 nsPoint appliedOffset;
5610 nsRectAbsolute outer;
5611 nsRectAbsolute inner;
5612 stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5614 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
5615 nsPoint offset =
5616 scrollFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5618 // Adjust the scrollPort coordinates to be relative to the reference frame,
5619 // so that it is in the same space as everything else.
5620 nsRect scrollPort =
5621 stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
5622 scrollPort += offset;
5624 // The following computations make more sense upon understanding the
5625 // semantics of "inner" and "outer", which is explained in the comment on
5626 // SetStickyPositionData in Layers.h.
5628 if (outer.YMost() != inner.YMost()) {
5629 // Question: How far will itemBounds.y be from the top of the scrollport
5630 // when we have scrolled from the current scroll position of "0" to
5631 // reach the range [inner.YMost(), outer.YMost()] where the item gets
5632 // stuck?
5633 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5634 // needs to be adjusted by the distance to the range, less any other
5635 // sticky ranges that fall between 0 and the range. If the distance is
5636 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5637 // scrolling upwards (decreasing scroll offset) to reach that range,
5638 // which would increase itemBounds.y and make it farther away from the
5639 // top of the scrollport. So in that case the adjustment is -distance.
5640 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5641 // we would be scrolling downwards, itemBounds.y would decrease, and we
5642 // again need to adjust by -distance. If we are already in the range
5643 // then no adjustment is needed and distance is 0 so again using
5644 // -distance works. If the distance is positive, and the item has both
5645 // top and bottom sticky ranges, then the bottom sticky range may fall
5646 // (entirely[1] or partly[2]) between the current scroll position.
5647 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5648 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5649 // In these cases, the item doesn't actually move for that part of the
5650 // distance, so we need to subtract out that bit, which can be computed
5651 // as the positive portion of the range [outer.Y(), inner.Y()].
5652 nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
5653 if (distance > 0) {
5654 distance -= PositivePart(outer.Y(), inner.Y());
5656 topMargin = Some(NSAppUnitsToFloatPixels(
5657 itemBounds.y - scrollPort.y - distance, auPerDevPixel));
5658 // Question: What is the maximum positive ("downward") offset that WR
5659 // will have to apply to this item in order to prevent the item from
5660 // visually moving?
5661 // Answer: Since the item is "sticky" in the range [inner.YMost(),
5662 // outer.YMost()], the maximum offset will be the size of the range, which
5663 // is outer.YMost() - inner.YMost().
5664 vBounds.max =
5665 NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
5666 // Question: how much of an offset has layout already applied to the item?
5667 // Answer: if we are
5668 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5669 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5670 // then layout has already applied some offset to the position of the
5671 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5672 // and |outer.YMost() - inner.YMost()| in case (b).
5673 if (inner.YMost() < 0) {
5674 appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
5675 MOZ_ASSERT(appliedOffset.y > 0);
5678 if (outer.Y() != inner.Y()) {
5679 // Similar logic as in the previous section, but this time we care about
5680 // the distance from itemBounds.YMost() to scrollPort.YMost().
5681 nscoord distance = DistanceToRange(outer.Y(), inner.Y());
5682 if (distance < 0) {
5683 distance += NegativePart(inner.YMost(), outer.YMost());
5685 bottomMargin = Some(NSAppUnitsToFloatPixels(
5686 scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
5687 // And here WR will be moving the item upwards rather than downwards so
5688 // again things are inverted from the previous block.
5689 vBounds.min =
5690 NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
5691 // We can't have appliedOffset be both positive and negative, and the top
5692 // adjustment takes priority. So here we only update appliedOffset.y if
5693 // it wasn't set by the top-sticky case above.
5694 if (appliedOffset.y == 0 && inner.Y() > 0) {
5695 appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
5696 MOZ_ASSERT(appliedOffset.y < 0);
5699 // Same as above, but for the x-axis
5700 if (outer.XMost() != inner.XMost()) {
5701 nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
5702 if (distance > 0) {
5703 distance -= PositivePart(outer.X(), inner.X());
5705 leftMargin = Some(NSAppUnitsToFloatPixels(
5706 itemBounds.x - scrollPort.x - distance, auPerDevPixel));
5707 hBounds.max =
5708 NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
5709 if (inner.XMost() < 0) {
5710 appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
5711 MOZ_ASSERT(appliedOffset.x > 0);
5714 if (outer.X() != inner.X()) {
5715 nscoord distance = DistanceToRange(outer.X(), inner.X());
5716 if (distance < 0) {
5717 distance += NegativePart(inner.XMost(), outer.XMost());
5719 rightMargin = Some(NSAppUnitsToFloatPixels(
5720 scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
5721 hBounds.min =
5722 NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
5723 if (appliedOffset.x == 0 && inner.X() > 0) {
5724 appliedOffset.x = std::max(0, outer.X()) - inner.X();
5725 MOZ_ASSERT(appliedOffset.x < 0);
5729 LayoutDeviceRect bounds =
5730 LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
5731 wr::LayoutVector2D applied = {
5732 NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
5733 NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
5734 wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame(
5735 wr::ToLayoutRect(bounds), topMargin.ptrOr(nullptr),
5736 rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
5737 leftMargin.ptrOr(nullptr), vBounds, hBounds, applied,
5738 wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5739 wr::SpatialKeyKind::Sticky));
5741 saccHelper.emplace(aBuilder, spatialId);
5742 aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId);
5746 wr::StackingContextParams params;
5747 params.clip =
5748 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5749 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
5750 aBuilder, params);
5751 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc,
5752 aManager, aDisplayListBuilder);
5755 if (stickyScrollContainer) {
5756 aManager->CommandBuilder().PopOverrideForASR(mContainerASR);
5759 return true;
5762 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5763 StickyScrollContainer* aStickyScrollContainer, float aAppUnitsPerDevPixel,
5764 float aScaleX, float aScaleY, LayerRectAbsolute& aStickyOuter,
5765 LayerRectAbsolute& aStickyInner) {
5766 nsRectAbsolute outer;
5767 nsRectAbsolute inner;
5768 aStickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5769 aStickyOuter.SetBox(
5770 NSAppUnitsToFloatPixels(outer.X(), aAppUnitsPerDevPixel) * aScaleX,
5771 NSAppUnitsToFloatPixels(outer.Y(), aAppUnitsPerDevPixel) * aScaleY,
5772 NSAppUnitsToFloatPixels(outer.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5773 NSAppUnitsToFloatPixels(outer.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5774 aStickyInner.SetBox(
5775 NSAppUnitsToFloatPixels(inner.X(), aAppUnitsPerDevPixel) * aScaleX,
5776 NSAppUnitsToFloatPixels(inner.Y(), aAppUnitsPerDevPixel) * aScaleY,
5777 NSAppUnitsToFloatPixels(inner.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5778 NSAppUnitsToFloatPixels(inner.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5781 bool nsDisplayStickyPosition::UpdateScrollData(
5782 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5783 bool hasDynamicToolbar = HasDynamicToolbar();
5784 if (aLayerData && hasDynamicToolbar) {
5785 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5786 if (stickyScrollContainer) {
5787 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5788 float cumulativeResolution =
5789 mFrame->PresShell()->GetCumulativeResolution();
5790 LayerRectAbsolute stickyOuter;
5791 LayerRectAbsolute stickyInner;
5792 CalculateLayerScrollRanges(stickyScrollContainer, auPerDevPixel,
5793 cumulativeResolution, cumulativeResolution,
5794 stickyOuter, stickyInner);
5795 aLayerData->SetStickyScrollRangeOuter(stickyOuter);
5796 aLayerData->SetStickyScrollRangeInner(stickyInner);
5798 SideBits sides =
5799 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5800 aLayerData->SetFixedPositionSides(sides);
5802 ScrollableLayerGuid::ViewID scrollId =
5803 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5804 ->GetScrolledFrame()
5805 ->GetContent());
5806 aLayerData->SetStickyPositionScrollContainerId(scrollId);
5809 // Return true if either there is a dynamic toolbar affecting this sticky
5810 // item or the OwnLayer base implementation returns true for some other
5811 // reason.
5812 bool ret = hasDynamicToolbar;
5813 ret |= nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5814 return ret;
5817 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5818 nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
5819 nsIFrame* aScrollFrame, const CompositorHitTestInfo& aHitInfo,
5820 const nsRect& aHitArea)
5821 : nsDisplayWrapList(aBuilder, aScrollFrame),
5822 mScrollFrame(aScrollFrame),
5823 mScrolledFrame(aScrolledFrame),
5824 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5825 mHitInfo(aHitInfo),
5826 mHitArea(aHitArea) {
5827 #ifdef NS_BUILD_REFCNT_LOGGING
5828 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
5829 #endif
5832 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5833 nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) {
5834 ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
5835 mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(),
5836 ToReferenceFrame(), aLayerManager, mScrollParentId,
5837 mScrollFrame->GetSize(), false);
5838 metadata.GetMetrics().SetIsScrollInfoLayer(true);
5839 nsIScrollableFrame* scrollableFrame = mScrollFrame->GetScrollTargetFrame();
5840 if (scrollableFrame) {
5841 aBuilder->AddScrollFrameToNotify(scrollableFrame);
5844 return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
5847 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5848 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5849 if (aLayerData) {
5850 UniquePtr<ScrollMetadata> metadata =
5851 ComputeScrollMetadata(aData->GetBuilder(), aData->GetManager());
5852 MOZ_ASSERT(aData);
5853 MOZ_ASSERT(metadata);
5854 aLayerData->AppendScrollMetadata(*aData, *metadata);
5856 return true;
5859 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5860 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5861 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5862 nsDisplayListBuilder* aDisplayListBuilder) {
5863 ScrollableLayerGuid::ViewID scrollId =
5864 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame->GetContent());
5866 const LayoutDeviceRect devRect = LayoutDeviceRect::FromAppUnits(
5867 mHitArea, mScrollFrame->PresContext()->AppUnitsPerDevPixel());
5869 const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
5871 aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, mHitInfo,
5872 SideBits::eNone);
5874 return true;
5877 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) {
5878 aStream << " (scrollframe " << mScrollFrame << " scrolledFrame "
5879 << mScrolledFrame << ")";
5882 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5883 nsSubDocumentFrame* aSubDocFrame,
5884 nsDisplayList* aList, int32_t aAPD,
5885 int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags)
5886 : nsDisplaySubDocument(aBuilder, aFrame, aSubDocFrame, aList, aFlags),
5887 mAPD(aAPD),
5888 mParentAPD(aParentAPD) {
5889 MOZ_COUNT_CTOR(nsDisplayZoom);
5892 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder,
5893 bool* aSnap) const {
5894 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
5895 *aSnap = false;
5896 return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD);
5899 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5900 HitTestState* aState,
5901 nsTArray<nsIFrame*>* aOutFrames) {
5902 nsRect rect;
5903 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5904 // rect as well instead of possibly rounding the width or height to zero.
5905 if (aRect.width == 1 && aRect.height == 1) {
5906 rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD));
5907 rect.width = rect.height = 1;
5908 } else {
5909 rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
5911 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5914 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5915 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5916 const ActiveScrolledRoot* aActiveScrolledRoot, FrameMetrics::ViewID aViewID)
5917 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5918 mViewID(aViewID) {
5919 MOZ_COUNT_CTOR(nsDisplayAsyncZoom);
5922 #ifdef NS_BUILD_REFCNT_LOGGING
5923 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5924 MOZ_COUNT_DTOR(nsDisplayAsyncZoom);
5926 #endif
5928 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder* aBuilder,
5929 const nsRect& aRect, HitTestState* aState,
5930 nsTArray<nsIFrame*>* aOutFrames) {
5931 #ifdef DEBUG
5932 nsIScrollableFrame* scrollFrame = do_QueryFrame(mFrame);
5933 MOZ_ASSERT(scrollFrame && ViewportUtils::IsZoomedContentRoot(
5934 scrollFrame->GetScrolledFrame()));
5935 #endif
5936 nsRect rect = ViewportUtils::VisualToLayout(aRect, mFrame->PresShell());
5937 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5940 bool nsDisplayAsyncZoom::UpdateScrollData(
5941 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5942 bool ret = nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5943 MOZ_ASSERT(ret);
5944 if (aLayerData) {
5945 aLayerData->SetAsyncZoomContainerId(mViewID);
5947 return ret;
5950 ///////////////////////////////////////////////////
5951 // nsDisplayTransform Implementation
5954 #ifndef DEBUG
5955 static_assert(sizeof(nsDisplayTransform) <= 512,
5956 "nsDisplayTransform has grown");
5957 #endif
5959 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5960 nsIFrame* aFrame, nsDisplayList* aList,
5961 const nsRect& aChildrenBuildingRect)
5962 : nsPaintedDisplayItem(aBuilder, aFrame),
5963 mChildren(aBuilder),
5964 mTransform(Some(Matrix4x4())),
5965 mChildrenBuildingRect(aChildrenBuildingRect),
5966 mPrerenderDecision(PrerenderDecision::No),
5967 mIsTransformSeparator(true),
5968 mHasTransformGetter(false),
5969 mHasAssociatedPerspective(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 MOZ_COUNT_CTOR(nsDisplayTransform);
5987 MOZ_ASSERT(aFrame, "Must have a frame!");
5988 SetReferenceFrameToAncestor(aBuilder);
5989 Init(aBuilder, aList);
5992 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5993 nsIFrame* aFrame, nsDisplayList* aList,
5994 const nsRect& aChildrenBuildingRect,
5995 decltype(WithTransformGetter))
5996 : nsPaintedDisplayItem(aBuilder, aFrame),
5997 mChildren(aBuilder),
5998 mChildrenBuildingRect(aChildrenBuildingRect),
5999 mPrerenderDecision(PrerenderDecision::No),
6000 mIsTransformSeparator(false),
6001 mHasTransformGetter(true),
6002 mHasAssociatedPerspective(false) {
6003 MOZ_COUNT_CTOR(nsDisplayTransform);
6004 MOZ_ASSERT(aFrame, "Must have a frame!");
6005 MOZ_ASSERT(aFrame->GetTransformGetter());
6006 Init(aBuilder, aList);
6009 void nsDisplayTransform::SetReferenceFrameToAncestor(
6010 nsDisplayListBuilder* aBuilder) {
6011 if (mFrame == aBuilder->RootReferenceFrame()) {
6012 return;
6014 // We manually recompute mToReferenceFrame without going through the
6015 // builder, since this won't apply the 'additional offset'. Our
6016 // children will already be painting with that applied, and we don't
6017 // want to include it a second time in our transform. We don't recompute
6018 // our visible/building rects, since those should still include the additional
6019 // offset.
6020 // TODO: Are there are things computed using our ToReferenceFrame that should
6021 // have the additional offset applied? Should we instead just manually remove
6022 // the offset from our transform instead of this more general value?
6023 // Can we instead apply the additional offset to us and not our children, like
6024 // we do for all other offsets (and how reference frames are supposed to
6025 // work)?
6026 nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame);
6027 const nsIFrame* referenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
6028 mToReferenceFrame = mFrame->GetOffsetToCrossDoc(referenceFrame);
6031 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
6032 nsDisplayList* aChildren) {
6033 mChildren.AppendToTop(aChildren);
6034 UpdateBounds(aBuilder);
6037 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
6038 return false;
6041 /* Returns the delta specified by the transform-origin property.
6042 * This is a positive delta, meaning that it indicates the direction to move
6043 * to get from (0, 0) of the frame to the transform origin. This function is
6044 * called off the main thread.
6046 /* static */
6047 Point3D nsDisplayTransform::GetDeltaToTransformOrigin(
6048 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6049 float aAppUnitsPerPixel) {
6050 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6051 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6052 aFrame->Combines3DTransformWithAncestors(),
6053 "Shouldn't get a delta for an untransformed frame!");
6055 if (!aFrame->IsTransformed()) {
6056 return Point3D();
6059 /* For both of the coordinates, if the value of transform is a
6060 * percentage, it's relative to the size of the frame. Otherwise, if it's
6061 * a distance, it's already computed for us!
6063 const nsStyleDisplay* display = aFrame->StyleDisplay();
6065 const StyleTransformOrigin& transformOrigin = display->mTransformOrigin;
6066 CSSPoint origin = nsStyleTransformMatrix::Convert2DPosition(
6067 transformOrigin.horizontal, transformOrigin.vertical, aRefBox);
6069 // Note:
6070 // 1. SVG frames have a reference box that can be (and typically is) offset
6071 // from the TopLeft() of the frame. We need to account for that here.
6072 // 2. If we are using transform-box:content-box in CSS layout, we have the
6073 // offset from TopLeft() of the frame as well.
6074 origin.x += CSSPixel::FromAppUnits(aRefBox.X());
6075 origin.y += CSSPixel::FromAppUnits(aRefBox.Y());
6077 float scale = AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel);
6078 float z = transformOrigin.depth._0;
6079 return Point3D(origin.x * scale, origin.y * scale, z * scale);
6082 /* static */
6083 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
6084 float aAppUnitsPerPixel,
6085 Matrix4x4& aOutMatrix) {
6086 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6087 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6088 aFrame->Combines3DTransformWithAncestors(),
6089 "Shouldn't get a delta for an untransformed frame!");
6090 MOZ_ASSERT(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
6092 if (!aFrame->IsTransformed()) {
6093 return false;
6096 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6097 // correctly yet (similar to the aBoundsOverride case for
6098 // GetResultingTransformMatrix)?
6099 nsIFrame* perspectiveFrame =
6100 aFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6101 if (!perspectiveFrame) {
6102 return false;
6105 /* Grab the values for perspective and perspective-origin (if present) */
6106 const nsStyleDisplay* perspectiveDisplay = perspectiveFrame->StyleDisplay();
6107 if (perspectiveDisplay->mChildPerspective.IsNone()) {
6108 return false;
6111 MOZ_ASSERT(perspectiveDisplay->mChildPerspective.IsLength());
6112 float perspective =
6113 perspectiveDisplay->mChildPerspective.AsLength().ToCSSPixels();
6114 perspective = std::max(1.0f, perspective);
6115 if (perspective < std::numeric_limits<Float>::epsilon()) {
6116 return true;
6119 TransformReferenceBox refBox(perspectiveFrame);
6121 Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition(
6122 perspectiveDisplay->mPerspectiveOrigin.horizontal,
6123 perspectiveDisplay->mPerspectiveOrigin.vertical, refBox,
6124 aAppUnitsPerPixel);
6126 /* GetOffsetTo computes the offset required to move from 0,0 in
6127 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6128 * this, it's faster to compute this way.
6130 nsPoint frameToPerspectiveOffset = -aFrame->GetOffsetTo(perspectiveFrame);
6131 Point frameToPerspectiveGfxOffset(
6132 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.x, aAppUnitsPerPixel),
6133 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.y, aAppUnitsPerPixel));
6135 /* Move the perspective origin to be relative to aFrame, instead of relative
6136 * to the containing block which is how it was specified in the style system.
6138 perspectiveOrigin += frameToPerspectiveGfxOffset;
6140 aOutMatrix._34 =
6141 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective),
6142 aAppUnitsPerPixel);
6144 aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
6145 return true;
6148 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6149 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6150 float aAppUnitsPerPixel)
6151 : mFrame(aFrame),
6152 mTranslate(aFrame->StyleDisplay()->mTranslate),
6153 mRotate(aFrame->StyleDisplay()->mRotate),
6154 mScale(aFrame->StyleDisplay()->mScale),
6155 mTransform(aFrame->StyleDisplay()->mTransform),
6156 mMotion(aFrame->StyleDisplay()->mOffsetPath.IsNone()
6157 ? Nothing()
6158 : MotionPathUtils::ResolveMotionPath(aFrame, aRefBox)),
6159 mToTransformOrigin(
6160 GetDeltaToTransformOrigin(aFrame, aRefBox, aAppUnitsPerPixel)) {}
6162 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6163 * translates from local coordinate space to transform coordinate space, then
6164 * hands it back.
6166 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6167 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6168 float aAppUnitsPerPixel) {
6169 return GetResultingTransformMatrixInternal(aProperties, aRefBox, nsPoint(),
6170 aAppUnitsPerPixel, 0);
6173 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6174 const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel,
6175 uint32_t aFlags) {
6176 TransformReferenceBox refBox(aFrame);
6177 FrameTransformProperties props(aFrame, refBox, aAppUnitsPerPixel);
6178 return GetResultingTransformMatrixInternal(props, refBox, aOrigin,
6179 aAppUnitsPerPixel, aFlags);
6182 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal(
6183 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6184 const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags) {
6185 const nsIFrame* frame = aProperties.mFrame;
6186 NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE),
6187 "Must have a frame to compute perspective!");
6189 // Get the underlying transform matrix:
6191 /* Get the matrix, then change its basis to factor in the origin. */
6192 Matrix4x4 result;
6193 // Call IsSVGTransformed() regardless of the value of
6194 // aProperties.HasTransform(), since we still need any
6195 // potential parentsChildrenOnlyTransform.
6196 Matrix svgTransform, parentsChildrenOnlyTransform;
6197 const bool hasSVGTransforms =
6198 frame && frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
6199 frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform);
6200 bool shouldRound = nsLayoutUtils::ShouldSnapToGrid(frame);
6202 /* Transformed frames always have a transform, or are preserving 3d (and might
6203 * still have perspective!) */
6204 if (aProperties.HasTransform()) {
6205 result = nsStyleTransformMatrix::ReadTransforms(
6206 aProperties.mTranslate, aProperties.mRotate, aProperties.mScale,
6207 aProperties.mMotion.ptrOr(nullptr), aProperties.mTransform, aRefBox,
6208 aAppUnitsPerPixel);
6209 } else if (hasSVGTransforms) {
6210 // Correct the translation components for zoom:
6211 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6212 svgTransform._31 *= pixelsPerCSSPx;
6213 svgTransform._32 *= pixelsPerCSSPx;
6214 result = Matrix4x4::From2D(svgTransform);
6217 // Apply any translation due to 'transform-origin' and/or 'transform-box':
6218 result.ChangeBasis(aProperties.mToTransformOrigin);
6220 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
6221 // an explanation of what children-only transforms are.
6222 const bool parentHasChildrenOnlyTransform =
6223 hasSVGTransforms && !parentsChildrenOnlyTransform.IsIdentity();
6225 if (parentHasChildrenOnlyTransform) {
6226 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6227 parentsChildrenOnlyTransform._31 *= pixelsPerCSSPx;
6228 parentsChildrenOnlyTransform._32 *= pixelsPerCSSPx;
6230 Point3D frameOffset(
6231 NSAppUnitsToFloatPixels(-frame->GetPosition().x, aAppUnitsPerPixel),
6232 NSAppUnitsToFloatPixels(-frame->GetPosition().y, aAppUnitsPerPixel), 0);
6233 Matrix4x4 parentsChildrenOnlyTransform3D =
6234 Matrix4x4::From2D(parentsChildrenOnlyTransform)
6235 .ChangeBasis(frameOffset);
6237 result *= parentsChildrenOnlyTransform3D;
6240 Matrix4x4 perspectiveMatrix;
6241 bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE;
6242 if (hasPerspective) {
6243 if (ComputePerspectiveMatrix(frame, aAppUnitsPerPixel, perspectiveMatrix)) {
6244 result *= perspectiveMatrix;
6248 if ((aFlags & INCLUDE_PRESERVE3D_ANCESTORS) && frame &&
6249 frame->Combines3DTransformWithAncestors()) {
6250 // Include the transform set on our parent
6251 nsIFrame* parentFrame =
6252 frame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6253 NS_ASSERTION(parentFrame && parentFrame->IsTransformed() &&
6254 parentFrame->Extend3DContext(),
6255 "Preserve3D mismatch!");
6256 TransformReferenceBox refBox(parentFrame);
6257 FrameTransformProperties props(parentFrame, refBox, aAppUnitsPerPixel);
6259 uint32_t flags =
6260 aFlags & (INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE);
6262 // If this frame isn't transformed (but we exist for backface-visibility),
6263 // then we're not a reference frame so no offset to origin will be added.
6264 // Otherwise we need to manually translate into our parent's coordinate
6265 // space.
6266 if (frame->IsTransformed()) {
6267 nsLayoutUtils::PostTranslate(result, frame->GetPosition(),
6268 aAppUnitsPerPixel, shouldRound);
6270 Matrix4x4 parent = GetResultingTransformMatrixInternal(
6271 props, refBox, nsPoint(0, 0), aAppUnitsPerPixel, flags);
6272 result = result * parent;
6275 if (aFlags & OFFSET_BY_ORIGIN) {
6276 nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel,
6277 shouldRound);
6280 return result;
6283 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6284 static constexpr nsCSSPropertyIDSet opacitySet =
6285 nsCSSPropertyIDSet::OpacityProperties();
6286 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, opacitySet)) {
6287 return true;
6290 EffectCompositor::SetPerformanceWarning(
6291 mFrame, opacitySet,
6292 AnimationPerformanceWarning(
6293 AnimationPerformanceWarning::Type::OpacityFrameInactive));
6295 return false;
6298 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6299 return mPrerenderDecision != PrerenderDecision::No;
6302 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
6303 nsDisplayListBuilder* aBuilder) {
6304 return StaticPrefs::gfx_omta_background_color();
6307 static bool IsInStickyPositionedSubtree(const nsIFrame* aFrame) {
6308 for (const nsIFrame* frame = aFrame; frame;
6309 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame)) {
6310 if (frame->IsStickyPositioned()) {
6311 return true;
6314 return false;
6317 static bool ShouldUsePartialPrerender(const nsIFrame* aFrame) {
6318 return StaticPrefs::layout_animation_prerender_partial() &&
6319 // Bug 1642547: Support partial prerender for position:sticky elements.
6320 !IsInStickyPositionedSubtree(aFrame);
6323 /* static */
6324 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
6325 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect)
6326 -> PrerenderInfo {
6327 PrerenderInfo result;
6328 // If we are in a preserve-3d tree, and we've disallowed async animations, we
6329 // return No prerender decision directly.
6330 if ((aFrame->Extend3DContext() ||
6331 aFrame->Combines3DTransformWithAncestors()) &&
6332 !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
6333 return result;
6336 // Elements whose transform has been modified recently, or which
6337 // have a compositor-animated transform, can be prerendered. An element
6338 // might have only just had its transform animated in which case
6339 // the ActiveLayerManager may not have been notified yet.
6340 static constexpr nsCSSPropertyIDSet transformSet =
6341 nsCSSPropertyIDSet::TransformLikeProperties();
6342 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame) &&
6343 !EffectCompositor::HasAnimationsForCompositor(
6344 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
6345 EffectCompositor::SetPerformanceWarning(
6346 aFrame, transformSet,
6347 AnimationPerformanceWarning(
6348 AnimationPerformanceWarning::Type::TransformFrameInactive));
6350 // This case happens when we're sure that the frame is not animated and its
6351 // preserve-3d ancestors are not, either. So we don't need to pre-render.
6352 // However, this decision shouldn't affect the decisions for other frames in
6353 // the preserve-3d context. We need this flag to determine whether we should
6354 // block async animations on other frames in the current preserve-3d tree.
6355 result.mHasAnimations = false;
6356 return result;
6359 // We should not allow prerender if any ancestor container element has
6360 // mask/clip-path effects.
6362 // With prerender and async transform animation, we do not need to restyle an
6363 // animated element to respect position changes, since that transform is done
6364 // by layer animation. As a result, the container element is not aware of
6365 // position change of that containing element and loses the chance to update
6366 // the content of mask/clip-path.
6368 // Why do we need to update a mask? This is relative to how we generate a
6369 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
6370 // mask layer, to reduce memory usage, we did not choose the size of the
6371 // masked element as mask size. Instead, we read the union of bounds of all
6372 // children display items by nsDisplayWrapList::GetBounds, which is smaller
6373 // than or equal to the masked element's boundary, and use it as the position
6374 // size of the mask layer. That union bounds is actually affected by the
6375 // geometry of the animated element. To keep the content of mask up to date,
6376 // forbidding of prerender is required.
6377 for (nsIFrame* container =
6378 nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
6379 container;
6380 container = nsLayoutUtils::GetCrossDocParentFrameInProcess(container)) {
6381 const nsStyleSVGReset* svgReset = container->StyleSVGReset();
6382 if (svgReset->HasMask() || svgReset->HasClipPath()) {
6383 return result;
6387 // If the incoming dirty rect already contains the entire overflow area,
6388 // we are already rendering the entire content.
6389 nsRect overflow = aFrame->InkOverflowRectRelativeToSelf();
6390 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
6391 // in cases of non-invertible transforms, so we set `untransformedRect` to
6392 // `aDirtyRect` as an initial value for such cases.
6393 nsRect untransformedDirtyRect = *aDirtyRect;
6394 UntransformRect(*aDirtyRect, overflow, aFrame, &untransformedDirtyRect);
6395 if (untransformedDirtyRect.Contains(overflow)) {
6396 *aDirtyRect = untransformedDirtyRect;
6397 result.mDecision = PrerenderDecision::Full;
6398 return result;
6401 float viewportRatio =
6402 StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
6403 uint32_t absoluteLimitX =
6404 StaticPrefs::layout_animation_prerender_absolute_limit_x();
6405 uint32_t absoluteLimitY =
6406 StaticPrefs::layout_animation_prerender_absolute_limit_y();
6407 nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
6409 float resolution = aFrame->PresShell()->GetCumulativeResolution();
6410 if (resolution < 1.0f) {
6411 refSize.SizeTo(
6412 NSCoordSaturatingNonnegativeMultiply(refSize.width, 1.0f / resolution),
6413 NSCoordSaturatingNonnegativeMultiply(refSize.height,
6414 1.0f / resolution));
6417 // Only prerender if the transformed frame's size is <= a multiple of the
6418 // reference frame size (~viewport), and less than an absolute limit.
6419 // Both the ratio and the absolute limit are configurable.
6420 nscoord maxLength = std::max(nscoord(refSize.width * viewportRatio),
6421 nscoord(refSize.height * viewportRatio));
6422 nsSize relativeLimit(maxLength, maxLength);
6423 nsSize absoluteLimit(
6424 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
6425 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
6426 nsSize maxSize = Min(relativeLimit, absoluteLimit);
6428 const auto transform = nsLayoutUtils::GetTransformToAncestor(
6429 RelativeTo{aFrame},
6430 RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});
6431 const gfxRect transformedBounds = transform.TransformAndClipBounds(
6432 gfxRect(overflow.x, overflow.y, overflow.width, overflow.height),
6433 gfxRect::MaxIntRect());
6434 const nsSize frameSize =
6435 nsSize(transformedBounds.width, transformedBounds.height);
6437 uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
6438 uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
6439 if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
6440 *aDirtyRect = overflow;
6441 result.mDecision = PrerenderDecision::Full;
6442 return result;
6445 if (ShouldUsePartialPrerender(aFrame)) {
6446 *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(
6447 aFrame, untransformedDirtyRect, overflow, maxSize);
6448 result.mDecision = PrerenderDecision::Partial;
6449 return result;
6452 if (frameArea > maxLimitArea) {
6453 uint64_t appUnitsPerPixel = AppUnitsPerCSSPixel();
6454 EffectCompositor::SetPerformanceWarning(
6455 aFrame, transformSet,
6456 AnimationPerformanceWarning(
6457 AnimationPerformanceWarning::Type::ContentTooLargeArea,
6459 int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
6460 int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
6461 }));
6462 } else {
6463 EffectCompositor::SetPerformanceWarning(
6464 aFrame, transformSet,
6465 AnimationPerformanceWarning(
6466 AnimationPerformanceWarning::Type::ContentTooLarge,
6468 nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
6469 nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
6470 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
6471 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
6472 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
6473 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
6474 }));
6477 return result;
6480 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
6481 * visible or hit. */
6482 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix) {
6483 if (aMatrix.IsSingular()) {
6484 return false;
6486 if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) {
6487 return false;
6489 return true;
6492 const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const {
6493 if (mTransform) {
6494 return *mTransform;
6497 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6499 if (mHasTransformGetter) {
6500 mTransform.emplace((mFrame->GetTransformGetter())(mFrame, scale));
6501 Point3D newOrigin =
6502 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
6503 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f);
6504 mTransform->ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
6505 } else if (!mIsTransformSeparator) {
6506 DebugOnly<bool> isReference = mFrame->IsTransformed() ||
6507 mFrame->Combines3DTransformWithAncestors() ||
6508 mFrame->Extend3DContext();
6509 MOZ_ASSERT(isReference);
6510 mTransform.emplace(
6511 GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale,
6512 INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN));
6513 } else {
6514 // Use identity matrix
6515 mTransform.emplace();
6518 return *mTransform;
6521 const Matrix4x4Flagged& nsDisplayTransform::GetInverseTransform() const {
6522 if (mInverseTransform) {
6523 return *mInverseTransform;
6526 MOZ_ASSERT(!GetTransform().IsSingular());
6528 mInverseTransform.emplace(GetTransform().Inverse());
6530 return *mInverseTransform;
6533 Matrix4x4 nsDisplayTransform::GetTransformForRendering(
6534 LayoutDevicePoint* aOutOrigin) const {
6535 if (!mFrame->HasPerspective() || mHasTransformGetter ||
6536 mIsTransformSeparator) {
6537 if (!mHasTransformGetter && !mIsTransformSeparator && aOutOrigin) {
6538 // If aOutOrigin is provided, put the offset to origin into it, because
6539 // we need to keep it separate for webrender. The combination of
6540 // *aOutOrigin and the returned matrix here should always be equivalent
6541 // to what GetTransform() would have returned.
6542 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6543 *aOutOrigin = LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale);
6545 // The rounding behavior should also be the same as GetTransform().
6546 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6547 aOutOrigin->Round();
6549 return GetResultingTransformMatrix(mFrame, nsPoint(0, 0), scale,
6550 INCLUDE_PERSPECTIVE);
6552 return GetTransform().GetMatrix();
6554 MOZ_ASSERT(!mHasTransformGetter);
6556 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6557 // Don't include perspective transform, or the offset to origin, since
6558 // nsDisplayPerspective will handle both of those.
6559 return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
6562 const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
6563 nsDisplayListBuilder* aBuilder) {
6564 MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
6566 if (!IsLeafOf3DContext()) {
6567 return GetTransform().GetMatrix();
6570 if (!mTransformPreserves3D) {
6571 const nsIFrame* establisher; // Establisher of the 3D rendering context.
6572 for (establisher = mFrame;
6573 establisher && establisher->Combines3DTransformWithAncestors();
6574 establisher =
6575 establisher->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
6577 const nsIFrame* establisherReference = aBuilder->FindReferenceFrameFor(
6578 nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher));
6580 nsPoint offset = establisher->GetOffsetToCrossDoc(establisherReference);
6581 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6582 uint32_t flags =
6583 INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN;
6584 mTransformPreserves3D = MakeUnique<Matrix4x4>(
6585 GetResultingTransformMatrix(mFrame, offset, scale, flags));
6588 return *mTransformPreserves3D;
6591 bool nsDisplayTransform::CreateWebRenderCommands(
6592 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
6593 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
6594 nsDisplayListBuilder* aDisplayListBuilder) {
6595 // We want to make sure we don't pollute the transform property in the WR
6596 // stacking context by including the position of this frame (relative to the
6597 // parent reference frame). We need to keep those separate; the position of
6598 // this frame goes into the stacking context bounds while the transform goes
6599 // into the transform.
6600 LayoutDevicePoint position;
6601 Matrix4x4 newTransformMatrix = GetTransformForRendering(&position);
6603 gfx::Matrix4x4* transformForSC = &newTransformMatrix;
6604 if (newTransformMatrix.IsIdentity()) {
6605 // If the transform is an identity transform, strip it out so that WR
6606 // doesn't turn this stacking context into a reference frame, as it
6607 // affects positioning. Bug 1345577 tracks a better fix.
6608 transformForSC = nullptr;
6610 // In ChooseScaleAndSetTransform, we round the offset from the reference
6611 // frame used to adjust the transform, if there is no transform, or it
6612 // is just a translation. We need to do the same here.
6613 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6614 position.Round();
6618 auto key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
6619 wr::SpatialKeyKind::Transform);
6621 // We don't send animations for transform separator display items.
6622 uint64_t animationsId =
6623 mIsTransformSeparator
6625 : AddAnimationsForWebRender(
6626 this, aManager, aDisplayListBuilder,
6627 IsPartialPrerender() ? Some(position) : Nothing());
6628 wr::WrAnimationProperty prop{wr::WrAnimationType::Transform, animationsId,
6629 key};
6631 nsDisplayTransform* deferredTransformItem = nullptr;
6632 if (!mFrame->ChildrenHavePerspective()) {
6633 // If it has perspective, we create a new scroll data via the
6634 // UpdateScrollData call because that scenario is more complex. Otherwise
6635 // we can just stash the transform on the StackingContextHelper and
6636 // apply it to any scroll data that are created inside this
6637 // nsDisplayTransform.
6638 deferredTransformItem = this;
6641 // Determine if we're possibly animated (= would need an active layer in FLB).
6642 bool animated = !mIsTransformSeparator &&
6643 ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
6645 wr::StackingContextParams params;
6646 params.mBoundTransform = &newTransformMatrix;
6647 params.animation = animationsId ? &prop : nullptr;
6649 wr::WrTransformInfo transform_info;
6650 if (transformForSC) {
6651 transform_info.transform = wr::ToLayoutTransform(newTransformMatrix);
6652 transform_info.key = key;
6653 params.mTransformPtr = &transform_info;
6654 } else {
6655 params.mTransformPtr = nullptr;
6658 params.prim_flags = !BackfaceIsHidden()
6659 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
6660 : wr::PrimitiveFlags{0};
6661 params.paired_with_perspective = mHasAssociatedPerspective;
6662 params.mDeferredTransformItem = deferredTransformItem;
6663 params.mAnimated = animated;
6664 // Determine if we would have to rasterize any items in local raster space
6665 // (i.e. disable subpixel AA). We don't always need to rasterize locally even
6666 // if the stacking context is possibly animated (at the cost of potentially
6667 // some false negatives with respect to will-change handling), so we pass in
6668 // this determination separately to accurately match with when FLB would
6669 // normally disable subpixel AA.
6670 params.mRasterizeLocally = animated && Frame()->HasAnimationOfTransform();
6671 params.SetPreserve3D(mFrame->Extend3DContext() && !mIsTransformSeparator);
6672 params.clip =
6673 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
6675 LayoutDeviceSize boundsSize = LayoutDeviceSize::FromAppUnits(
6676 mChildBounds.Size(), mFrame->PresContext()->AppUnitsPerDevPixel());
6678 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
6679 params, LayoutDeviceRect(position, boundsSize));
6681 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6682 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
6683 return true;
6686 bool nsDisplayTransform::UpdateScrollData(
6687 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
6688 if (!mFrame->ChildrenHavePerspective()) {
6689 // This case is handled in CreateWebRenderCommands by stashing the transform
6690 // on the stacking context.
6691 return false;
6693 if (aLayerData) {
6694 aLayerData->SetTransform(GetTransform().GetMatrix());
6695 aLayerData->SetTransformIsPerspective(true);
6697 return true;
6700 bool nsDisplayTransform::ShouldSkipTransform(
6701 nsDisplayListBuilder* aBuilder) const {
6702 return (aBuilder->RootReferenceFrame() == mFrame) &&
6703 aBuilder->IsForGenerateGlyphMask();
6706 void nsDisplayTransform::Collect3DTransformLeaves(
6707 nsDisplayListBuilder* aBuilder, nsTArray<nsDisplayTransform*>& aLeaves) {
6708 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6709 aLeaves.AppendElement(this);
6710 return;
6713 FlattenedDisplayListIterator iter(aBuilder, &mChildren);
6714 while (iter.HasNext()) {
6715 nsDisplayItem* item = iter.GetNextItem();
6716 if (item->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
6717 auto* perspective = static_cast<nsDisplayPerspective*>(item);
6718 if (!perspective->GetChildren()->GetTop()) {
6719 continue;
6721 item = perspective->GetChildren()->GetTop();
6723 if (item->GetType() != DisplayItemType::TYPE_TRANSFORM) {
6724 gfxCriticalError() << "Invalid child item within 3D transform of type: "
6725 << item->Name();
6726 continue;
6728 static_cast<nsDisplayTransform*>(item)->Collect3DTransformLeaves(aBuilder,
6729 aLeaves);
6733 static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
6734 const gfx::Polygon& aPolygon) {
6735 MOZ_ASSERT(!aPolygon.IsEmpty());
6737 RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
6738 const nsTArray<Point4D>& points = aPolygon.GetPoints();
6740 pathBuilder->MoveTo(points[0].As2DPoint());
6742 for (size_t i = 1; i < points.Length(); ++i) {
6743 pathBuilder->LineTo(points[i].As2DPoint());
6746 pathBuilder->Close();
6747 return pathBuilder->Finish();
6750 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6751 nsDisplayListBuilder* aBuilder, nsTArray<TransformPolygon>& aLeaves) {
6752 std::list<TransformPolygon> inputLayers;
6754 nsTArray<nsDisplayTransform*> leaves;
6755 Collect3DTransformLeaves(aBuilder, leaves);
6756 for (nsDisplayTransform* item : leaves) {
6757 auto bounds = LayoutDeviceRect::FromAppUnits(
6758 item->mChildBounds, item->mFrame->PresContext()->AppUnitsPerDevPixel());
6759 Matrix4x4 transform = item->GetAccumulatedPreserved3DTransform(aBuilder);
6761 if (!IsFrameVisible(item->mFrame, transform)) {
6762 continue;
6764 gfx::Polygon polygon =
6765 gfx::Polygon::FromRect(gfx::Rect(bounds.ToUnknownRect()));
6767 polygon.TransformToScreenSpace(transform);
6769 if (polygon.GetPoints().Length() >= 3) {
6770 inputLayers.push_back(TransformPolygon(item, std::move(polygon)));
6774 if (inputLayers.empty()) {
6775 return;
6778 BSPTree<nsDisplayTransform> tree(inputLayers);
6779 nsTArray<TransformPolygon> orderedLayers(tree.GetDrawOrder());
6781 for (TransformPolygon& polygon : orderedLayers) {
6782 Matrix4x4 inverse =
6783 polygon.data->GetAccumulatedPreserved3DTransform(aBuilder).Inverse();
6785 MOZ_ASSERT(polygon.geometry);
6786 polygon.geometry->TransformToLayerSpace(inverse);
6789 aLeaves = std::move(orderedLayers);
6792 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder,
6793 gfxContext* aCtx) {
6794 Paint(aBuilder, aCtx, Nothing());
6797 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
6798 const Maybe<gfx::Polygon>& aPolygon) {
6799 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6800 MOZ_ASSERT(!aPolygon);
6801 nsTArray<TransformPolygon> leaves;
6802 CollectSorted3DTransformLeaves(aBuilder, leaves);
6803 for (TransformPolygon& item : leaves) {
6804 item.data->Paint(aBuilder, aCtx, item.geometry);
6806 return;
6809 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
6810 Matrix4x4 trans = ShouldSkipTransform(aBuilder)
6811 ? Matrix4x4()
6812 : GetAccumulatedPreserved3DTransform(aBuilder);
6813 if (!IsFrameVisible(mFrame, trans)) {
6814 return;
6817 Matrix trans2d;
6818 if (trans.CanDraw2D(&trans2d)) {
6819 aCtx->Multiply(ThebesMatrix(trans2d));
6821 if (aPolygon) {
6822 RefPtr<gfx::Path> path =
6823 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6824 aCtx->GetDrawTarget()->PushClip(path);
6827 GetChildren()->Paint(aBuilder, aCtx,
6828 mFrame->PresContext()->AppUnitsPerDevPixel());
6830 if (aPolygon) {
6831 aCtx->GetDrawTarget()->PopClip();
6833 return;
6836 // TODO: Implement 3d transform handling, including plane splitting and
6837 // sorting. See BasicCompositor.
6838 auto pixelBounds = LayoutDeviceRect::FromAppUnitsToOutside(
6839 mChildBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
6840 RefPtr<DrawTarget> untransformedDT =
6841 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6842 IntSize(pixelBounds.Width(), pixelBounds.Height()),
6843 SurfaceFormat::B8G8R8A8, true);
6844 if (!untransformedDT || !untransformedDT->IsValid()) {
6845 return;
6847 untransformedDT->SetTransform(
6848 Matrix::Translation(-Point(pixelBounds.X(), pixelBounds.Y())));
6850 gfxContext groupTarget(untransformedDT, /* aPreserveTransform */ true);
6852 if (aPolygon) {
6853 RefPtr<gfx::Path> path =
6854 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6855 aCtx->GetDrawTarget()->PushClip(path);
6858 GetChildren()->Paint(aBuilder, &groupTarget,
6859 mFrame->PresContext()->AppUnitsPerDevPixel());
6861 if (aPolygon) {
6862 aCtx->GetDrawTarget()->PopClip();
6865 RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
6867 trans.PreTranslate(pixelBounds.X(), pixelBounds.Y(), 0);
6868 aCtx->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf, trans);
6871 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
6872 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6873 // completely bypass the main thread for this animation, so it is always
6874 // worthwhile.
6875 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6876 // already involved so there is less to be gained.
6877 // Therefore we check that the *post-transform* bounds of this item are
6878 // big enough to justify an active layer.
6879 return EffectCompositor::HasAnimationsForCompositor(
6880 mFrame, DisplayItemType::TYPE_TRANSFORM) ||
6881 (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame));
6884 nsRect nsDisplayTransform::TransformUntransformedBounds(
6885 nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
6886 bool snap;
6887 const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
6888 // GetTransform always operates in dev pixels.
6889 const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
6890 return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
6891 factor);
6895 * Returns the bounds for this transform. The bounds are calculated during
6896 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6898 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
6899 bool* aSnap) const {
6900 *aSnap = false;
6901 return mBounds;
6904 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
6905 MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
6907 /* Some transforms can get empty bounds in 2D, but might get transformed again
6908 * and get non-empty bounds. A simple example of this would be a 180 degree
6909 * rotation getting applied twice.
6910 * We should not depend on transforming bounds level by level.
6912 * This function collects the bounds of this transform and stores it in
6913 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6914 * down and include the bounds of the child transforms.
6915 * The bounds are transformed with the accumulated transformation matrix up to
6916 * the 3D context root coordinate space.
6918 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6919 accTransform.Accumulate(GetTransform().GetMatrix());
6921 // Do not dive into another 3D context.
6922 if (!IsLeafOf3DContext()) {
6923 for (nsDisplayItem* i : *GetChildren()) {
6924 i->DoUpdateBoundsPreserves3D(aBuilder);
6928 /* The child transforms that extend 3D context further will have empty bounds,
6929 * so the untransformed bounds here is the bounds of all the non-preserve-3d
6930 * content under this transform.
6932 const nsRect rect = TransformUntransformedBounds(
6933 aBuilder, accTransform.GetCurrentTransform());
6934 aBuilder->AccumulateRect(rect);
6937 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6938 nsDisplayListBuilder* aBuilder) {
6939 MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
6940 IsTransformSeparator());
6941 // Updating is not going through to child 3D context.
6942 ComputeBounds(aBuilder);
6945 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
6946 UpdateUntransformedBounds(aBuilder);
6948 if (IsTransformSeparator()) {
6949 MOZ_ASSERT(GetTransform().IsIdentity());
6950 mBounds = mChildBounds;
6951 return;
6954 if (mFrame->Extend3DContext()) {
6955 if (!Combines3DTransformWithAncestors()) {
6956 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6957 // collect the bounds from the child transforms.
6958 UpdateBoundsFor3D(aBuilder);
6959 } else {
6960 // With nested 3D transforms, the 2D bounds might not be useful.
6961 mBounds = nsRect();
6964 return;
6967 MOZ_ASSERT(!mFrame->Extend3DContext());
6969 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
6970 // but mix-blend-mode relies on having bounds set. See bug 1556956.
6972 // A stand-alone transform.
6973 mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
6976 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
6977 MOZ_ASSERT(mFrame->Extend3DContext() &&
6978 !mFrame->Combines3DTransformWithAncestors() &&
6979 !IsTransformSeparator());
6981 // Always start updating from an establisher of a 3D rendering context.
6982 nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
6983 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6984 accTransform.StartRoot();
6985 ComputeBounds(aBuilder);
6986 mBounds = aBuilder->GetAccumulatedRect();
6989 void nsDisplayTransform::UpdateUntransformedBounds(
6990 nsDisplayListBuilder* aBuilder) {
6991 mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
6992 aBuilder, mActiveScrolledRoot);
6995 #ifdef DEBUG_HIT
6996 # include <time.h>
6997 #endif
6999 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
7000 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
7001 const nsRect& aRect, HitTestState* aState,
7002 nsTArray<nsIFrame*>* aOutFrames) {
7003 if (aState->mInPreserves3D) {
7004 GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
7005 return;
7008 /* Here's how this works:
7009 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
7010 * anything).
7011 * 2. Invert the matrix.
7012 * 3. Use it to transform the rect into the correct space.
7013 * 4. Pass that rect down through to the list's version of HitTest.
7015 // GetTransform always operates in dev pixels.
7016 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7017 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7019 if (!IsFrameVisible(mFrame, matrix)) {
7020 return;
7023 const bool oldHitOccludingItem = aState->mHitOccludingItem;
7025 /* We want to go from transformed-space to regular space.
7026 * Thus we have to invert the matrix, which normally does
7027 * the reverse operation (e.g. regular->transformed)
7030 /* Now, apply the transform and pass it down the channel. */
7031 matrix.Invert();
7032 nsRect resultingRect;
7033 // Magic width/height indicating we're hit testing a point, not a rect
7034 const bool testingPoint = aRect.width == 1 && aRect.height == 1;
7035 if (testingPoint) {
7036 Point4D point =
7037 matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
7038 NSAppUnitsToFloatPixels(aRect.y, factor)));
7039 if (!point.HasPositiveWCoord()) {
7040 return;
7043 Point point2d = point.As2DPoint();
7045 resultingRect =
7046 nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
7047 NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1);
7049 } else {
7050 Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
7051 NSAppUnitsToFloatPixels(aRect.y, factor),
7052 NSAppUnitsToFloatPixels(aRect.width, factor),
7053 NSAppUnitsToFloatPixels(aRect.height, factor));
7055 Rect childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds.x, factor),
7056 NSAppUnitsToFloatPixels(mChildBounds.y, factor),
7057 NSAppUnitsToFloatPixels(mChildBounds.width, factor),
7058 NSAppUnitsToFloatPixels(mChildBounds.height, factor));
7060 Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
7062 resultingRect =
7063 nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
7064 NSFloatPixelsToAppUnits(float(rect.Y()), factor),
7065 NSFloatPixelsToAppUnits(float(rect.Width()), factor),
7066 NSFloatPixelsToAppUnits(float(rect.Height()), factor));
7069 if (resultingRect.IsEmpty()) {
7070 return;
7073 #ifdef DEBUG_HIT
7074 printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
7075 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(),
7076 resultingRect.Y());
7077 uint32_t originalFrameCount = aOutFrames.Length();
7078 #endif
7080 GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
7082 if (aState->mHitOccludingItem && !testingPoint &&
7083 !mChildBounds.Contains(aRect)) {
7084 MOZ_ASSERT(aBuilder->HitTestIsForVisibility());
7085 // We're hit-testing a rect that's bigger than our child bounds, but
7086 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so
7087 // we can't stop hit-testing altogether.
7089 // FIXME(emilio): I think this means that theoretically we might include
7090 // some frames fully behind other transformed-but-opaque frames? Then again
7091 // that's our pre-existing behavior for other untransformed content that
7092 // doesn't fill the whole rect. To be fully correct I think we'd need proper
7093 // "known occluded region" tracking, but that might be overkill for our
7094 // purposes here.
7095 aState->mHitOccludingItem = oldHitOccludingItem;
7098 #ifdef DEBUG_HIT
7099 if (originalFrameCount != aOutFrames.Length())
7100 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7101 dynamic_cast<void*>(aOutFrames.ElementAt(0)));
7102 printf("=== end of hit test ===\n");
7103 #endif
7106 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
7107 const nsPoint& aPoint) {
7108 // GetTransform always operates in dev pixels.
7109 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7110 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7112 NS_ASSERTION(IsFrameVisible(mFrame, matrix),
7113 "We can't have hit a frame that isn't visible!");
7115 Matrix4x4 inverse = matrix;
7116 inverse.Invert();
7117 Point4D point =
7118 inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
7119 NSAppUnitsToFloatPixels(aPoint.y, factor)));
7121 Point point2d = point.As2DPoint();
7123 Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
7124 return transformed.z;
7127 /* The transform is opaque iff the transform consists solely of scales and
7128 * translations and if the underlying content is opaque. Thus if the transform
7129 * is of the form
7131 * |a c e|
7132 * |b d f|
7133 * |0 0 1|
7135 * We need b and c to be zero.
7137 * We also need to check whether the underlying opaque content completely fills
7138 * our visible rect. We use UntransformRect which expands to the axis-aligned
7139 * bounding rect, but that's OK since if
7140 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7141 * certainly contains the actual (non-axis-aligned) untransformed rect.
7143 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7144 bool* aSnap) const {
7145 *aSnap = false;
7147 nsRect untransformedVisible;
7148 if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
7149 return nsRegion();
7152 const Matrix4x4Flagged& matrix = GetTransform();
7153 Matrix matrix2d;
7154 if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
7155 return nsRegion();
7158 nsRegion result;
7160 bool tmpSnap;
7161 const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
7162 const nsRegion opaque =
7163 ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
7165 if (opaque.Contains(untransformedVisible)) {
7166 result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
7168 return result;
7171 nsRect nsDisplayTransform::GetComponentAlphaBounds(
7172 nsDisplayListBuilder* aBuilder) const {
7173 if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
7174 return nsRect();
7177 bool snap;
7178 return GetBounds(aBuilder, &snap);
7181 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7182 * the smallest rectangle (in app space) containing the transformed image of
7183 * that rectangle. That is, it takes the four corners of the rectangle,
7184 * transforms them according to the matrix associated with the specified frame,
7185 * then returns the smallest rectangle containing the four transformed points.
7187 * @param aUntransformedBounds The rectangle (in app units) to transform.
7188 * @param aFrame The frame whose transformation should be applied.
7189 * @param aOrigin The delta from the frame origin to the coordinate space origin
7190 * @return The smallest rectangle containing the image of the transformed
7191 * rectangle.
7193 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds,
7194 const nsIFrame* aFrame,
7195 TransformReferenceBox& aRefBox) {
7196 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7198 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7200 FrameTransformProperties props(aFrame, aRefBox, factor);
7201 return nsLayoutUtils::MatrixTransformRect(
7202 aUntransformedBounds,
7203 GetResultingTransformMatrixInternal(props, aRefBox, nsPoint(), factor,
7204 kTransformRectFlags),
7205 factor);
7208 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7209 const nsRect& aChildBounds,
7210 const nsIFrame* aFrame,
7211 nsRect* aOutRect) {
7212 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7214 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7215 Matrix4x4 transform = GetResultingTransformMatrix(aFrame, nsPoint(), factor,
7216 kTransformRectFlags);
7217 return UntransformRect(aTransformedBounds, aChildBounds, transform, factor,
7218 aOutRect);
7221 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7222 const nsRect& aChildBounds,
7223 const Matrix4x4& aMatrix,
7224 float aAppUnitsPerPixel,
7225 nsRect* aOutRect) {
7226 if (aMatrix.IsSingular()) {
7227 return false;
7230 RectDouble result(
7231 NSAppUnitsToFloatPixels(aTransformedBounds.x, aAppUnitsPerPixel),
7232 NSAppUnitsToFloatPixels(aTransformedBounds.y, aAppUnitsPerPixel),
7233 NSAppUnitsToFloatPixels(aTransformedBounds.width, aAppUnitsPerPixel),
7234 NSAppUnitsToFloatPixels(aTransformedBounds.height, aAppUnitsPerPixel));
7236 RectDouble childGfxBounds(
7237 NSAppUnitsToFloatPixels(aChildBounds.x, aAppUnitsPerPixel),
7238 NSAppUnitsToFloatPixels(aChildBounds.y, aAppUnitsPerPixel),
7239 NSAppUnitsToFloatPixels(aChildBounds.width, aAppUnitsPerPixel),
7240 NSAppUnitsToFloatPixels(aChildBounds.height, aAppUnitsPerPixel));
7242 result = aMatrix.Inverse().ProjectRectBounds(result, childGfxBounds);
7243 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result),
7244 aAppUnitsPerPixel);
7245 return true;
7248 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder,
7249 const nsRect& aRect,
7250 nsRect* aOutRect) const {
7251 if (GetTransform().IsSingular()) {
7252 return false;
7255 // GetTransform always operates in dev pixels.
7256 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7257 RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor),
7258 NSAppUnitsToFloatPixels(aRect.y, factor),
7259 NSAppUnitsToFloatPixels(aRect.width, factor),
7260 NSAppUnitsToFloatPixels(aRect.height, factor));
7262 bool snap;
7263 nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
7264 RectDouble childGfxBounds(
7265 NSAppUnitsToFloatPixels(childBounds.x, factor),
7266 NSAppUnitsToFloatPixels(childBounds.y, factor),
7267 NSAppUnitsToFloatPixels(childBounds.width, factor),
7268 NSAppUnitsToFloatPixels(childBounds.height, factor));
7270 /* We want to untransform the matrix, so invert the transformation first! */
7271 result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds);
7273 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
7275 return true;
7278 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) {
7279 aStream << GetTransform().GetMatrix();
7280 if (IsTransformSeparator()) {
7281 aStream << " transform-separator";
7283 if (IsLeafOf3DContext()) {
7284 aStream << " 3d-context-leaf";
7286 if (mFrame->Extend3DContext()) {
7287 aStream << " extends-3d-context";
7289 if (mFrame->Combines3DTransformWithAncestors()) {
7290 aStream << " combines-3d-with-ancestors";
7293 aStream << " prerender(";
7294 switch (mPrerenderDecision) {
7295 case PrerenderDecision::No:
7296 aStream << "no";
7297 break;
7298 case PrerenderDecision::Partial:
7299 aStream << "partial";
7300 break;
7301 case PrerenderDecision::Full:
7302 aStream << "full";
7303 break;
7305 aStream << ")";
7306 aStream << " childrenBuildingRect" << mChildrenBuildingRect;
7309 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
7310 nsIFrame* aFrame,
7311 nsDisplayList* aList)
7312 : nsPaintedDisplayItem(aBuilder, aFrame), mList(aBuilder) {
7313 mList.AppendToTop(aList);
7314 MOZ_ASSERT(mList.Length() == 1);
7315 MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
7318 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder,
7319 gfxContext* aCtx) {
7320 // Just directly recurse into children, since we'll include the persepctive
7321 // value in any nsDisplayTransform children.
7322 GetChildren()->Paint(aBuilder, aCtx,
7323 mFrame->PresContext()->AppUnitsPerDevPixel());
7326 nsRegion nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7327 bool* aSnap) const {
7328 if (!GetChildren()->GetTop()) {
7329 *aSnap = false;
7330 return nsRegion();
7333 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder, aSnap);
7336 bool nsDisplayPerspective::CreateWebRenderCommands(
7337 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7338 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7339 nsDisplayListBuilder* aDisplayListBuilder) {
7340 float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
7341 Matrix4x4 perspectiveMatrix;
7342 DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
7343 mFrame, appUnitsPerPixel, perspectiveMatrix);
7344 MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
7347 * ClipListToRange can remove our child after we were created.
7349 if (!GetChildren()->GetTop()) {
7350 return false;
7354 * The resulting matrix is still in the coordinate space of the transformed
7355 * frame. Append a translation to the reference frame coordinates.
7357 nsDisplayTransform* transform =
7358 static_cast<nsDisplayTransform*>(GetChildren()->GetTop());
7360 Point3D newOrigin =
7361 Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
7362 appUnitsPerPixel),
7363 NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
7364 appUnitsPerPixel),
7365 0.0f);
7366 Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
7368 perspectiveMatrix.PostTranslate(roundedOrigin);
7370 nsIFrame* perspectiveFrame =
7371 mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
7373 // Passing true here is always correct, since perspective always combines
7374 // transforms with the descendants. However that'd make WR do a lot of work
7375 // that it doesn't really need to do if there aren't other transforms forming
7376 // part of the 3D context.
7378 // WR knows how to treat perspective in that case, so the only thing we need
7379 // to do is to ensure we pass true when we're involved in a 3d context in any
7380 // other way via the transform-style property on either the transformed frame
7381 // or the perspective frame in order to not confuse WR's preserve-3d code in
7382 // very awful ways.
7383 bool preserve3D =
7384 mFrame->Extend3DContext() || perspectiveFrame->Extend3DContext();
7386 wr::StackingContextParams params;
7388 wr::WrTransformInfo transform_info;
7389 transform_info.transform = wr::ToLayoutTransform(perspectiveMatrix);
7390 transform_info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
7391 wr::SpatialKeyKind::Perspective);
7392 params.mTransformPtr = &transform_info;
7394 params.reference_frame_kind = wr::WrReferenceFrameKind::Perspective;
7395 params.prim_flags = !BackfaceIsHidden()
7396 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7397 : wr::PrimitiveFlags{0};
7398 params.SetPreserve3D(preserve3D);
7399 params.clip =
7400 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
7402 Maybe<uint64_t> scrollingRelativeTo;
7403 for (const auto* asr = GetActiveScrolledRoot(); asr; asr = asr->mParent) {
7404 // In OOP documents, the root scrollable frame of the in-process root
7405 // document is always active, so using IsAncestorFrameCrossDocInProcess
7406 // should be fine here.
7407 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7408 asr->mScrollableFrame->GetScrolledFrame(), perspectiveFrame)) {
7409 scrollingRelativeTo.emplace(asr->GetViewId());
7410 break;
7414 // We put the perspective reference frame wrapping the transformed frame,
7415 // even though there may be arbitrarily nested scroll frames in between.
7417 // We need to know how many ancestor scroll-frames are we nested in, in order
7418 // for the async scrolling code in WebRender to calculate the right
7419 // transformation for the perspective contents.
7420 params.scrolling_relative_to = scrollingRelativeTo.ptrOr(nullptr);
7422 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
7423 params);
7425 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7426 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
7428 return true;
7431 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
7432 nsTextFrame* aFrame)
7433 : nsPaintedDisplayItem(aBuilder, aFrame),
7434 mVisIStartEdge(0),
7435 mVisIEndEdge(0) {
7436 MOZ_COUNT_CTOR(nsDisplayText);
7437 mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7438 // Bug 748228
7439 mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
7440 mVisibleRect = aBuilder->GetVisibleRect() +
7441 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
7444 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager,
7445 nsDisplayListBuilder* aBuilder) const {
7446 auto* f = static_cast<nsTextFrame*>(mFrame);
7448 if (f->IsSelected()) {
7449 return false;
7452 const nsStyleText* textStyle = f->StyleText();
7453 if (textStyle->HasTextShadow()) {
7454 return false;
7457 nsTextFrame::TextDecorations decorations;
7458 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7459 decorations);
7460 return !decorations.HasDecorationLines();
7463 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
7464 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
7465 // We don't pass mVisibleRect here, since this can be called from within
7466 // the WebRender fallback painting path, and we don't want to issue
7467 // recorded commands that are dependent on the visible/building rect.
7468 RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
7470 auto* textFrame = static_cast<nsTextFrame*>(mFrame);
7471 LCPTextFrameHelper::MaybeUnionTextFrame(textFrame,
7472 mBounds - ToReferenceFrame());
7475 bool nsDisplayText::CreateWebRenderCommands(
7476 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7477 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7478 nsDisplayListBuilder* aDisplayListBuilder) {
7479 auto* f = static_cast<nsTextFrame*>(mFrame);
7480 auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
7482 nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
7483 // Bug 748228
7484 bounds.Inflate(appUnitsPerDevPixel);
7486 if (bounds.IsEmpty()) {
7487 return true;
7490 // For large font sizes, punt to a blob image, to avoid the blurry rendering
7491 // that results from WR clamping the glyph size used for rasterization.
7493 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7495 // This is not strictly accurate, as final used font sizes might not be the
7496 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7497 // altering the used size of the font actually used).
7498 // It also fails to consider how transforms might affect the device-font-size
7499 // that webrender uses (and clamps).
7500 // But it should be near enough for practical purposes; the limitations just
7501 // mean we might sometimes end up with webrender still applying some bitmap
7502 // scaling, or bail out when we didn't really need to.
7503 constexpr float kWebRenderFontSizeLimit = 320.0;
7504 f->EnsureTextRun(nsTextFrame::eInflated);
7505 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7506 if (textRun &&
7507 textRun->GetFontGroup()->GetStyle()->size > kWebRenderFontSizeLimit) {
7508 return false;
7511 gfx::Point deviceOffset =
7512 LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
7513 .ToUnknownPoint();
7515 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7516 // frames) lets us early reject a bunch of things.
7517 nsRect visible = mVisibleRect;
7519 // Add the "source rect" area from which the given shadows could intersect
7520 // with mVisibleRect, and which therefore needs to included in the paint
7521 // operation, to the `visible` rect that we will use to limit the bounds of
7522 // what we send to the renderer.
7523 auto addShadowSourceToVisible = [&](Span<const StyleSimpleShadow> aShadows) {
7524 for (const auto& shadow : aShadows) {
7525 nsRect sourceRect = mVisibleRect;
7526 // Negate the offsets, because we're looking for the "source" rect that
7527 // could cast a shadow into the visible rect, rather than a "target" area
7528 // onto which the visible rect would cast a shadow.
7529 sourceRect.MoveBy(-shadow.horizontal.ToAppUnits(),
7530 -shadow.vertical.ToAppUnits());
7531 // Inflate to account for the shadow blur.
7532 sourceRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
7533 shadow.blur.ToAppUnits(), appUnitsPerDevPixel));
7534 visible.OrWith(sourceRect);
7538 // Shadows can translate things back into view, so we enlarge the notional
7539 // "visible" rect to ensure we don't skip painting relevant parts that might
7540 // cast a shadow within the visible area.
7541 addShadowSourceToVisible(f->StyleText()->mTextShadow.AsSpan());
7543 // Similarly for shadows that may be cast by ::selection.
7544 if (f->IsSelected()) {
7545 nsTextPaintStyle textPaint(f);
7546 Span<const StyleSimpleShadow> shadows;
7547 f->GetSelectionTextShadow(SelectionType::eNormal, textPaint, &shadows);
7548 addShadowSourceToVisible(shadows);
7551 // Inflate a little extra to allow for potential antialiasing "blur".
7552 visible.Inflate(3 * appUnitsPerDevPixel);
7553 bounds = bounds.Intersect(visible);
7555 gfxContext* textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager,
7556 this, bounds, deviceOffset);
7558 LCPTextFrameHelper::MaybeUnionTextFrame(f, bounds - ToReferenceFrame());
7560 aBuilder.StartGroup(this);
7562 RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
7563 aBuilder.GetInheritedOpacity(), true);
7564 const bool result = textDrawer->GetTextDrawer()->Finish();
7566 if (result) {
7567 aBuilder.FinishGroup();
7568 } else {
7569 aBuilder.CancelGroup(true);
7572 return result;
7575 void nsDisplayText::RenderToContext(gfxContext* aCtx,
7576 nsDisplayListBuilder* aBuilder,
7577 const nsRect& aVisibleRect, float aOpacity,
7578 bool aIsRecording) {
7579 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7581 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7582 // antialiased pixels beyond the measured text extents.
7583 // This is temporary until we do this in the actual calculation of text
7584 // extents.
7585 auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
7586 LayoutDeviceRect extraVisible =
7587 LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D);
7588 extraVisible.Inflate(1);
7590 gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width,
7591 extraVisible.height);
7592 pixelVisible.Inflate(2);
7593 pixelVisible.RoundOut();
7595 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7596 if (!aBuilder->IsForGenerateGlyphMask() && !aIsRecording) {
7597 autoSaveClip.Clip(pixelVisible);
7600 NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
7601 NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
7603 gfxContextMatrixAutoSaveRestore matrixSR;
7605 nsPoint framePt = ToReferenceFrame();
7606 if (f->Style()->IsTextCombined()) {
7607 float scaleFactor = nsTextFrame::GetTextCombineScaleFactor(f);
7608 if (scaleFactor != 1.0f) {
7609 if (auto* textDrawer = aCtx->GetTextDrawer()) {
7610 // WebRender doesn't support scaling text like this yet
7611 textDrawer->FoundUnsupportedFeature();
7612 return;
7614 matrixSR.SetContext(aCtx);
7615 // Setup matrix to compress text for text-combine-upright if
7616 // necessary. This is done here because we want selection be
7617 // compressed at the same time as text.
7618 gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
7619 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7620 if (textRun && textRun->IsRightToLeft()) {
7621 pt.x += gfxFloat(f->GetSize().width) / A2D;
7623 gfxMatrix mat = aCtx->CurrentMatrixDouble()
7624 .PreTranslate(pt)
7625 .PreScale(scaleFactor, 1.0)
7626 .PreTranslate(-pt);
7627 aCtx->SetMatrixDouble(mat);
7630 nsTextFrame::PaintTextParams params(aCtx);
7631 params.framePt = gfx::Point(framePt.x, framePt.y);
7632 params.dirtyRect = extraVisible;
7634 if (aBuilder->IsForGenerateGlyphMask()) {
7635 params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
7636 } else {
7637 params.state = nsTextFrame::PaintTextParams::PaintText;
7640 f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
7641 f->IsSelected(), aOpacity);
7644 // This could go to nsDisplayListInvalidation.h, but
7645 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7646 // would produce circular dependencies.
7647 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
7648 public:
7649 nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
7650 : nsDisplayItemGenericGeometry(aItem, aBuilder),
7651 mVisIStartEdge(aItem->VisIStartEdge()),
7652 mVisIEndEdge(aItem->VisIEndEdge()) {
7653 nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
7654 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7655 mDecorations);
7659 * We store the computed text decorations here since they are
7660 * computed using style data from parent frames. Any changes to these
7661 * styles will only invalidate the parent frame and not this frame.
7663 nsTextFrame::TextDecorations mDecorations;
7664 nscoord mVisIStartEdge;
7665 nscoord mVisIEndEdge;
7668 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry(
7669 nsDisplayListBuilder* aBuilder) {
7670 return new nsDisplayTextGeometry(this, aBuilder);
7673 void nsDisplayText::ComputeInvalidationRegion(
7674 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7675 nsRegion* aInvalidRegion) const {
7676 const nsDisplayTextGeometry* geometry =
7677 static_cast<const nsDisplayTextGeometry*>(aGeometry);
7678 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7680 nsTextFrame::TextDecorations decorations;
7681 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7682 decorations);
7684 bool snap;
7685 const nsRect& newRect = geometry->mBounds;
7686 nsRect oldRect = GetBounds(aBuilder, &snap);
7687 if (decorations != geometry->mDecorations ||
7688 mVisIStartEdge != geometry->mVisIStartEdge ||
7689 mVisIEndEdge != geometry->mVisIEndEdge ||
7690 !oldRect.IsEqualInterior(newRect) ||
7691 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
7692 aInvalidRegion->Or(oldRect, newRect);
7696 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) {
7697 #ifdef DEBUG
7698 aStream << " (\"";
7700 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7701 nsCString buf;
7702 f->ToCString(buf);
7704 aStream << buf.get() << "\")";
7705 #endif
7708 nsDisplayEffectsBase::nsDisplayEffectsBase(
7709 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7710 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
7711 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
7712 aClearClipChain) {
7713 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7716 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
7717 nsIFrame* aFrame,
7718 nsDisplayList* aList)
7719 : nsDisplayWrapList(aBuilder, aFrame, aList) {
7720 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7723 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7724 bool* aSnap) const {
7725 *aSnap = false;
7726 return nsRegion();
7729 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder* aBuilder,
7730 const nsRect& aRect, HitTestState* aState,
7731 nsTArray<nsIFrame*>* aOutFrames) {
7732 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
7733 if (SVGIntegrationUtils::HitTestFrameForEffects(
7734 mFrame, rectCenter - ToReferenceFrame())) {
7735 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
7739 gfxRect nsDisplayEffectsBase::BBoxInUserSpace() const {
7740 return SVGUtils::GetBBox(mFrame);
7743 gfxPoint nsDisplayEffectsBase::UserSpaceOffset() const {
7744 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
7747 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7748 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7749 nsRegion* aInvalidRegion) const {
7750 const auto* geometry =
7751 static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
7752 bool snap;
7753 nsRect bounds = GetBounds(aBuilder, &snap);
7754 if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
7755 geometry->mUserSpaceOffset != UserSpaceOffset() ||
7756 !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
7757 // Filter and mask output can depend on the location of the frame's user
7758 // space and on the frame's BBox. We need to invalidate if either of these
7759 // change relative to the reference frame.
7760 // Invalidations from our inactive layer manager are not enough to catch
7761 // some of these cases because filters can produce output even if there's
7762 // nothing in the filter input.
7763 aInvalidRegion->Or(bounds, geometry->mBounds);
7767 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7768 if (mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
7769 ISVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame);
7770 if (!svgFrame) {
7771 return false;
7773 if (auto* svgElement = SVGElement::FromNode(mFrame->GetContent())) {
7774 // The SVG spec says only to draw filters if the element
7775 // has valid dimensions.
7776 return svgElement->HasValidDimensions();
7778 return false;
7781 return true;
7784 using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams;
7786 static void ComputeMaskGeometry(PaintFramesParams& aParams) {
7787 // Properties are added lazily and may have been removed by a restyle, so
7788 // make sure all applicable ones are set again.
7789 nsIFrame* firstFrame =
7790 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
7792 const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
7794 nsTArray<SVGMaskFrame*> maskFrames;
7795 // XXX check return value?
7796 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
7798 if (maskFrames.Length() == 0) {
7799 return;
7802 gfxContext& ctx = aParams.ctx;
7803 nsIFrame* frame = aParams.frame;
7805 nsPoint offsetToUserSpace =
7806 nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame);
7808 auto cssToDevScale = frame->PresContext()->CSSToDevPixelScale();
7809 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
7811 gfxPoint devPixelOffsetToUserSpace =
7812 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, appUnitsPerDevPixel);
7814 gfxContextMatrixAutoSaveRestore matSR(&ctx);
7815 ctx.SetMatrixDouble(
7816 ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace));
7818 // Convert boaderArea and dirtyRect to user space.
7819 nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
7820 nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
7822 // Union all mask layer rectangles in user space.
7823 LayoutDeviceRect maskInUserSpace;
7824 for (size_t i = 0; i < maskFrames.Length(); i++) {
7825 SVGMaskFrame* maskFrame = maskFrames[i];
7826 LayoutDeviceRect currentMaskSurfaceRect;
7828 if (maskFrame) {
7829 auto rect = maskFrame->GetMaskArea(aParams.frame);
7830 currentMaskSurfaceRect =
7831 CSSRect::FromUnknownRect(ToRect(rect)) * cssToDevScale;
7832 } else {
7833 nsCSSRendering::ImageLayerClipState clipState;
7834 nsCSSRendering::GetImageLayerClip(
7835 svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(),
7836 userSpaceBorderArea, userSpaceDirtyRect,
7837 /* aWillPaintBorder = */ false, appUnitsPerDevPixel, &clipState);
7838 currentMaskSurfaceRect = LayoutDeviceRect::FromUnknownRect(
7839 ToRect(clipState.mDirtyRectInDevPx));
7842 maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
7845 if (!maskInUserSpace.IsEmpty()) {
7846 aParams.maskRect = Some(maskInUserSpace);
7847 } else {
7848 aParams.maskRect = Nothing();
7852 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7853 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7854 const ActiveScrolledRoot* aActiveScrolledRoot, bool aWrapsBackdropFilter)
7855 : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
7856 mWrapsBackdropFilter(aWrapsBackdropFilter) {
7857 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
7859 nsPresContext* presContext = mFrame->PresContext();
7860 uint32_t flags =
7861 aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
7862 const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
7863 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7864 const auto& layer = svgReset->mMask.mLayers[i];
7865 if (!layer.mImage.IsResolved()) {
7866 continue;
7868 const nsRect& borderArea = mFrame->GetRectRelativeToSelf();
7869 // NOTE(emilio): We only care about the dest rect so we don't bother
7870 // computing a clip.
7871 bool isTransformedFixed = false;
7872 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
7873 presContext, aFrame, flags, borderArea, borderArea, layer,
7874 &isTransformedFixed);
7875 mDestRects.AppendElement(state.mDestArea);
7879 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) {
7880 // Do not merge items for box-decoration-break:clone elements,
7881 // since each box should have its own mask in that case.
7882 if (aFrame->StyleBorder()->mBoxDecorationBreak ==
7883 StyleBoxDecorationBreak::Clone) {
7884 return false;
7887 // Do not merge if either frame has a mask. Continuation frames should apply
7888 // the mask independently (just like nsDisplayBackgroundImage).
7889 if (aFrame->StyleSVGReset()->HasMask()) {
7890 return false;
7893 return true;
7896 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem* aItem) const {
7897 // Items for the same content element should be merged into a single
7898 // compositing group.
7899 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
7900 !HasSameContent(aItem)) {
7901 return false;
7904 return CanMergeDisplayMaskFrame(mFrame) &&
7905 CanMergeDisplayMaskFrame(aItem->Frame());
7908 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7909 if (!ValidateSVGFrame()) {
7910 return false;
7913 return SVGUtils::DetermineMaskUsage(mFrame, false).UsingMaskOrClipPath();
7916 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder,
7917 gfxContext* aMaskContext,
7918 bool aHandleOpacity,
7919 bool* aMaskPainted) {
7920 MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
7922 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7923 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7924 PaintFramesParams params(*aMaskContext, mFrame, mBounds, borderArea, aBuilder,
7925 aHandleOpacity, imgParams);
7926 ComputeMaskGeometry(params);
7927 bool maskIsComplete = false;
7928 bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete);
7929 if (aMaskPainted) {
7930 *aMaskPainted = painted;
7933 return maskIsComplete &&
7934 (imgParams.result == ImgDrawResult::SUCCESS ||
7935 imgParams.result == ImgDrawResult::SUCCESS_NOT_COMPLETE ||
7936 imgParams.result == ImgDrawResult::WRONG_SIZE);
7939 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7940 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7941 nsRegion* aInvalidRegion) const {
7942 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
7943 aInvalidRegion);
7945 const auto* geometry =
7946 static_cast<const nsDisplayMasksAndClipPathsGeometry*>(aGeometry);
7947 bool snap;
7948 nsRect bounds = GetBounds(aBuilder, &snap);
7950 if (mDestRects.Length() != geometry->mDestRects.Length()) {
7951 aInvalidRegion->Or(bounds, geometry->mBounds);
7952 } else {
7953 for (size_t i = 0; i < mDestRects.Length(); i++) {
7954 if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) {
7955 aInvalidRegion->Or(bounds, geometry->mBounds);
7956 break;
7962 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7963 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
7964 const std::function<void()>& aPaintChildren) {
7965 // Clip the drawing target by mVisibleRect, which contains the visible
7966 // region of the target frame and its out-of-flow and inflow descendants.
7967 Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx),
7968 mFrame->PresContext()->AppUnitsPerDevPixel());
7969 bounds.RoundOut();
7970 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7971 autoSaveClip.Clip(bounds);
7973 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7974 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7975 PaintFramesParams params(*aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
7976 borderArea, aBuilder, false, imgParams);
7978 ComputeMaskGeometry(params);
7980 SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren);
7983 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder* aBuilder,
7984 gfxContext* aCtx) {
7985 if (!IsValidMask()) {
7986 return;
7988 PaintWithContentsPaintCallback(aBuilder, aCtx, [&] {
7989 GetChildren()->Paint(aBuilder, aCtx,
7990 mFrame->PresContext()->AppUnitsPerDevPixel());
7994 static Maybe<wr::WrClipChainId> CreateSimpleClipRegion(
7995 const nsDisplayMasksAndClipPaths& aDisplayItem,
7996 wr::DisplayListBuilder& aBuilder) {
7997 nsIFrame* frame = aDisplayItem.Frame();
7998 const auto* style = frame->StyleSVGReset();
7999 MOZ_ASSERT(style->HasClipPath() || style->HasMask());
8000 if (!SVGUtils::DetermineMaskUsage(frame, false).IsSimpleClipShape()) {
8001 return Nothing();
8004 const auto& clipPath = style->mClipPath;
8005 const auto& shape = *clipPath.AsShape()._0;
8007 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8008 const nsRect refBox =
8009 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8011 wr::WrClipId clipId{};
8013 switch (shape.tag) {
8014 case StyleBasicShape::Tag::Rect: {
8015 const nsRect rect =
8016 ShapeUtils::ComputeInsetRect(shape.AsRect().rect, refBox) +
8017 aDisplayItem.ToReferenceFrame();
8019 nscoord radii[8] = {0};
8020 if (ShapeUtils::ComputeRectRadii(shape.AsRect().round, refBox, rect,
8021 radii)) {
8022 clipId = aBuilder.DefineRoundedRectClip(
8023 Nothing(),
8024 wr::ToComplexClipRegion(rect, radii, appUnitsPerDevPixel));
8025 } else {
8026 clipId = aBuilder.DefineRectClip(
8027 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8028 rect, appUnitsPerDevPixel)));
8031 break;
8033 case StyleBasicShape::Tag::Ellipse:
8034 case StyleBasicShape::Tag::Circle: {
8035 nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox);
8037 nsSize radii;
8038 if (shape.IsEllipse()) {
8039 radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox);
8040 } else {
8041 nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox);
8042 radii = {radius, radius};
8045 nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center -
8046 nsPoint(radii.width, radii.height),
8047 radii * 2);
8049 nscoord ellipseRadii[8];
8050 for (const auto corner : AllPhysicalHalfCorners()) {
8051 ellipseRadii[corner] =
8052 HalfCornerIsX(corner) ? radii.width : radii.height;
8055 clipId = aBuilder.DefineRoundedRectClip(
8056 Nothing(), wr::ToComplexClipRegion(ellipseRect, ellipseRadii,
8057 appUnitsPerDevPixel));
8059 break;
8061 default:
8062 // Please don't add more exceptions, try to find a way to define the clip
8063 // without using a mask image.
8065 // And if you _really really_ need to add an exception, add it to
8066 // SVGUtils::DetermineMaskUsage
8067 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8068 return Nothing();
8071 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8073 return Some(clipChainId);
8076 static void FillPolygonDataForDisplayItem(
8077 const nsDisplayMasksAndClipPaths& aDisplayItem,
8078 nsTArray<wr::LayoutPoint>& aPoints, wr::FillRule& aFillRule) {
8079 nsIFrame* frame = aDisplayItem.Frame();
8080 const auto* style = frame->StyleSVGReset();
8081 bool isPolygon = style->HasClipPath() && style->mClipPath.IsShape() &&
8082 style->mClipPath.AsShape()._0->IsPolygon();
8083 if (!isPolygon) {
8084 return;
8087 const auto& clipPath = style->mClipPath;
8088 const auto& shape = *clipPath.AsShape()._0;
8089 const nsRect refBox =
8090 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8092 // We only fill polygon data for polygons that are below a complexity
8093 // limit.
8094 nsTArray<nsPoint> vertices =
8095 ShapeUtils::ComputePolygonVertices(shape, refBox);
8096 if (vertices.Length() > wr::POLYGON_CLIP_VERTEX_MAX) {
8097 return;
8100 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8102 for (size_t i = 0; i < vertices.Length(); ++i) {
8103 wr::LayoutPoint point = wr::ToLayoutPoint(
8104 LayoutDevicePoint::FromAppUnits(vertices[i], appUnitsPerDevPixel));
8105 aPoints.AppendElement(point);
8108 aFillRule = (shape.AsPolygon().fill == StyleFillRule::Nonzero)
8109 ? wr::FillRule::Nonzero
8110 : wr::FillRule::Evenodd;
8113 static Maybe<wr::WrClipChainId> CreateWRClipPathAndMasks(
8114 nsDisplayMasksAndClipPaths* aDisplayItem, const LayoutDeviceRect& aBounds,
8115 wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder,
8116 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8117 nsDisplayListBuilder* aDisplayListBuilder) {
8118 if (auto clip = CreateSimpleClipRegion(*aDisplayItem, aBuilder)) {
8119 return clip;
8122 Maybe<wr::ImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(
8123 aDisplayItem, aBuilder, aResources, aSc, aDisplayListBuilder, aBounds);
8124 if (!mask) {
8125 return Nothing();
8128 // We couldn't create a simple clip region, but before we create an image
8129 // mask clip, see if we can get a polygon clip to add to it.
8130 nsTArray<wr::LayoutPoint> points;
8131 wr::FillRule fillRule = wr::FillRule::Nonzero;
8132 FillPolygonDataForDisplayItem(*aDisplayItem, points, fillRule);
8134 wr::WrClipId clipId =
8135 aBuilder.DefineImageMaskClip(mask.ref(), points, fillRule);
8137 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8139 return Some(clipChainId);
8142 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8143 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8144 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8145 nsDisplayListBuilder* aDisplayListBuilder) {
8146 bool snap;
8147 auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8148 nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
8149 LayoutDeviceRect bounds =
8150 LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel);
8152 Maybe<wr::WrClipChainId> clip = CreateWRClipPathAndMasks(
8153 this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder);
8155 float oldOpacity = aBuilder.GetInheritedOpacity();
8157 Maybe<StackingContextHelper> layer;
8158 const StackingContextHelper* sc = &aSc;
8159 if (clip) {
8160 // Create a new stacking context to attach the mask to, ensuring the mask is
8161 // applied to the aggregate, and not the individual elements.
8163 // The stacking context shouldn't have any offset.
8164 bounds.MoveTo(0, 0);
8166 Maybe<float> opacity =
8167 (SVGUtils::DetermineMaskUsage(mFrame, false).IsSimpleClipShape() &&
8168 aBuilder.GetInheritedOpacity() != 1.0f)
8169 ? Some(aBuilder.GetInheritedOpacity())
8170 : Nothing();
8172 wr::StackingContextParams params;
8173 params.clip = wr::WrStackingContextClip::ClipChain(clip->id);
8174 params.opacity = opacity.ptrOr(nullptr);
8175 if (mWrapsBackdropFilter) {
8176 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8178 layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params,
8179 bounds);
8180 sc = layer.ptr();
8183 aBuilder.SetInheritedOpacity(1.0f);
8184 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8185 aBuilder.SetInheritedClipChain(nullptr);
8186 CreateWebRenderCommandsNewClipListOption(aBuilder, aResources, *sc, aManager,
8187 aDisplayListBuilder, layer.isSome());
8188 aBuilder.SetInheritedOpacity(oldOpacity);
8189 aBuilder.SetInheritedClipChain(oldClipChain);
8191 return true;
8194 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8195 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
8196 if (const DisplayItemClip* clip =
8197 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
8198 return Some(clip->GetClipRect());
8200 // This item does not have a clip with respect to |aASR|. However, we
8201 // might still have finite bounds with respect to |aASR|. Check our
8202 // children.
8203 nsDisplayList* childList = GetSameCoordinateSystemChildren();
8204 if (childList) {
8205 return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR));
8207 #ifdef DEBUG
8208 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
8209 #endif
8210 return Nothing();
8213 #ifdef MOZ_DUMP_PAINTING
8214 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) {
8215 nsIFrame* firstFrame =
8216 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8217 bool first = true;
8218 aTo += " effects=(";
8219 SVGClipPathFrame* clipPathFrame;
8220 // XXX Check return value?
8221 SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
8222 if (clipPathFrame) {
8223 if (!first) {
8224 aTo += ", ";
8226 aTo += nsPrintfCString(
8227 "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
8228 first = false;
8229 } else if (mFrame->StyleSVGReset()->HasClipPath()) {
8230 if (!first) {
8231 aTo += ", ";
8233 aTo += "clip(basic-shape)";
8234 first = false;
8237 nsTArray<SVGMaskFrame*> masks;
8238 // XXX check return value?
8239 SVGObserverUtils::GetAndObserveMasks(firstFrame, &masks);
8240 if (!masks.IsEmpty() && masks[0]) {
8241 if (!first) {
8242 aTo += ", ";
8244 aTo += "mask";
8246 aTo += ")";
8248 #endif
8250 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8251 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8252 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8253 nsDisplayListBuilder* aDisplayListBuilder) {
8254 WrFiltersHolder wrFilters;
8255 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8256 auto filterChain = style.StyleEffects()->mBackdropFilters.AsSpan();
8257 bool initialized = true;
8258 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8259 wrFilters) &&
8260 !SVGIntegrationUtils::BuildWebRenderFilters(
8261 mFrame, filterChain, StyleFilterType::BackdropFilter, wrFilters,
8262 initialized)) {
8263 // TODO: If painting backdrop-filters on the content side is implemented,
8264 // consider returning false to fall back to that.
8265 wrFilters = {};
8268 if (!initialized) {
8269 wrFilters = {};
8272 nsCSSRendering::ImageLayerClipState clip;
8273 nsCSSRendering::GetImageLayerClip(
8274 style.StyleBackground()->BottomLayer(), mFrame, *style.StyleBorder(),
8275 mBackdropRect, mBackdropRect, false,
8276 mFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
8278 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
8279 mBackdropRect, mFrame->PresContext()->AppUnitsPerDevPixel());
8281 wr::ComplexClipRegion region =
8282 wr::ToComplexClipRegion(clip.mBGClipArea, clip.mRadii,
8283 mFrame->PresContext()->AppUnitsPerDevPixel());
8285 aBuilder.PushBackdropFilter(wr::ToLayoutRect(bounds), region,
8286 wrFilters.filters, wrFilters.filter_datas,
8287 !BackfaceIsHidden());
8289 wr::StackingContextParams params;
8290 params.clip =
8291 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8292 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8293 params);
8295 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8296 aDisplayListBuilder);
8297 return true;
8300 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder* aBuilder,
8301 gfxContext* aCtx) {
8302 // TODO: Implement backdrop filters
8303 GetChildren()->Paint(aBuilder, aCtx,
8304 mFrame->PresContext()->AppUnitsPerDevPixel());
8307 nsRect nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder* aBuilder,
8308 bool* aSnap) const {
8309 nsRect childBounds = nsDisplayWrapList::GetBounds(aBuilder, aSnap);
8311 *aSnap = false;
8313 return mBackdropRect.Union(childBounds);
8316 /* static */
8317 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder,
8318 nsIFrame* aFrame, nsDisplayList* aList,
8319 nsIFrame* aStyleFrame,
8320 bool aWrapsBackdropFilter)
8321 : nsDisplayEffectsBase(aBuilder, aFrame, aList),
8322 mStyle(aFrame == aStyleFrame ? nullptr : aStyleFrame->Style()),
8323 mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()),
8324 mWrapsBackdropFilter(aWrapsBackdropFilter) {
8325 MOZ_COUNT_CTOR(nsDisplayFilters);
8326 mVisibleRect = aBuilder->GetVisibleRect() +
8327 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
8330 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8331 PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) {
8332 GetChildren()->Paint(aBuilder, aContext,
8333 mFrame->PresContext()->AppUnitsPerDevPixel());
8337 void nsDisplayFilters::PaintWithContentsPaintCallback(
8338 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
8339 const std::function<void(gfxContext* aContext)>& aPaintChildren) {
8340 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
8341 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8342 PaintFramesParams params(*aCtx, mFrame, mVisibleRect, borderArea, aBuilder,
8343 false, imgParams);
8345 gfxPoint userSpaceToFrameSpaceOffset =
8346 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params);
8348 auto filterChain = mStyle ? mStyle->StyleEffects()->mFilters.AsSpan()
8349 : mFrame->StyleEffects()->mFilters.AsSpan();
8350 SVGIntegrationUtils::PaintFilter(
8351 params, filterChain,
8352 [&](gfxContext& aContext, imgDrawingParams&, const gfxMatrix*,
8353 const nsIntRect*) {
8354 gfxContextMatrixAutoSaveRestore autoSR(&aContext);
8355 aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(
8356 -userSpaceToFrameSpaceOffset));
8357 aPaintChildren(&aContext);
8361 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8362 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame);
8365 bool nsDisplayFilters::CreateWebRenderCommands(
8366 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8367 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8368 nsDisplayListBuilder* aDisplayListBuilder) {
8369 WrFiltersHolder wrFilters;
8370 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8371 auto filterChain = style.StyleEffects()->mFilters.AsSpan();
8372 bool initialized = true;
8373 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8374 wrFilters) &&
8375 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame, filterChain,
8376 StyleFilterType::Filter,
8377 wrFilters, initialized)) {
8378 if (mStyle) {
8379 // TODO(bug 1769223): Support fallback filters in the root code-path,
8380 // perhaps. For now treat it the same way as invalid filters.
8381 wrFilters = {};
8382 } else {
8383 // Draw using fallback.
8384 return false;
8388 if (!initialized) {
8389 // https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
8391 // If the filter references a non-existent object or the referenced object
8392 // is not a filter element, then the whole filter chain is ignored. No
8393 // filter is applied to the object.
8395 // Note that other engines have a weird discrepancy between SVG and HTML
8396 // content here, but the spec is clear.
8397 wrFilters = {};
8400 uint64_t clipChainId;
8401 if (wrFilters.post_filters_clip) {
8402 auto devPxRect = LayoutDeviceRect::FromAppUnits(
8403 wrFilters.post_filters_clip.value() + ToReferenceFrame(),
8404 mFrame->PresContext()->AppUnitsPerDevPixel());
8405 auto clipId =
8406 aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect));
8407 clipChainId = aBuilder.DefineClipChain({clipId}, true).id;
8408 } else {
8409 clipChainId = aBuilder.CurrentClipChainId();
8411 wr::WrStackingContextClip clip =
8412 wr::WrStackingContextClip::ClipChain(clipChainId);
8414 float opacity = aBuilder.GetInheritedOpacity();
8415 aBuilder.SetInheritedOpacity(1.0f);
8416 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8417 aBuilder.SetInheritedClipChain(nullptr);
8418 wr::StackingContextParams params;
8419 params.mFilters = std::move(wrFilters.filters);
8420 params.mFilterDatas = std::move(wrFilters.filter_datas);
8421 params.opacity = opacity != 1.0f ? &opacity : nullptr;
8422 params.clip = clip;
8423 if (mWrapsBackdropFilter) {
8424 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8426 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8427 params);
8429 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
8430 aManager, aDisplayListBuilder);
8431 aBuilder.SetInheritedOpacity(opacity);
8432 aBuilder.SetInheritedClipChain(oldClipChain);
8434 return true;
8437 #ifdef MOZ_DUMP_PAINTING
8438 void nsDisplayFilters::PrintEffects(nsACString& aTo) {
8439 nsIFrame* firstFrame =
8440 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8441 bool first = true;
8442 aTo += " effects=(";
8443 // We may exist for a mix of CSS filter functions and/or references to SVG
8444 // filters. If we have invalid references to SVG filters then we paint
8445 // nothing, but otherwise we will apply one or more filters.
8446 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) !=
8447 SVGObserverUtils::eHasRefsSomeInvalid) {
8448 if (!first) {
8449 aTo += ", ";
8451 aTo += "filter";
8453 aTo += ")";
8455 #endif
8457 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder,
8458 nsIFrame* aFrame, nsDisplayList* aList)
8459 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8460 MOZ_COUNT_CTOR(nsDisplaySVGWrapper);
8463 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8464 return !aBuilder->GetWidgetLayerManager();
8467 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8468 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8469 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8470 nsDisplayListBuilder* aDisplayListBuilder) {
8471 return CreateWebRenderCommandsNewClipListOption(
8472 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8475 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder* aBuilder,
8476 nsIFrame* aFrame,
8477 nsDisplayList* aList)
8478 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8479 MOZ_COUNT_CTOR(nsDisplayForeignObject);
8482 #ifdef NS_BUILD_REFCNT_LOGGING
8483 nsDisplayForeignObject::~nsDisplayForeignObject() {
8484 MOZ_COUNT_DTOR(nsDisplayForeignObject);
8486 #endif
8488 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8489 return !aBuilder->GetWidgetLayerManager();
8492 bool nsDisplayForeignObject::CreateWebRenderCommands(
8493 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8494 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8495 nsDisplayListBuilder* aDisplayListBuilder) {
8496 AutoRestore<bool> restoreDoGrouping(aManager->CommandBuilder().mDoGrouping);
8497 aManager->CommandBuilder().mDoGrouping = false;
8498 return CreateWebRenderCommandsNewClipListOption(
8499 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8502 void nsDisplayLink::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8503 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8504 aCtx->GetDrawTarget()->Link(
8505 mLinkSpec.get(), NSRectToRect(GetPaintRect(aBuilder, aCtx), appPerDev));
8508 void nsDisplayDestination::Paint(nsDisplayListBuilder* aBuilder,
8509 gfxContext* aCtx) {
8510 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8511 aCtx->GetDrawTarget()->Destination(
8512 mDestinationName.get(),
8513 NSPointToPoint(GetPaintRect(aBuilder, aCtx).TopLeft(), appPerDev));
8516 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8517 nsDisplayList* aOutResultList, nsIContent* aContent) {
8518 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
8519 // in content document order and SortByZOrder is a stable sort which
8520 // guarantees that boxes produced by the same element are placed together
8521 // in the sort. Consider a position:relative inline element that breaks
8522 // across lines and has absolutely positioned children; all the abs-pos
8523 // children should be z-ordered after all the boxes for the position:relative
8524 // element itself.
8525 PositionedDescendants()->SortByZOrder();
8527 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8528 // 1,2: backgrounds and borders
8529 aOutResultList->AppendToTop(BorderBackground());
8530 // 3: negative z-index children.
8531 for (auto* item : PositionedDescendants()->TakeItems()) {
8532 if (item->ZIndex() < 0) {
8533 aOutResultList->AppendToTop(item);
8534 } else {
8535 PositionedDescendants()->AppendToTop(item);
8539 // 4: block backgrounds
8540 aOutResultList->AppendToTop(BlockBorderBackgrounds());
8541 // 5: floats
8542 aOutResultList->AppendToTop(Floats());
8543 // 7: general content
8544 aOutResultList->AppendToTop(Content());
8545 // 7.5: outlines, in content tree order. We need to sort by content order
8546 // because an element with outline that breaks and has children with outline
8547 // might have placed child outline items between its own outline items.
8548 // The element's outline items need to all come before any child outline
8549 // items.
8550 if (aContent) {
8551 Outlines()->SortByContentOrder(aContent);
8553 aOutResultList->AppendToTop(Outlines());
8554 // 8, 9: non-negative z-index children
8555 aOutResultList->AppendToTop(PositionedDescendants());
8558 uint32_t PaintTelemetry::sPaintLevel = 0;
8560 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8561 // Don't record nested paints.
8562 if (sPaintLevel++ > 0) {
8563 return;
8566 mStart = TimeStamp::Now();
8569 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8570 MOZ_ASSERT(sPaintLevel != 0);
8571 if (--sPaintLevel > 0) {
8572 return;
8575 // If we're in multi-process mode, don't include paint times for the parent
8576 // process.
8577 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8578 return;
8581 double totalMs = (TimeStamp::Now() - mStart).ToMilliseconds();
8583 // Record the total time.
8584 Telemetry::Accumulate(Telemetry::CONTENT_PAINT_TIME,
8585 static_cast<uint32_t>(totalMs));
8588 static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
8589 if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
8590 return aFrame;
8593 if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
8594 !aFrame->GetPrevInFlow()) {
8595 return aFrame->GetPlaceholderFrame();
8598 return aFrame;
8601 static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
8602 nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
8603 MOZ_ASSERT(f);
8604 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
8607 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8608 nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
8609 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
8610 const bool aIsTransformed)
8611 : mBuilder(aBuilder),
8612 mPrevFrame(aBuilder->mCurrentFrame),
8613 mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
8614 mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
8615 mPrevAdditionalOffset(aBuilder->mAdditionalOffset),
8616 mPrevVisibleRect(aBuilder->mVisibleRect),
8617 mPrevDirtyRect(aBuilder->mDirtyRect),
8618 mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo),
8619 mPrevAncestorHasApzAwareEventHandler(
8620 aBuilder->mAncestorHasApzAwareEventHandler),
8621 mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
8622 mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
8623 if (aIsTransformed) {
8624 aBuilder->mCurrentOffsetToReferenceFrame =
8625 aBuilder->AdditionalOffset().refOr(nsPoint());
8626 aBuilder->mCurrentReferenceFrame = aForChild;
8627 } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
8628 aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
8629 } else {
8630 aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
8631 aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
8634 // If aForChild is being visited from a frame other than it's ancestor frame,
8635 // mInInvalidSubtree will need to be recalculated the slow way.
8636 if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) {
8637 aBuilder->mInInvalidSubtree =
8638 aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
8639 } else {
8640 aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
8643 aBuilder->mCurrentFrame = aForChild;
8644 aBuilder->mVisibleRect = aVisibleRect;
8645 aBuilder->mDirtyRect =
8646 aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
8649 } // namespace mozilla