no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / layout / painting / nsDisplayList.cpp
blob348a6614d86757a94eefa22d6dfa3b6a0902e325
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 */
8 /*
9 * structures that represent things to be painted (ordered in z-order),
10 * used during painting and hit testing
13 #include "nsDisplayList.h"
15 #include <stdint.h>
16 #include <algorithm>
17 #include <limits>
19 #include "gfxContext.h"
20 #include "gfxUtils.h"
21 #include "mozilla/DisplayPortUtils.h"
22 #include "mozilla/Likely.h"
23 #include "mozilla/dom/BrowserChild.h"
24 #include "mozilla/dom/HTMLCanvasElement.h"
25 #include "mozilla/dom/RemoteBrowser.h"
26 #include "mozilla/dom/Selection.h"
27 #include "mozilla/dom/ServiceWorkerRegistrar.h"
28 #include "mozilla/dom/ServiceWorkerRegistration.h"
29 #include "mozilla/dom/SVGElement.h"
30 #include "mozilla/dom/TouchEvent.h"
31 #include "mozilla/dom/PerformanceMainThread.h"
32 #include "mozilla/gfx/2D.h"
33 #include "mozilla/PresShell.h"
34 #include "mozilla/ShapeUtils.h"
35 #include "mozilla/StaticPrefs_apz.h"
36 #include "mozilla/StaticPrefs_gfx.h"
37 #include "mozilla/StaticPrefs_layers.h"
38 #include "mozilla/StaticPrefs_layout.h"
39 #include "mozilla/StaticPrefs_print.h"
40 #include "mozilla/SVGIntegrationUtils.h"
41 #include "mozilla/SVGUtils.h"
42 #include "mozilla/ViewportUtils.h"
43 #include "nsCSSRendering.h"
44 #include "nsCSSRenderingGradients.h"
45 #include "nsCaseTreatment.h"
46 #include "nsRefreshDriver.h"
47 #include "nsRegion.h"
48 #include "nsStyleStructInlines.h"
49 #include "nsStyleTransformMatrix.h"
50 #include "nsTransitionManager.h"
51 #include "gfxMatrix.h"
52 #include "nsLayoutUtils.h"
53 #include "nsIScrollableFrame.h"
54 #include "nsIFrameInlines.h"
55 #include "nsStyleConsts.h"
56 #include "BorderConsts.h"
57 #include "mozilla/MathAlgorithms.h"
59 #include "imgIContainer.h"
60 #include "nsImageFrame.h"
61 #include "nsSubDocumentFrame.h"
62 #include "nsViewManager.h"
63 #include "ImageContainer.h"
64 #include "nsCanvasFrame.h"
65 #include "nsSubDocumentFrame.h"
66 #include "StickyScrollContainer.h"
67 #include "mozilla/AnimationPerformanceWarning.h"
68 #include "mozilla/AnimationUtils.h"
69 #include "mozilla/AutoRestore.h"
70 #include "mozilla/EffectCompositor.h"
71 #include "mozilla/EffectSet.h"
72 #include "mozilla/glean/GleanMetrics.h"
73 #include "mozilla/HashTable.h"
74 #include "mozilla/LookAndFeel.h"
75 #include "mozilla/OperatorNewExtensions.h"
76 #include "mozilla/Preferences.h"
77 #include "mozilla/ProfilerLabels.h"
78 #include "mozilla/ProfilerMarkers.h"
79 #include "mozilla/StyleAnimationValue.h"
80 #include "mozilla/ServoBindings.h"
81 #include "mozilla/SVGClipPathFrame.h"
82 #include "mozilla/SVGMaskFrame.h"
83 #include "mozilla/SVGObserverUtils.h"
84 #include "mozilla/Telemetry.h"
85 #include "mozilla/UniquePtr.h"
86 #include "mozilla/Unused.h"
87 #include "mozilla/ViewportFrame.h"
88 #include "mozilla/gfx/gfxVars.h"
89 #include "ActiveLayerTracker.h"
90 #include "nsEscape.h"
91 #include "nsPrintfCString.h"
92 #include "UnitTransforms.h"
93 #include "LayerAnimationInfo.h"
94 #include "mozilla/EventStateManager.h"
95 #include "nsCaret.h"
96 #include "nsDOMTokenList.h"
97 #include "nsCSSProps.h"
98 #include "nsTableCellFrame.h"
99 #include "nsTableColFrame.h"
100 #include "nsTextFrame.h"
101 #include "nsTextPaintStyle.h"
102 #include "nsSliderFrame.h"
103 #include "nsFocusManager.h"
104 #include "TextDrawTarget.h"
105 #include "mozilla/layers/AnimationHelper.h"
106 #include "mozilla/layers/CompositorThread.h"
107 #include "mozilla/layers/InputAPZContext.h"
108 #include "mozilla/layers/RenderRootStateManager.h"
109 #include "mozilla/layers/StackingContextHelper.h"
110 #include "mozilla/layers/TreeTraversal.h"
111 #include "mozilla/layers/WebRenderBridgeChild.h"
112 #include "mozilla/layers/WebRenderLayerManager.h"
113 #include "mozilla/layers/WebRenderMessages.h"
114 #include "mozilla/layers/WebRenderScrollData.h"
116 namespace mozilla {
118 using namespace dom;
119 using namespace gfx;
120 using namespace layout;
121 using namespace layers;
122 using namespace image;
124 LazyLogModule sContentDisplayListLog("dl.content");
125 LazyLogModule sParentDisplayListLog("dl.parent");
127 LazyLogModule& GetLoggerByProcess() {
128 return XRE_IsContentProcess() ? sContentDisplayListLog
129 : sParentDisplayListLog;
132 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
133 void AssertUniqueItem(nsDisplayItem* aItem) {
134 for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) {
135 if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
136 i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
137 if (i->IsPreProcessedItem() || i->IsPreProcessed()) {
138 continue;
140 MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
144 #endif
146 bool ShouldBuildItemForEvents(const DisplayItemType aType) {
147 return aType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO ||
148 (GetDisplayItemFlagsForType(aType) & TYPE_IS_CONTAINER);
151 static bool ItemTypeSupportsHitTesting(const DisplayItemType aType) {
152 switch (aType) {
153 case DisplayItemType::TYPE_BACKGROUND:
154 case DisplayItemType::TYPE_BACKGROUND_COLOR:
155 case DisplayItemType::TYPE_THEMED_BACKGROUND:
156 return true;
157 default:
158 return false;
162 void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder,
163 nsPaintedDisplayItem* aItem,
164 const DisplayItemType aType) {
165 if (ItemTypeSupportsHitTesting(aType)) {
166 aItem->InitializeHitTestInfo(aBuilder);
170 /* static */
171 already_AddRefed<ActiveScrolledRoot> ActiveScrolledRoot::CreateASRForFrame(
172 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame,
173 bool aIsRetained) {
174 nsIFrame* f = do_QueryFrame(aScrollableFrame);
176 RefPtr<ActiveScrolledRoot> asr;
177 if (aIsRetained) {
178 asr = f->GetProperty(ActiveScrolledRootCache());
181 if (!asr) {
182 asr = new ActiveScrolledRoot();
184 if (aIsRetained) {
185 RefPtr<ActiveScrolledRoot> ref = asr;
186 f->SetProperty(ActiveScrolledRootCache(), ref.forget().take());
189 asr->mParent = aParent;
190 asr->mScrollableFrame = aScrollableFrame;
191 asr->mDepth = aParent ? aParent->mDepth + 1 : 1;
192 asr->mRetained = aIsRetained;
194 return asr.forget();
197 /* static */
198 bool ActiveScrolledRoot::IsAncestor(const ActiveScrolledRoot* aAncestor,
199 const ActiveScrolledRoot* aDescendant) {
200 if (!aAncestor) {
201 // nullptr is the root
202 return true;
204 if (Depth(aAncestor) > Depth(aDescendant)) {
205 return false;
207 const ActiveScrolledRoot* asr = aDescendant;
208 while (asr) {
209 if (asr == aAncestor) {
210 return true;
212 asr = asr->mParent;
214 return false;
217 /* static */
218 bool ActiveScrolledRoot::IsProperAncestor(
219 const ActiveScrolledRoot* aAncestor,
220 const ActiveScrolledRoot* aDescendant) {
221 return aAncestor != aDescendant && IsAncestor(aAncestor, aDescendant);
224 /* static */
225 nsCString ActiveScrolledRoot::ToString(
226 const ActiveScrolledRoot* aActiveScrolledRoot) {
227 nsAutoCString str;
228 for (const auto* asr = aActiveScrolledRoot; asr; asr = asr->mParent) {
229 str.AppendPrintf("<0x%p>", asr->mScrollableFrame);
230 if (asr->mParent) {
231 str.AppendLiteral(", ");
234 return std::move(str);
237 ScrollableLayerGuid::ViewID ActiveScrolledRoot::ComputeViewId() const {
238 nsIContent* content = mScrollableFrame->GetScrolledFrame()->GetContent();
239 return nsLayoutUtils::FindOrCreateIDFor(content);
242 ActiveScrolledRoot::~ActiveScrolledRoot() {
243 if (mScrollableFrame && mRetained) {
244 nsIFrame* f = do_QueryFrame(mScrollableFrame);
245 f->RemoveProperty(ActiveScrolledRootCache());
249 static uint64_t AddAnimationsForWebRender(
250 nsDisplayItem* aItem, RenderRootStateManager* aManager,
251 nsDisplayListBuilder* aDisplayListBuilder,
252 const Maybe<LayoutDevicePoint>& aPosition = Nothing()) {
253 auto* effects = EffectSet::GetForFrame(aItem->Frame(), aItem->GetType());
254 if (!effects || effects->IsEmpty()) {
255 // If there is no animation on the nsIFrame, that means
256 // 1) we've never created any animations on this frame or
257 // 2) the frame was reconstruced or
258 // 3) all animations on the frame have finished
259 // in such cases we don't need do anything here.
261 // Even if there is a WebRenderAnimationData for the display item type on
262 // this frame, it's going to be discarded since it's not marked as being
263 // used.
264 return 0;
267 RefPtr<WebRenderAnimationData> animationData =
268 aManager->CommandBuilder()
269 .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(aItem);
270 AnimationInfo& animationInfo = animationData->GetAnimationInfo();
271 nsIFrame* frame = aItem->Frame();
272 animationInfo.AddAnimationsForDisplayItem(
273 frame, aDisplayListBuilder, aItem, aItem->GetType(),
274 aManager->LayerManager(), aPosition);
276 // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
277 // are no active animations.
278 uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
279 if (!animationInfo.GetAnimations().IsEmpty()) {
280 OpAddCompositorAnimations anim(
281 CompositorAnimations(animationInfo.GetAnimations(), animationsId));
282 aManager->WrBridge()->AddWebRenderParentCommand(anim);
283 aManager->AddActiveCompositorAnimationId(animationsId);
284 } else if (animationsId) {
285 aManager->AddCompositorAnimationsIdForDiscard(animationsId);
286 animationsId = 0;
289 return animationsId;
292 static bool GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
293 const nsRect& aFillRect,
294 nsDisplayListBuilder* aBuilder) {
295 if (aBuilder->IsForGenerateGlyphMask()) {
296 return false;
299 SVGObserverUtils::GetAndObserveBackgroundClip(aFrame);
301 // The main function of enabling background-clip:text property value.
302 // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
303 // this function to
304 // 1. Generate a mask by all descendant text frames
305 // 2. Push the generated mask into aContext.
307 gfxContext* sourceCtx = aContext;
308 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
309 aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel());
311 // Create a mask surface.
312 RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget();
313 RefPtr<DrawTarget> maskDT = sourceTarget->CreateClippedDrawTarget(
314 bounds.ToUnknownRect(), SurfaceFormat::A8);
315 if (!maskDT || !maskDT->IsValid()) {
316 return false;
318 gfxContext maskCtx(maskDT, /* aPreserveTransform */ true);
319 maskCtx.Multiply(Matrix::Translation(bounds.TopLeft().ToUnknownPoint()));
321 // Shade text shape into mask A8 surface.
322 nsLayoutUtils::PaintFrame(
323 &maskCtx, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()),
324 NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph);
326 // Push the generated mask into aContext, so that the caller can pop and
327 // blend with it.
329 Matrix currentMatrix = sourceCtx->CurrentMatrix();
330 Matrix invCurrentMatrix = currentMatrix;
331 invCurrentMatrix.Invert();
333 RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
334 sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0,
335 maskSurface, invCurrentMatrix);
337 return true;
340 nsDisplayWrapper* nsDisplayWrapList::CreateShallowCopy(
341 nsDisplayListBuilder* aBuilder) {
342 const nsDisplayWrapList* wrappedItem = AsDisplayWrapList();
343 MOZ_ASSERT(wrappedItem);
345 // Create a new nsDisplayWrapList using a copy-constructor. This is done
346 // to preserve the information about bounds.
347 nsDisplayWrapper* wrapper =
348 new (aBuilder) nsDisplayWrapper(aBuilder, *wrappedItem);
349 wrapper->SetType(nsDisplayWrapper::ItemType());
350 MOZ_ASSERT(wrapper);
352 // Set the display list pointer of the new wrapper item to the display list
353 // of the wrapped item.
354 wrapper->mListPtr = wrappedItem->mListPtr;
355 return wrapper;
358 nsDisplayWrapList* nsDisplayListBuilder::MergeItems(
359 nsTArray<nsDisplayItem*>& aItems) {
360 // For merging, we create a temporary item by cloning the last item of the
361 // mergeable items list. This ensures that the temporary item will have the
362 // correct frame and bounds.
363 nsDisplayWrapList* last = aItems.PopLastElement()->AsDisplayWrapList();
364 MOZ_ASSERT(last);
365 nsDisplayWrapList* merged = last->Clone(this);
366 MOZ_ASSERT(merged);
367 AddTemporaryItem(merged);
369 // Create nsDisplayWrappers that point to the internal display lists of the
370 // items we are merging. These nsDisplayWrappers are added to the display list
371 // of the temporary item.
372 for (nsDisplayItem* item : aItems) {
373 MOZ_ASSERT(item);
374 MOZ_ASSERT(merged->CanMerge(item));
375 merged->Merge(item);
376 MOZ_ASSERT(item->AsDisplayWrapList());
377 merged->GetChildren()->AppendToTop(
378 static_cast<nsDisplayWrapList*>(item)->CreateShallowCopy(this));
381 merged->GetChildren()->AppendToTop(last->CreateShallowCopy(this));
383 return merged;
386 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
387 SetCurrentActiveScrolledRoot(
388 const ActiveScrolledRoot* aActiveScrolledRoot) {
389 MOZ_ASSERT(!mUsed);
391 // Set the builder's mCurrentActiveScrolledRoot.
392 mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;
394 // We also need to adjust the builder's mCurrentContainerASR.
395 // mCurrentContainerASR needs to be an ASR that all the container's
396 // contents have finite bounds with respect to. If aActiveScrolledRoot
397 // is an ancestor ASR of mCurrentContainerASR, that means we need to
398 // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
399 // the items that will be created with aActiveScrolledRoot wouldn't
400 // have finite bounds with respect to mCurrentContainerASR. There's one
401 // exception, in the case where there's a content clip on the builder
402 // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
403 // content clip will clip all items that are created while this
404 // AutoCurrentActiveScrolledRootSetter exists. This means that the items
405 // created during our lifetime will have finite bounds with respect to
406 // the content clip's ASR, even if the items' actual ASR is an ancestor
407 // of that. And it also means that mCurrentContainerASR only needs to be
408 // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
409 // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
410 // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
412 // finiteBoundsASR is the leafmost ASR that all items created during
413 // object's lifetime have finite bounds with respect to.
414 const ActiveScrolledRoot* finiteBoundsASR =
415 ActiveScrolledRoot::PickDescendant(mContentClipASR, aActiveScrolledRoot);
417 // mCurrentContainerASR is adjusted so that it's still an ancestor of
418 // finiteBoundsASR.
419 mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
420 mBuilder->mCurrentContainerASR, finiteBoundsASR);
422 // If we are entering out-of-flow content inside a CSS filter, mark
423 // scroll frames wrt. which the content is fixed as containing such content.
424 if (mBuilder->mFilterASR && ActiveScrolledRoot::IsAncestor(
425 aActiveScrolledRoot, mBuilder->mFilterASR)) {
426 for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
427 asr && asr != aActiveScrolledRoot; asr = asr->mParent) {
428 asr->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
432 mUsed = true;
435 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
436 InsertScrollFrame(nsIScrollableFrame* aScrollableFrame) {
437 MOZ_ASSERT(!mUsed);
438 size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
439 const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot;
440 const ActiveScrolledRoot* asr =
441 mBuilder->AllocateActiveScrolledRoot(parentASR, aScrollableFrame);
442 mBuilder->mCurrentActiveScrolledRoot = asr;
444 // All child ASRs of parentASR that were created while this
445 // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
446 // now. Reparent them to asr.
447 for (size_t i = mDescendantsStartIndex; i < descendantsEndIndex; i++) {
448 ActiveScrolledRoot* descendantASR = mBuilder->mActiveScrolledRoots[i];
449 if (ActiveScrolledRoot::IsAncestor(parentASR, descendantASR)) {
450 descendantASR->IncrementDepth();
451 if (descendantASR->mParent == parentASR) {
452 descendantASR->mParent = asr;
457 mUsed = true;
460 nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
461 nsDisplayListBuilder* aBuilder)
462 : mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) {
463 mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot;
466 nsPresContext* nsDisplayListBuilder::CurrentPresContext() {
467 return CurrentPresShellState()->mPresShell->GetPresContext();
470 /* static */
471 nsRect nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
472 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
473 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
474 nsRect* aOutDirtyRect) {
475 nsRect visible = aVisibleRect;
476 nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
478 bool inPartialUpdate =
479 aBuilder->IsRetainingDisplayList() && aBuilder->IsPartialUpdate();
480 if (StaticPrefs::apz_allow_zooming() &&
481 DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame) &&
482 aBuilder->IsPaintingToWindow() && !inPartialUpdate) {
483 dirtyRectRelativeToDirtyFrame =
484 nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize());
486 // If there's a visual viewport size set, restrict the amount of the
487 // fixed-position element we paint to the visual viewport. (In general
488 // the fixed-position element can be as large as the layout viewport,
489 // which at a high zoom level can cause us to paint too large of an
490 // area.)
491 PresShell* presShell = aFrame->PresShell();
492 if (presShell->IsVisualViewportSizeSet()) {
493 dirtyRectRelativeToDirtyFrame =
494 nsRect(presShell->GetVisualViewportOffsetRelativeToLayoutViewport(),
495 presShell->GetVisualViewportSize());
496 // But if we have a displayport, expand it to the displayport, so
497 // that async-scrolling the visual viewport within the layout viewport
498 // will not checkerboard.
499 if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
500 nsRect displayport;
501 // Note that the displayport here is already in the right coordinate
502 // space: it's relative to the scroll port (= layout viewport), but
503 // covers the visual viewport with some margins around it, which is
504 // exactly what we want.
505 if (DisplayPortUtils::GetDisplayPort(
506 rootScrollFrame->GetContent(), &displayport,
507 DisplayPortOptions().With(ContentGeometryType::Fixed))) {
508 dirtyRectRelativeToDirtyFrame = displayport;
512 visible = dirtyRectRelativeToDirtyFrame;
513 if (StaticPrefs::apz_test_logging_enabled() &&
514 presShell->GetDocument()->IsContentDocument()) {
515 nsLayoutUtils::LogAdditionalTestData(
516 aBuilder, "fixedPosDisplayport",
517 ToString(CSSSize::FromAppUnits(visible)));
521 *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
522 visible -= aFrame->GetPosition();
524 nsRect overflowRect = aFrame->InkOverflowRect();
526 if (aFrame->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
527 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
529 * Add a fuzz factor to the overflow rectangle so that elements only
530 * just out of view are pulled into the display list, so they can be
531 * prerendered if necessary.
533 overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
536 visible.IntersectRect(visible, overflowRect);
537 aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect);
539 return visible;
542 nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder* aBuilder,
543 nsIFrame* aFrame,
544 nsDisplayList* aList)
545 : mList(aList) {
546 // Find the element that we need to check for link-ness, bailing out if
547 // we can't find one.
548 Element* elem = Element::FromNodeOrNull(aFrame->GetContent());
549 if (!elem) {
550 return;
553 // If the element has an id and/or name attribute, generate a destination
554 // for possible internal linking.
555 auto maybeGenerateDest = [&](const nsAtom* aAttr) {
556 nsAutoString attrValue;
557 elem->GetAttr(aAttr, attrValue);
558 if (!attrValue.IsEmpty()) {
559 NS_ConvertUTF16toUTF8 dest(attrValue);
560 // Ensure that we only emit a given destination once, although there may
561 // be multiple frames associated with a given element; we'll simply use
562 // the first of them as the target of any links to it.
563 // XXX(jfkthame) This prevents emitting duplicate destinations *on the
564 // same page*, but does not prevent duplicates on subsequent pages, as
565 // each new page is handled by a new temporary DisplayListBuilder. This
566 // seems to be harmless in practice, though a bit wasteful of space. To
567 // fix, we need to maintain the set of already-seen destinations globally
568 // for the print job, rather than attached to the (per-page) builder.
569 if (aBuilder->mDestinations.EnsureInserted(dest)) {
570 auto* destination = MakeDisplayItem<nsDisplayDestination>(
571 aBuilder, aFrame, dest.get(), aFrame->GetRect().TopLeft());
572 mList->AppendToTop(destination);
577 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
578 if (elem->HasID()) {
579 maybeGenerateDest(nsGkAtoms::id);
581 if (elem->HasName()) {
582 maybeGenerateDest(nsGkAtoms::name);
586 // Links don't nest, so if the builder already has a destination, no need to
587 // check for a link element here.
588 if (!aBuilder->mLinkSpec.IsEmpty()) {
589 return;
592 // Check if we have actually found a link.
593 if (!elem->IsLink()) {
594 return;
597 nsCOMPtr<nsIURI> uri = elem->GetHrefURI();
598 if (!uri) {
599 return;
602 // Is it a local (in-page) destination?
603 bool hasRef, eqExRef;
604 nsIURI* docURI;
605 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
606 NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef &&
607 (docURI = aFrame->PresContext()->Document()->GetDocumentURI()) &&
608 NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &eqExRef)) && eqExRef) {
609 if (NS_FAILED(uri->GetRef(aBuilder->mLinkSpec)) ||
610 aBuilder->mLinkSpec.IsEmpty()) {
611 return;
613 // The destination name is simply a string; we don't want URL-escaping
614 // applied to it.
615 NS_UnescapeURL(aBuilder->mLinkSpec);
616 // Mark the link spec as being an internal destination
617 aBuilder->mLinkSpec.Insert('#', 0);
618 } else {
619 if (NS_FAILED(uri->GetSpec(aBuilder->mLinkSpec)) ||
620 aBuilder->mLinkSpec.IsEmpty()) {
621 return;
625 // Record that we need to reset the builder's state on destruction.
626 mBuilderToReset = aBuilder;
629 void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
630 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
631 // Note that we may generate a link here even if the constructor bailed out
632 // without updating aBuilder->LinkSpec(), because it may have been set by
633 // an ancestor that was associated with a link element.
634 if (!aBuilder->mLinkSpec.IsEmpty()) {
635 auto* link = MakeDisplayItem<nsDisplayLink>(
636 aBuilder, aFrame, aBuilder->mLinkSpec.get(), aFrame->GetRect());
637 mList->AppendToTop(link);
641 uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1);
643 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
644 nsDisplayListBuilderMode aMode,
645 bool aBuildCaret,
646 bool aRetainingDisplayList)
647 : mReferenceFrame(aReferenceFrame),
648 mIgnoreScrollFrame(nullptr),
649 mCurrentActiveScrolledRoot(nullptr),
650 mCurrentContainerASR(nullptr),
651 mCurrentFrame(aReferenceFrame),
652 mCurrentReferenceFrame(aReferenceFrame),
653 mScrollInfoItemsForHoisting(nullptr),
654 mFirstClipChainToDestroy(nullptr),
655 mTableBackgroundSet(nullptr),
656 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
657 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
658 mFilterASR(nullptr),
659 mDirtyRect(-1, -1, -1, -1),
660 mBuildingExtraPagesForPageNum(0),
661 mMode(aMode),
662 mContainsBlendMode(false),
663 mIsBuildingScrollbar(false),
664 mCurrentScrollbarWillHaveLayer(false),
665 mBuildCaret(aBuildCaret),
666 mRetainingDisplayList(aRetainingDisplayList),
667 mPartialUpdate(false),
668 mIgnoreSuppression(false),
669 mIncludeAllOutOfFlows(false),
670 mDescendIntoSubdocuments(true),
671 mSelectedFramesOnly(false),
672 mAllowMergingAndFlattening(true),
673 mInTransform(false),
674 mInEventsOnly(false),
675 mInFilter(false),
676 mInPageSequence(false),
677 mIsInChromePresContext(false),
678 mSyncDecodeImages(false),
679 mIsPaintingToWindow(false),
680 mUseHighQualityScaling(false),
681 mIsPaintingForWebRender(false),
682 mIsCompositingCheap(false),
683 mAncestorHasApzAwareEventHandler(false),
684 mHaveScrollableDisplayPort(false),
685 mWindowDraggingAllowed(false),
686 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
687 mForceLayerForScrollParent(false),
688 mContainsNonMinimalDisplayPort(false),
689 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
690 mBuildingInvisibleItems(false),
691 mIsBuilding(false),
692 mInInvalidSubtree(false),
693 mDisablePartialUpdates(false),
694 mPartialBuildFailed(false),
695 mIsInActiveDocShell(false),
696 mBuildAsyncZoomContainer(false),
697 mIsRelativeToLayoutViewport(false),
698 mUseOverlayScrollbars(false),
699 mAlwaysLayerizeScrollbars(false) {
700 MOZ_COUNT_CTOR(nsDisplayListBuilder);
702 mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting();
704 ShouldRebuildDisplayListDueToPrefChange();
706 mUseOverlayScrollbars =
707 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
709 mAlwaysLayerizeScrollbars =
710 StaticPrefs::layout_scrollbars_always_layerize_track();
712 static_assert(
713 static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
714 "Check TYPE_MAX should not overflow");
716 mIsReusingStackingContextItems =
717 mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
720 void nsDisplayListBuilder::BeginFrame() {
721 nsCSSRendering::BeginFrameTreesLocked();
723 mIsPaintingToWindow = false;
724 mUseHighQualityScaling = false;
725 mIgnoreSuppression = false;
726 mInTransform = false;
727 mInFilter = false;
728 mSyncDecodeImages = false;
731 void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser* aBrowser,
732 const dom::EffectsInfo& aUpdate) {
733 dom::EffectsInfo update = aUpdate;
734 // For printing we create one display item for each page that an iframe
735 // appears on, the proper visible rect is the union of all the visible rects
736 // we get from each display item.
737 nsPresContext* pc =
738 mReferenceFrame ? mReferenceFrame->PresContext() : nullptr;
739 if (pc && pc->Type() != nsPresContext::eContext_Galley) {
740 Maybe<dom::EffectsInfo> existing = mEffectsUpdates.MaybeGet(aBrowser);
741 if (existing) {
742 // Only the visible rect should differ, the scales should match.
743 MOZ_ASSERT(existing->mRasterScale == aUpdate.mRasterScale &&
744 existing->mTransformToAncestorScale ==
745 aUpdate.mTransformToAncestorScale);
746 if (existing->mVisibleRect) {
747 if (update.mVisibleRect) {
748 update.mVisibleRect =
749 Some(update.mVisibleRect->Union(*existing->mVisibleRect));
750 } else {
751 update.mVisibleRect = existing->mVisibleRect;
756 mEffectsUpdates.InsertOrUpdate(aBrowser, update);
759 void nsDisplayListBuilder::EndFrame() {
760 NS_ASSERTION(!mInInvalidSubtree,
761 "Someone forgot to cleanup mInInvalidSubtree!");
762 mCurrentContainerASR = nullptr;
763 mActiveScrolledRoots.Clear();
764 mEffectsUpdates.Clear();
765 FreeClipChains();
766 FreeTemporaryItems();
767 nsCSSRendering::EndFrameTreesLocked();
770 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
771 const nsIFrame* aStopAtFrame) {
772 mFramesMarkedForDisplay.AppendElement(aFrame);
773 for (nsIFrame* f = aFrame; f;
774 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
775 if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
776 return;
778 f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
779 if (f == aStopAtFrame) {
780 // we've reached a frame that we know will be painted, so we can stop.
781 break;
786 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) {
787 mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
790 static void MarkFrameForDisplayIfVisibleInternal(nsIFrame* aFrame,
791 const nsIFrame* aStopAtFrame) {
792 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
793 if (f->ForceDescendIntoIfVisible()) {
794 return;
796 f->SetForceDescendIntoIfVisible(true);
798 // This condition must match the condition in
799 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
800 // nsLayoutUtils::GetDisplayListParent
801 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
802 nsIFrame* parent = f->GetParent();
803 if (parent && !parent->ForceDescendIntoIfVisible()) {
804 // If the GetDisplayListParent call is going to walk to a placeholder,
805 // in rare cases the placeholder might be contained in a different
806 // continuation from the oof. So we have to make sure to mark the oofs
807 // parent. In the common case this doesn't make us do any extra work,
808 // just changes the order in which we visit the frames since walking
809 // through placeholders will walk through the parent, and we stop when
810 // we find a ForceDescendIntoIfVisible bit set.
811 MarkFrameForDisplayIfVisibleInternal(parent, aStopAtFrame);
815 if (f == aStopAtFrame) {
816 // we've reached a frame that we know will be painted, so we can stop.
817 break;
822 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
823 nsIFrame* aFrame, const nsIFrame* aStopAtFrame) {
824 AddFrameMarkedForDisplayIfVisible(aFrame);
826 MarkFrameForDisplayIfVisibleInternal(aFrame, aStopAtFrame);
829 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
830 mIsRelativeToLayoutViewport = true;
831 UpdateShouldBuildAsyncZoomContainer();
834 void nsDisplayListBuilder::ForceLayerForScrollParent() {
835 mForceLayerForScrollParent = true;
836 mNumActiveScrollframesEncountered++;
839 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
840 const Document* document = mReferenceFrame->PresContext()->Document();
841 mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
842 !document->Fullscreen() &&
843 nsLayoutUtils::AllowZoomingForDocument(document);
846 // Certain prefs may cause display list items to be added or removed when they
847 // are toggled. In those cases, we need to fully rebuild the display list.
848 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
849 // If we transition between wrapping the RCD-RSF contents into an async
850 // zoom container vs. not, we need to rebuild the display list. This only
851 // happens when the zooming or container scrolling prefs are toggled
852 // (manually by the user, or during test setup).
853 bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer;
854 UpdateShouldBuildAsyncZoomContainer();
856 bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars;
857 mUseOverlayScrollbars =
858 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
860 bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars;
861 mAlwaysLayerizeScrollbars =
862 StaticPrefs::layout_scrollbars_always_layerize_track();
864 if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) {
865 return true;
868 if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) {
869 return true;
872 if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) {
873 return true;
876 return false;
879 void nsDisplayListBuilder::AddScrollFrameToNotify(
880 nsIScrollableFrame* aScrollFrame) {
881 mScrollFramesToNotify.insert(aScrollFrame);
884 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
885 for (const auto& it : mScrollFramesToNotify) {
886 it->NotifyApzTransaction();
888 mScrollFramesToNotify.clear();
891 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
892 nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect,
893 const nsRect& aDirtyRect) {
894 MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
895 nsRect dirty;
896 nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
897 this, aFrame, aVisibleRect, aDirtyRect, &dirty);
898 if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
899 visible.IsEmpty()) {
900 return false;
903 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
904 // frame, then it will also mark any outer frames to ensure that building
905 // reaches the dirty feame.
906 if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
907 MarkFrameForDisplay(aFrame, aDirtyFrame);
910 return true;
913 static void UnmarkFrameForDisplay(nsIFrame* aFrame,
914 const nsIFrame* aStopAtFrame) {
915 for (nsIFrame* f = aFrame; f;
916 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
917 if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
918 return;
920 f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
921 if (f == aStopAtFrame) {
922 // we've reached a frame that we know will be painted, so we can stop.
923 break;
928 static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
929 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
930 if (!f->ForceDescendIntoIfVisible()) {
931 return;
933 f->SetForceDescendIntoIfVisible(false);
935 // This condition must match the condition in
936 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
937 // nsLayoutUtils::GetDisplayListParent
938 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
939 nsIFrame* parent = f->GetParent();
940 if (parent && parent->ForceDescendIntoIfVisible()) {
941 // If the GetDisplayListParent call is going to walk to a placeholder,
942 // in rare cases the placeholder might be contained in a different
943 // continuation from the oof. So we have to make sure to mark the oofs
944 // parent. In the common case this doesn't make us do any extra work,
945 // just changes the order in which we visit the frames since walking
946 // through placeholders will walk through the parent, and we stop when
947 // we find a ForceDescendIntoIfVisible bit set.
948 UnmarkFrameForDisplayIfVisible(f);
954 nsDisplayListBuilder::~nsDisplayListBuilder() {
955 NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
956 "All frames should have been unmarked");
957 NS_ASSERTION(mFramesWithOOFData.Length() == 0,
958 "All OOF data should have been removed");
959 NS_ASSERTION(mPresShellStates.Length() == 0,
960 "All presshells should have been exited");
962 DisplayItemClipChain* c = mFirstClipChainToDestroy;
963 while (c) {
964 DisplayItemClipChain* next = c->mNextClipChainToDestroy;
965 c->DisplayItemClipChain::~DisplayItemClipChain();
966 c = next;
969 MOZ_COUNT_DTOR(nsDisplayListBuilder);
972 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
973 uint32_t flags = 0;
974 if (mSyncDecodeImages) {
975 flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
977 if (mIsPaintingToWindow) {
978 flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
980 if (mUseHighQualityScaling) {
981 flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING;
983 return flags;
986 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
987 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
988 uint32_t flags = 0;
989 if (mSyncDecodeImages) {
990 flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
992 if (mIsPaintingToWindow) {
993 flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
995 if (mUseHighQualityScaling) {
996 flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
998 return flags;
1001 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1002 uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
1003 if (mSyncDecodeImages) {
1004 flags |= imgIContainer::FLAG_SYNC_DECODE;
1005 } else {
1006 flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
1008 if (mIsPaintingToWindow || mUseHighQualityScaling) {
1009 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1011 return flags;
1014 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
1015 const nsRegion& aRegion) {
1016 if (aRegion.IsEmpty()) {
1017 return;
1020 nsRegion tmp;
1021 tmp.Sub(*aVisibleRegion, aRegion);
1022 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1023 // to its bounds either, which can be very bad (see bug 516740).
1024 // Do let aVisibleRegion get more complex if by doing so we reduce its
1025 // area by at least half.
1026 if (tmp.GetNumRects() <= 15 || tmp.Area() <= aVisibleRegion->Area() / 2) {
1027 *aVisibleRegion = tmp;
1031 nsCaret* nsDisplayListBuilder::GetCaret() {
1032 RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
1033 return caret;
1036 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) {
1037 if (mIsPaintingToWindow) {
1038 aPresShell->IncrementPaintCount();
1042 void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
1043 bool aPointerEventsNoneDoc) {
1044 PresShellState* state = mPresShellStates.AppendElement();
1045 state->mPresShell = aReferenceFrame->PresShell();
1046 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
1047 state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
1049 nsIScrollableFrame* sf = state->mPresShell->GetRootScrollFrameAsScrollable();
1050 if (sf && IsInSubdocument()) {
1051 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1052 // that the canvas background color will be set correctly, and that only one
1053 // unscrollable item will be created.
1054 // This is done to avoid, for example, a case where only scrollbar frames
1055 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1056 // and possibly end up with an extra nsDisplaySolidColor item.
1057 // We skip this for the root document, since we don't want to use
1058 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1059 // do it manually there.
1060 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
1061 if (canvasFrame) {
1062 MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
1066 #ifdef DEBUG
1067 state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
1068 nsLayoutPhase::DisplayListBuilding);
1069 #endif
1071 state->mPresShell->UpdateCanvasBackground();
1073 bool buildCaret = mBuildCaret;
1074 if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
1075 state->mIsBackgroundOnly = false;
1076 } else {
1077 state->mIsBackgroundOnly = true;
1078 buildCaret = false;
1081 bool pointerEventsNone = aPointerEventsNoneDoc;
1082 if (IsInSubdocument()) {
1083 pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
1084 .mInsidePointerEventsNoneDoc;
1086 state->mInsidePointerEventsNoneDoc = pointerEventsNone;
1088 state->mPresShellIgnoreScrollFrame =
1089 state->mPresShell->IgnoringViewportScrolling()
1090 ? state->mPresShell->GetRootScrollFrame()
1091 : nullptr;
1093 nsPresContext* pc = aReferenceFrame->PresContext();
1094 mIsInChromePresContext = pc->IsChrome();
1095 nsIDocShell* docShell = pc->GetDocShell();
1097 if (docShell) {
1098 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1101 state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);
1103 if (!buildCaret) {
1104 return;
1107 RefPtr<nsCaret> caret = state->mPresShell->GetCaret();
1108 // This code run for each pres shell and caret->GetPaintGeometry
1109 // will return nullptr for invisible caret. So only one caret
1110 // can be painted at a time.
1111 state->mCaretFrame = caret->GetPaintGeometry(&mCaretRect);
1113 // Check if the display root for the caret matches the display
1114 // root that we're painting, and only use it if it matches. Likely
1115 // we only need this for popup.
1116 if (state->mCaretFrame &&
1117 nsLayoutUtils::GetDisplayRootFrame(state->mCaretFrame) !=
1118 nsLayoutUtils::GetDisplayRootFrame(aReferenceFrame)) {
1119 state->mCaretFrame = nullptr;
1122 // Caret frames add visual area to their frame, but we don't update the
1123 // overflow area. Use flags to make sure we build display items for that frame
1124 // instead.
1125 if (state->mCaretFrame) {
1126 MOZ_ASSERT(state->mCaretFrame->PresShell() == state->mPresShell);
1127 MarkFrameForDisplay(state->mCaretFrame, aReferenceFrame);
1131 // A non-blank paint is a paint that does not just contain the canvas
1132 // background.
1133 static bool DisplayListIsNonBlank(nsDisplayList* aList) {
1134 for (nsDisplayItem* i : *aList) {
1135 switch (i->GetType()) {
1136 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
1137 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
1138 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
1139 continue;
1140 case DisplayItemType::TYPE_SOLID_COLOR:
1141 case DisplayItemType::TYPE_BACKGROUND:
1142 case DisplayItemType::TYPE_BACKGROUND_COLOR:
1143 if (i->Frame()->IsCanvasFrame()) {
1144 continue;
1146 return true;
1147 default:
1148 return true;
1151 return false;
1154 // A contentful paint is a paint that does contains DOM content (text,
1155 // images, non-blank canvases, SVG): "First Contentful Paint entry
1156 // contains a DOMHighResTimeStamp reporting the time when the browser
1157 // first rendered any text, image (including background images),
1158 // non-white canvas or SVG. This excludes any content of iframes, but
1159 // includes text with pending webfonts. This is the first time users
1160 // could start consuming page content."
1161 static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
1162 nsDisplayList* aList) {
1163 for (nsDisplayItem* i : *aList) {
1164 DisplayItemType type = i->GetType();
1165 nsDisplayList* children = i->GetChildren();
1167 switch (type) {
1168 case DisplayItemType::TYPE_SUBDOCUMENT: // iframes are ignored
1169 break;
1170 // CANVASes check if they may have been modified (as a stand-in
1171 // actually tracking all modifications)
1172 default:
1173 if (i->IsContentful()) {
1174 bool dummy;
1175 nsRect bound = i->GetBounds(aBuilder, &dummy);
1176 if (!bound.IsEmpty()) {
1177 return true;
1180 if (children) {
1181 if (DisplayListIsContentful(aBuilder, children)) {
1182 return true;
1185 break;
1188 return false;
1191 void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
1192 nsDisplayList* aPaintedContents) {
1193 NS_ASSERTION(
1194 CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
1195 "Presshell mismatch");
1197 if (mIsPaintingToWindow && aPaintedContents) {
1198 nsPresContext* pc = aReferenceFrame->PresContext();
1199 if (!pc->HadNonBlankPaint()) {
1200 if (!CurrentPresShellState()->mIsBackgroundOnly &&
1201 DisplayListIsNonBlank(aPaintedContents)) {
1202 pc->NotifyNonBlankPaint();
1205 nsRootPresContext* rootPresContext = pc->GetRootPresContext();
1206 if (!pc->HasStoppedGeneratingLCP() && rootPresContext) {
1207 if (!CurrentPresShellState()->mIsBackgroundOnly) {
1208 if (pc->HasEverBuiltInvisibleText() ||
1209 DisplayListIsContentful(this, aPaintedContents)) {
1210 pc->NotifyContentfulPaint();
1216 ResetMarkedFramesForDisplayList(aReferenceFrame);
1217 mPresShellStates.RemoveLastElement();
1219 if (!mPresShellStates.IsEmpty()) {
1220 nsPresContext* pc = CurrentPresContext();
1221 nsIDocShell* docShell = pc->GetDocShell();
1222 if (docShell) {
1223 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1225 mIsInChromePresContext = pc->IsChrome();
1226 } else {
1227 for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
1228 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
1230 mFramesMarkedForDisplayIfVisible.SetLength(0);
1234 void nsDisplayListBuilder::FreeClipChains() {
1235 // Iterate the clip chains from newest to oldest (forward
1236 // iteration), so that we destroy descendants first which
1237 // will drop the ref count on their ancestors.
1238 DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;
1240 while (*indirect) {
1241 if (!(*indirect)->mRefCount) {
1242 DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;
1244 mClipDeduplicator.erase(*indirect);
1245 (*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
1246 Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);
1248 *indirect = next;
1249 } else {
1250 indirect = &(*indirect)->mNextClipChainToDestroy;
1255 void nsDisplayListBuilder::FreeTemporaryItems() {
1256 for (nsDisplayItem* i : mTemporaryItems) {
1257 // Temporary display items are not added to the frames.
1258 MOZ_ASSERT(i->Frame());
1259 i->RemoveFrame(i->Frame());
1260 i->Destroy(this);
1263 mTemporaryItems.Clear();
1266 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1267 const nsIFrame* aReferenceFrame) {
1268 // Unmark and pop off the frames marked for display in this pres shell.
1269 uint32_t firstFrameForShell =
1270 CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1271 for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
1272 ++i) {
1273 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
1275 mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1277 firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
1278 for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
1279 mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
1281 mFramesWithOOFData.SetLength(firstFrameForShell);
1284 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1285 CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
1288 void nsDisplayListBuilder::MarkFramesForDisplayList(
1289 nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
1290 nsRect visibleRect = GetVisibleRect();
1291 nsRect dirtyRect = GetDirtyRect();
1293 // If we are entering content that is fixed to the RCD-RSF, we are
1294 // crossing the async zoom container boundary, and need to convert from
1295 // visual to layout coordinates.
1296 if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
1297 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1298 viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
1299 if (viewportFrame->PresShell()->GetRootScrollFrame()) {
1300 #ifdef DEBUG
1301 for (nsIFrame* f : aFrames) {
1302 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
1304 #endif
1305 visibleRect = ViewportUtils::VisualToLayout(visibleRect,
1306 viewportFrame->PresShell());
1307 dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
1308 viewportFrame->PresShell());
1310 #ifdef DEBUG
1311 else {
1312 // This is an edge case that should only happen if we are in a
1313 // document with a XUL root element so that it does not have a root
1314 // scroll frame but it has fixed pos content and all of the frames in
1315 // aFrames are that fixed pos content.
1316 for (nsIFrame* f : aFrames) {
1317 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
1318 f->GetParent() == aDirtyFrame &&
1319 f->StyleDisplay()->mPosition ==
1320 StylePositionProperty::Fixed);
1322 // There's no root scroll frame so there can't be any zooming or async
1323 // panning so we don't need to adjust the visible and dirty rects.
1325 #endif
1329 bool markedFrames = false;
1330 for (nsIFrame* e : aFrames) {
1331 // Skip the AccessibleCaret frame when building no caret.
1332 if (!IsBuildingCaret()) {
1333 nsIContent* content = e->GetContent();
1334 if (content && content->IsInNativeAnonymousSubtree() &&
1335 content->IsElement()) {
1336 const nsAttrValue* classes = content->AsElement()->GetClasses();
1337 if (classes &&
1338 classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) {
1339 continue;
1343 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
1344 markedFrames = true;
1348 if (markedFrames) {
1349 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1350 // to objects on the stack, so we need to clone the chain.
1351 const DisplayItemClipChain* clipChain =
1352 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1353 const DisplayItemClipChain* combinedClipChain =
1354 mClipState.GetCurrentCombinedClipChain(this);
1355 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1357 OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
1358 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1359 visibleRect, dirtyRect);
1360 aDirtyFrame->SetProperty(
1361 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
1362 mFramesWithOOFData.AppendElement(aDirtyFrame);
1365 if (!aDirtyFrame->GetParent()) {
1366 // This is the viewport frame of aDirtyFrame's presshell.
1367 // Store the current display data so that it can be used for fixed
1368 // background images.
1369 NS_ASSERTION(
1370 CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
1371 "Presshell mismatch");
1372 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
1373 "already traversed this presshell's root frame?");
1375 const DisplayItemClipChain* clipChain =
1376 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1377 const DisplayItemClipChain* combinedClipChain =
1378 mClipState.GetCurrentCombinedClipChain(this);
1379 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1380 CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
1381 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1382 GetVisibleRect(), GetDirtyRect());
1387 * Mark all preserve-3d children with
1388 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1389 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1390 * dirty rect for preserve-3d children.
1392 * @param aDirtyFrame is the frame to mark children extending context.
1394 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1395 nsIFrame* aDirtyFrame) {
1396 for (const auto& childList : aDirtyFrame->ChildLists()) {
1397 for (nsIFrame* child : childList.mList) {
1398 if (child->Combines3DTransformWithAncestors()) {
1399 MarkFrameForDisplay(child, aDirtyFrame);
1402 if (child->IsBlockWrapper()) {
1403 // Mark preserve-3d frames inside the block wrapper.
1404 MarkPreserve3DFramesForDisplayList(child);
1410 ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1411 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame) {
1412 RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
1413 aParent, aScrollableFrame, IsRetainingDisplayList());
1414 mActiveScrolledRoots.AppendElement(asr);
1415 return asr;
1418 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1419 const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
1420 const DisplayItemClipChain* aParent) {
1421 MOZ_DIAGNOSTIC_ASSERT(!(aParent && aParent->mOnStack));
1422 void* p = Allocate(sizeof(DisplayItemClipChain),
1423 DisplayListArenaObjectId::CLIPCHAIN);
1424 DisplayItemClipChain* c = new (KnownNotNull, p)
1425 DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy);
1426 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
1427 c->mOnStack = false;
1428 #endif
1429 auto result = mClipDeduplicator.insert(c);
1430 if (!result.second) {
1431 // An equivalent clip chain item was already created, so let's return that
1432 // instead. Destroy the one we just created.
1433 // Note that this can cause clip chains from different coordinate systems to
1434 // collapse into the same clip chain object, because clip chains do not keep
1435 // track of the reference frame that they were created in.
1436 c->DisplayItemClipChain::~DisplayItemClipChain();
1437 Destroy(DisplayListArenaObjectId::CLIPCHAIN, c);
1438 return *(result.first);
1440 mFirstClipChainToDestroy = c;
1441 return c;
1444 struct ClipChainItem {
1445 DisplayItemClip clip;
1446 const ActiveScrolledRoot* asr;
1449 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
1450 const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
1451 for (const ActiveScrolledRoot* asr =
1452 ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
1453 asr; asr = asr->mParent) {
1454 if (aOne == aTwo) {
1455 return aOne;
1457 if (aOne->mASR == asr) {
1458 aOne = aOne->mParent;
1460 if (aTwo->mASR == asr) {
1461 aTwo = aTwo->mParent;
1463 if (!aOne) {
1464 return aTwo;
1466 if (!aTwo) {
1467 return aOne;
1470 return nullptr;
1473 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1474 const DisplayItemClipChain* aAncestor,
1475 const DisplayItemClipChain* aLeafClip1,
1476 const DisplayItemClipChain* aLeafClip2) {
1477 AutoTArray<ClipChainItem, 8> intersectedClips;
1479 const DisplayItemClipChain* clip1 = aLeafClip1;
1480 const DisplayItemClipChain* clip2 = aLeafClip2;
1482 const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
1483 clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);
1485 // Build up the intersection from the leaf to the root and put it into
1486 // intersectedClips. The loop below will convert intersectedClips into an
1487 // actual DisplayItemClipChain.
1488 // (We need to do this in two passes because we need the parent clip in order
1489 // to create the DisplayItemClipChain object, but the parent clip has not
1490 // been created at that point.)
1491 while (!aAncestor || asr != aAncestor->mASR) {
1492 if (clip1 && clip1->mASR == asr) {
1493 if (clip2 && clip2->mASR == asr) {
1494 DisplayItemClip intersection = clip1->mClip;
1495 intersection.IntersectWith(clip2->mClip);
1496 intersectedClips.AppendElement(ClipChainItem{intersection, asr});
1497 clip2 = clip2->mParent;
1498 } else {
1499 intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
1501 clip1 = clip1->mParent;
1502 } else if (clip2 && clip2->mASR == asr) {
1503 intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
1504 clip2 = clip2->mParent;
1506 if (!asr) {
1507 MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
1508 break;
1510 asr = asr->mParent;
1513 // Convert intersectedClips into a DisplayItemClipChain.
1514 const DisplayItemClipChain* parentSC = aAncestor;
1515 for (auto& sc : Reversed(intersectedClips)) {
1516 parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
1518 return parentSC;
1521 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1522 const DisplayItemClipChain* aLeafClip1,
1523 const DisplayItemClipChain* aLeafClip2) {
1524 // aLeafClip2 might be a reference to a clip on the stack. We need to make
1525 // sure that CreateClipChainIntersection will allocate the actual intersected
1526 // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1527 // we supply nullptr as the common ancestor so that
1528 // CreateClipChainIntersection clones the whole chain.
1529 const DisplayItemClipChain* ancestorClip =
1530 aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
1531 : nullptr;
1533 return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2);
1536 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
1537 const DisplayItemClipChain* aClipChain) {
1538 return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
1541 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
1542 const nsIFrame* aFrame, nsPoint* aOffset) const {
1543 auto MaybeApplyAdditionalOffset = [&]() {
1544 if (auto offset = AdditionalOffset()) {
1545 *aOffset += *offset;
1549 if (aFrame == mCurrentFrame) {
1550 if (aOffset) {
1551 *aOffset = mCurrentOffsetToReferenceFrame;
1553 return mCurrentReferenceFrame;
1556 for (const nsIFrame* f = aFrame; f;
1557 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
1558 if (f == mReferenceFrame || f->IsTransformed()) {
1559 if (aOffset) {
1560 *aOffset = aFrame->GetOffsetToCrossDoc(f);
1561 MaybeApplyAdditionalOffset();
1563 return f;
1567 if (aOffset) {
1568 *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1569 MaybeApplyAdditionalOffset();
1572 return mReferenceFrame;
1575 // Sticky frames are active if their nearest scrollable frame is also active.
1576 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
1577 nsIFrame* aFrame) {
1578 MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
1579 StylePositionProperty::Sticky);
1581 StickyScrollContainer* stickyScrollContainer =
1582 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
1583 return stickyScrollContainer &&
1584 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1587 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
1588 nsIFrame** aParent) {
1589 if (aFrame == mReferenceFrame) {
1590 return true;
1593 if (!IsPaintingToWindow()) {
1594 if (aParent) {
1595 *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1597 return false;
1600 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1601 if (!parent) {
1602 return true;
1604 *aParent = parent;
1606 if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
1607 IsStickyFrameActive(this, aFrame)) {
1608 return true;
1611 if (aFrame->IsTransformed()) {
1612 if (EffectCompositor::HasAnimationsForCompositor(
1613 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
1614 return true;
1618 LayoutFrameType parentType = parent->Type();
1619 if (parentType == LayoutFrameType::Scroll ||
1620 parentType == LayoutFrameType::ListControl) {
1621 nsIScrollableFrame* sf = do_QueryFrame(parent);
1622 if (sf->GetScrolledFrame() == aFrame) {
1623 MOZ_ASSERT(!aFrame->IsTransformed());
1624 return sf->IsMaybeAsynchronouslyScrolled();
1628 return false;
1631 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1632 nsIFrame* aFrame) {
1633 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1634 RootReferenceFrame(), aFrame));
1635 nsIFrame* cursor = aFrame;
1636 while (cursor != RootReferenceFrame()) {
1637 nsIFrame* next;
1638 if (IsAnimatedGeometryRoot(cursor, &next)) {
1639 return cursor;
1641 cursor = next;
1643 return cursor;
1646 static nsRect ApplyAllClipNonRoundedIntersection(
1647 const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
1648 nsRect result = aRect;
1649 while (aClipChain) {
1650 result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
1651 aClipChain = aClipChain->mParent;
1653 return result;
1656 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
1657 if (!mWindowDraggingAllowed || !IsForPainting()) {
1658 return;
1661 const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
1662 if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
1663 // This frame has the default value and doesn't influence the window
1664 // dragging region.
1665 return;
1668 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;
1670 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1671 nsIFrame* referenceFrame =
1672 const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1674 if (IsInTransform()) {
1675 // Only support 2d rectilinear transforms. Transform support is needed for
1676 // the horizontal flip transform that's applied to the urlbar textbox in
1677 // RTL mode - it should be able to exclude itself from the draggable region.
1678 referenceFrameToRootReferenceFrame =
1679 ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
1680 nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
1681 RelativeTo{mReferenceFrame})
1682 .GetMatrix());
1683 Matrix referenceFrameToRootReferenceFrame2d;
1684 if (!referenceFrameToRootReferenceFrame.Is2D(
1685 &referenceFrameToRootReferenceFrame2d) ||
1686 !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1687 return;
1689 } else {
1690 MOZ_ASSERT(referenceFrame == mReferenceFrame,
1691 "referenceFrameToRootReferenceFrame needs to be adjusted");
1694 // We do some basic visibility checking on the frame's border box here.
1695 // We intersect it both with the current dirty rect and with the current
1696 // clip. Either one is just a conservative approximation on its own, but
1697 // their intersection luckily works well enough for our purposes, so that
1698 // we don't have to do full-blown visibility computations.
1699 // The most important case we need to handle is the scrolled-off tab:
1700 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1701 // should not be allowed to interfere with the window dragging region. Using
1702 // just the current DisplayItemClip is not enough to cover this case
1703 // completely because clips are reset while building stacking context
1704 // contents, so for example we'd fail to clip frames that have a clip path
1705 // applied to them. But the current dirty rect doesn't get reset in that
1706 // case, so we use it to make this case work.
1707 nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
1708 borderBox += ToReferenceFrame(aFrame);
1709 const DisplayItemClipChain* clip =
1710 ClipState().GetCurrentCombinedClipChain(this);
1711 borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
1712 if (borderBox.IsEmpty()) {
1713 return;
1716 LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
1717 borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1719 LayoutDeviceRect transformedDevPixelBorderBox =
1720 TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1721 transformedDevPixelBorderBox.Round();
1722 LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1724 if (!transformedDevPixelBorderBox.ToIntRect(
1725 &transformedDevPixelBorderBoxInt)) {
1726 return;
1729 LayoutDeviceIntRegion& region =
1730 styleUI->mWindowDragging == StyleWindowDragging::Drag
1731 ? mWindowDraggingRegion
1732 : mWindowNoDraggingRegion;
1734 if (!IsRetainingDisplayList()) {
1735 region.OrWith(transformedDevPixelBorderBoxInt);
1736 return;
1739 gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
1740 if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
1741 mRetainedWindowDraggingRegion.Add(aFrame, rect);
1742 } else {
1743 mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
1747 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
1748 LayoutDeviceIntRegion result;
1749 if (!IsRetainingDisplayList()) {
1750 result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
1751 return result;
1754 LayoutDeviceIntRegion dragRegion =
1755 mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();
1757 LayoutDeviceIntRegion noDragRegion =
1758 mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();
1760 result.Sub(dragRegion, noDragRegion);
1761 return result;
1764 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1765 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes);
1766 aSizes.mLayoutRetainedDisplayListSize +=
1767 aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get());
1770 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1771 mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);
1773 size_t n = 0;
1774 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
1775 n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1776 n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1777 n += mEffectsUpdates.ShallowSizeOfExcludingThis(mallocSizeOf);
1778 n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1779 n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1780 n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
1781 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1783 aSizes.mLayoutRetainedDisplayListSize += n;
1786 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1787 for (nsDisplayItem* item : *this) {
1788 item->AddSizeOfExcludingThis(aSizes);
1789 if (RetainedDisplayList* children = item->GetChildren()) {
1790 children->AddSizeOfExcludingThis(aSizes);
1794 size_t n = 0;
1796 n += mDAG.mDirectPredecessorList.ShallowSizeOfExcludingThis(
1797 aSizes.mState.mMallocSizeOf);
1798 n += mDAG.mNodesInfo.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1799 n += mOldItems.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1801 aSizes.mLayoutRetainedDisplayListSize += n;
1804 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1805 MallocSizeOf aMallocSizeOf) const {
1806 size_t n = 0;
1807 n += mFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
1808 for (const auto& frame : mFrames) {
1809 const UniquePtr<WeakFrame>& weakFrame = frame.mWeakFrame;
1810 n += aMallocSizeOf(weakFrame.get());
1812 n += mRects.ShallowSizeOfExcludingThis(aMallocSizeOf);
1813 return n;
1817 * Removes modified frames and rects from this WeakFrameRegion.
1819 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1820 MOZ_ASSERT(mFrames.Length() == mRects.Length());
1822 uint32_t i = 0;
1823 uint32_t length = mFrames.Length();
1825 while (i < length) {
1826 auto& wrapper = mFrames[i];
1828 if (!wrapper.mWeakFrame->IsAlive() ||
1829 AnyContentAncestorModified(wrapper.mWeakFrame->GetFrame())) {
1830 // To avoid multiple O(n) shifts in the array, move the last element of
1831 // the array to the current position and decrease the array length.
1832 mFrameSet.Remove(wrapper.mFrame);
1833 mFrames[i] = std::move(mFrames[length - 1]);
1834 mRects[i] = std::move(mRects[length - 1]);
1835 length--;
1836 } else {
1837 i++;
1841 mFrames.TruncateLength(length);
1842 mRects.TruncateLength(length);
1845 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1846 mRetainedWindowDraggingRegion.RemoveModifiedFramesAndRects();
1847 mRetainedWindowNoDraggingRegion.RemoveModifiedFramesAndRects();
1848 mRetainedWindowOpaqueRegion.RemoveModifiedFramesAndRects();
1851 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1852 mRetainedWindowDraggingRegion.Clear();
1853 mRetainedWindowNoDraggingRegion.Clear();
1854 mRetainedWindowOpaqueRegion.Clear();
1857 const uint32_t gWillChangeAreaMultiplier = 3;
1858 static uint32_t GetLayerizationCost(const nsSize& aSize) {
1859 // There's significant overhead for each layer created from Gecko
1860 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1861 // Therefore we set a minimum cost threshold of a 64x64 area.
1862 const int minBudgetCost = 64 * 64;
1864 const uint32_t budgetCost = std::max(
1865 minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
1866 nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
1868 return budgetCost;
1871 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
1872 const nsSize& aSize) {
1873 MOZ_ASSERT(IsForPainting());
1875 if (aFrame->MayHaveWillChangeBudget()) {
1876 // The frame is already in the will-change budget.
1877 return true;
1880 const nsPresContext* presContext = aFrame->PresContext();
1881 const nsRect area = presContext->GetVisibleArea();
1882 const uint32_t budgetLimit =
1883 nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1884 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1885 const uint32_t cost = GetLayerizationCost(aSize);
1887 DocumentWillChangeBudget& documentBudget =
1888 mDocumentWillChangeBudgets.LookupOrInsert(presContext);
1890 const bool onBudget =
1891 (documentBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
1893 if (onBudget) {
1894 documentBudget += cost;
1895 mFrameWillChangeBudgets.InsertOrUpdate(
1896 aFrame, FrameWillChangeBudget(presContext, cost));
1897 aFrame->SetMayHaveWillChangeBudget(true);
1900 return onBudget;
1903 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
1904 const nsSize& aSize) {
1905 if (!IsForPainting()) {
1906 // If this nsDisplayListBuilder is not for painting, the layerization should
1907 // not matter. Do the simple thing and return false.
1908 return false;
1911 const bool onBudget = AddToWillChangeBudget(aFrame, aSize);
1912 if (onBudget) {
1913 return true;
1916 auto* pc = aFrame->PresContext();
1917 auto* doc = pc->Document();
1918 if (!doc->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget)) {
1919 AutoTArray<nsString, 2> params;
1920 params.AppendElement()->AppendInt(gWillChangeAreaMultiplier);
1922 nsRect area = pc->GetVisibleArea();
1923 uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1924 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1925 params.AppendElement()->AppendInt(budgetLimit);
1927 doc->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget, false, params);
1930 return false;
1933 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame* aFrame) {
1934 MOZ_ASSERT(IsForPainting());
1936 if (!aFrame->MayHaveWillChangeBudget()) {
1937 return;
1940 aFrame->SetMayHaveWillChangeBudget(false);
1941 RemoveFromWillChangeBudgets(aFrame);
1944 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame* aFrame) {
1945 if (auto entry = mFrameWillChangeBudgets.Lookup(aFrame)) {
1946 const FrameWillChangeBudget& frameBudget = entry.Data();
1948 auto documentBudget =
1949 mDocumentWillChangeBudgets.Lookup(frameBudget.mPresContext);
1951 if (documentBudget) {
1952 *documentBudget -= frameBudget.mUsage;
1955 entry.Remove();
1959 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1960 mFrameWillChangeBudgets.Clear();
1961 mDocumentWillChangeBudgets.Clear();
1964 void nsDisplayListBuilder::EnterSVGEffectsContents(
1965 nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) {
1966 MOZ_ASSERT(aHoistedItemsStorage);
1967 if (mSVGEffectsFrames.IsEmpty()) {
1968 MOZ_ASSERT(!mScrollInfoItemsForHoisting);
1969 mScrollInfoItemsForHoisting = aHoistedItemsStorage;
1971 mSVGEffectsFrames.AppendElement(aEffectsFrame);
1974 void nsDisplayListBuilder::ExitSVGEffectsContents() {
1975 MOZ_ASSERT(!mSVGEffectsFrames.IsEmpty());
1976 mSVGEffectsFrames.RemoveLastElement();
1977 MOZ_ASSERT(mScrollInfoItemsForHoisting);
1978 if (mSVGEffectsFrames.IsEmpty()) {
1979 mScrollInfoItemsForHoisting = nullptr;
1983 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
1985 * Note: if changing the conditions under which scroll info layers
1986 * are created, make a corresponding change to
1987 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
1989 for (nsIFrame* frame : mSVGEffectsFrames) {
1990 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame)) {
1991 return true;
1994 return false;
1997 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
1998 nsDisplayScrollInfoLayer* aScrollInfoItem) {
1999 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2000 MOZ_ASSERT(mScrollInfoItemsForHoisting);
2001 mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
2004 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2005 nsIFrame* aFrame, nsDisplayList* aList) {
2006 MOZ_ASSERT(aFrame);
2007 MOZ_ASSERT(aList);
2009 if (!BuildCompositorHitTestInfo()) {
2010 return;
2013 const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
2014 if (info != CompositorHitTestInvisibleToHit) {
2015 aList->AppendNewToTop<nsDisplayCompositorHitTestInfo>(this, aFrame);
2019 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem* aItem) {
2020 mReuseableItems.Insert(aItem);
2023 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem* aItem) {
2024 MOZ_ASSERT(aItem->IsReusedItem());
2025 mReuseableItems.Remove(aItem);
2028 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2029 const size_t total = mReuseableItems.Count();
2031 size_t reused = 0;
2032 for (auto* item : mReuseableItems) {
2033 if (item->IsReusedItem()) {
2034 reused++;
2035 item->SetReusable();
2036 } else {
2037 item->Destroy(this);
2041 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
2042 mReuseableItems.Clear();
2045 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
2046 const auto* previous = mCurrentContainerASR;
2047 const auto* asr = aItem->GetActiveScrolledRoot();
2048 mCurrentContainerASR =
2049 ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
2051 if (previous != mCurrentContainerASR) {
2052 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
2053 mCurrentContainerASR);
2056 aItem->SetReusedItem();
2059 void nsDisplayListSet::CopyTo(const nsDisplayListSet& aDestination) const {
2060 for (size_t i = 0; i < mLists.size(); ++i) {
2061 auto* from = mLists[i];
2062 auto* to = aDestination.mLists[i];
2064 from->CopyTo(to);
2068 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
2069 aDestination.BorderBackground()->AppendToTop(BorderBackground());
2070 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2071 aDestination.Floats()->AppendToTop(Floats());
2072 aDestination.Content()->AppendToTop(Content());
2073 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
2074 aDestination.Outlines()->AppendToTop(Outlines());
2077 nsRect nsDisplayList::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2078 nsRect bounds;
2079 for (nsDisplayItem* i : *this) {
2080 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
2082 return bounds;
2085 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR(
2086 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
2087 nsRect* aBuildingRect) const {
2088 nsRect bounds;
2089 for (nsDisplayItem* i : *this) {
2090 nsRect r = i->GetClippedBounds(aBuilder);
2091 if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
2092 if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) {
2093 r = clip.ref();
2096 if (aBuildingRect) {
2097 aBuildingRect->UnionRect(*aBuildingRect, i->GetBuildingRect());
2099 bounds.UnionRect(bounds, r);
2101 return bounds;
2104 nsRect nsDisplayList::GetBuildingRect() const {
2105 nsRect result;
2106 for (nsDisplayItem* i : *this) {
2107 result.UnionRect(result, i->GetBuildingRect());
2109 return result;
2112 WindowRenderer* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView** aView) {
2113 if (aView) {
2114 *aView = RootReferenceFrame()->GetView();
2116 if (RootReferenceFrame() !=
2117 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2118 return nullptr;
2120 nsIWidget* window = RootReferenceFrame()->GetNearestWidget();
2121 if (window) {
2122 return window->GetWindowRenderer();
2124 return nullptr;
2127 WebRenderLayerManager* nsDisplayListBuilder::GetWidgetLayerManager(
2128 nsView** aView) {
2129 WindowRenderer* renderer = GetWidgetWindowRenderer();
2130 if (renderer) {
2131 return renderer->AsWebRender();
2133 return nullptr;
2136 void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2137 int32_t aAppUnitsPerDevPixel) {
2138 FlattenedDisplayListIterator iter(aBuilder, this);
2139 while (iter.HasNext()) {
2140 nsPaintedDisplayItem* item = iter.GetNextItem()->AsPaintedDisplayItem();
2141 if (!item) {
2142 continue;
2145 nsRect visible = item->GetClippedBounds(aBuilder);
2146 visible = visible.Intersect(item->GetPaintRect(aBuilder, aCtx));
2147 if (visible.IsEmpty()) {
2148 continue;
2151 DisplayItemClip currentClip = item->GetClip();
2152 if (currentClip.HasClip()) {
2153 aCtx->Save();
2154 if (currentClip.IsRectClippedByRoundedCorner(visible)) {
2155 currentClip.ApplyTo(aCtx, aAppUnitsPerDevPixel);
2156 } else {
2157 currentClip.ApplyRectTo(aCtx, aAppUnitsPerDevPixel);
2160 aCtx->NewPath();
2162 item->Paint(aBuilder, aCtx);
2164 if (currentClip.HasClip()) {
2165 aCtx->Restore();
2171 * We paint by executing a layer manager transaction, constructing a
2172 * single layer representing the display list, and then making it the
2173 * root of the layer manager, drawing into the PaintedLayers.
2175 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2176 uint32_t aFlags,
2177 Maybe<double> aDisplayListBuildTime) {
2178 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS);
2180 RefPtr<WebRenderLayerManager> layerManager;
2181 WindowRenderer* renderer = nullptr;
2182 bool widgetTransaction = false;
2183 bool doBeginTransaction = true;
2184 nsView* view = nullptr;
2185 if (aFlags & PAINT_USE_WIDGET_LAYERS) {
2186 renderer = aBuilder->GetWidgetWindowRenderer(&view);
2187 if (renderer) {
2188 // The fallback renderer doesn't retain any content, so it's
2189 // not meaningful to use it when drawing to an external context.
2190 if (aCtx && renderer->AsFallback()) {
2191 MOZ_ASSERT(!(aFlags & PAINT_EXISTING_TRANSACTION));
2192 renderer = nullptr;
2193 } else {
2194 layerManager = renderer->AsWebRender();
2195 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
2196 widgetTransaction = true;
2201 nsIFrame* frame = aBuilder->RootReferenceFrame();
2202 nsPresContext* presContext = frame->PresContext();
2203 PresShell* presShell = presContext->PresShell();
2204 Document* document = presShell->GetDocument();
2206 ScopeExit g([&]() {
2207 #ifdef DEBUG
2208 MOZ_ASSERT(!layerManager || !layerManager->GetTarget());
2209 #endif
2211 // For layers-free mode, we check the invalidation state bits in the
2212 // EndTransaction. So we clear the invalidation state bits after
2213 // EndTransaction.
2214 if (widgetTransaction ||
2215 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2216 // but they still need the invalidation state bits cleared in order for
2217 // invalidation for CSS/SMIL animation to work properly.
2218 (document && document->IsBeingUsedAsImage())) {
2219 DL_LOGD("Clearing invalidation state bits");
2220 frame->ClearInvalidationStateBits();
2224 if (!renderer) {
2225 if (!aCtx) {
2226 NS_WARNING("Nowhere to paint into");
2227 return;
2229 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(false);
2230 Paint(aBuilder, aCtx, presContext->AppUnitsPerDevPixel());
2232 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2233 return;
2236 if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) {
2237 MOZ_ASSERT(layerManager);
2238 if (doBeginTransaction) {
2239 if (aCtx) {
2240 if (!layerManager->BeginTransactionWithTarget(aCtx, nsCString())) {
2241 return;
2243 } else {
2244 if (!layerManager->BeginTransaction(nsCString())) {
2245 return;
2250 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(true);
2251 layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
2253 bool sent = false;
2254 if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
2255 MOZ_ASSERT(!aCtx);
2256 sent = layerManager->EndEmptyTransaction();
2259 if (!sent) {
2260 auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
2262 nsIDocShell* docShell = presContext->GetDocShell();
2263 WrFiltersHolder wrFilters;
2264 gfx::Matrix5x4* colorMatrix =
2265 nsDocShell::Cast(docShell)->GetColorMatrix();
2266 if (colorMatrix) {
2267 wrFilters.filters.AppendElement(
2268 wr::FilterOp::ColorMatrix(colorMatrix->components));
2271 wrManager->EndTransactionWithoutLayer(this, aBuilder,
2272 std::move(wrFilters), nullptr,
2273 aDisplayListBuildTime.valueOr(0.0));
2276 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2277 if (presContext->RefreshDriver()->HasScheduleFlush()) {
2278 presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2279 frame->GetRect());
2282 return;
2285 FallbackRenderer* fallback = renderer->AsFallback();
2286 MOZ_ASSERT(fallback);
2288 if (doBeginTransaction) {
2289 MOZ_ASSERT(!aCtx);
2290 if (!fallback->BeginTransaction()) {
2291 return;
2295 bool temp = aBuilder->SetIsCompositingCheap(renderer->IsCompositingCheap());
2296 fallback->EndTransactionWithList(aBuilder, this,
2297 presContext->AppUnitsPerDevPixel(),
2298 WindowRenderer::END_DEFAULT);
2300 aBuilder->SetIsCompositingCheap(temp);
2303 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
2304 for (auto* item : TakeItems()) {
2305 item->Destroy(aBuilder);
2309 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) {
2310 return aFrame->Style()->PointerEvents() != StylePointerEvents::None;
2313 // A list of frames, and their z depth. Used for sorting
2314 // the results of hit testing.
2315 struct FramesWithDepth {
2316 explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {}
2318 bool operator<(const FramesWithDepth& aOther) const {
2319 if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
2320 // We want to sort so that the shallowest item (highest depth value) is
2321 // first
2322 return mDepth > aOther.mDepth;
2324 return false;
2326 bool operator==(const FramesWithDepth& aOther) const {
2327 return this == &aOther;
2330 float mDepth;
2331 nsTArray<nsIFrame*> mFrames;
2334 // Sort the frames by depth and then moves all the contained frames to the
2335 // destination
2336 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource,
2337 nsTArray<nsIFrame*>* aDest) {
2338 if (aSource.IsEmpty()) {
2339 return;
2341 aSource.StableSort();
2342 uint32_t length = aSource.Length();
2343 for (uint32_t i = 0; i < length; i++) {
2344 aDest->AppendElements(std::move(aSource[i].mFrames));
2346 aSource.Clear();
2349 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2350 nsDisplayItem::HitTestState* aState,
2351 nsTArray<nsIFrame*>* aOutFrames) const {
2352 nsDisplayItem* item;
2354 if (aState->mInPreserves3D) {
2355 // Collect leaves of the current 3D rendering context.
2356 for (nsDisplayItem* item : *this) {
2357 auto itemType = item->GetType();
2358 if (itemType != DisplayItemType::TYPE_TRANSFORM ||
2359 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2360 item->HitTest(aBuilder, aRect, aState, aOutFrames);
2361 } else {
2362 // One of leaves in the current 3D rendering context.
2363 aState->mItemBuffer.AppendElement(item);
2366 return;
2369 int32_t itemBufferStart = aState->mItemBuffer.Length();
2370 for (nsDisplayItem* item : *this) {
2371 aState->mItemBuffer.AppendElement(item);
2374 AutoTArray<FramesWithDepth, 16> temp;
2375 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart;
2376 --i) {
2377 // Pop element off the end of the buffer. We want to shorten the buffer
2378 // so that recursive calls to HitTest have more buffer space.
2379 item = aState->mItemBuffer[i];
2380 aState->mItemBuffer.SetLength(i);
2382 bool snap;
2383 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
2384 auto itemType = item->GetType();
2385 bool same3DContext =
2386 (itemType == DisplayItemType::TYPE_TRANSFORM &&
2387 static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
2388 (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
2389 item->Frame()->Extend3DContext());
2390 if (same3DContext &&
2391 (itemType != DisplayItemType::TYPE_TRANSFORM ||
2392 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
2393 if (!item->GetClip().MayIntersect(aRect)) {
2394 continue;
2396 AutoTArray<nsIFrame*, 1> neverUsed;
2397 // Start gathering leaves of the 3D rendering context, and
2398 // append leaves at the end of mItemBuffer. Leaves are
2399 // processed at following iterations.
2400 aState->mInPreserves3D = true;
2401 item->HitTest(aBuilder, aRect, aState, &neverUsed);
2402 aState->mInPreserves3D = false;
2403 i = aState->mItemBuffer.Length();
2404 continue;
2406 if (same3DContext || item->GetClip().MayIntersect(r)) {
2407 AutoTArray<nsIFrame*, 16> outFrames;
2408 item->HitTest(aBuilder, aRect, aState, &outFrames);
2410 // For 3d transforms with preserve-3d we add hit frames into the temp list
2411 // so we can sort them later, otherwise we add them directly to the output
2412 // list.
2413 nsTArray<nsIFrame*>* writeFrames = aOutFrames;
2414 if (item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
2415 static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2416 if (outFrames.Length()) {
2417 nsDisplayTransform* transform =
2418 static_cast<nsDisplayTransform*>(item);
2419 nsPoint point = aRect.TopLeft();
2420 // A 1x1 rect means a point, otherwise use the center of the rect
2421 if (aRect.width != 1 || aRect.height != 1) {
2422 point = aRect.Center();
2424 temp.AppendElement(
2425 FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
2426 writeFrames = &temp[temp.Length() - 1].mFrames;
2428 } else {
2429 // We may have just finished a run of consecutive preserve-3d
2430 // transforms, so flush these into the destination array before
2431 // processing our frame list.
2432 FlushFramesArray(temp, aOutFrames);
2435 for (uint32_t j = 0; j < outFrames.Length(); j++) {
2436 nsIFrame* f = outFrames.ElementAt(j);
2437 // Filter out some frames depending on the type of hittest
2438 // we are doing. For visibility tests, pass through all frames.
2439 // For pointer tests, only pass through frames that are styled
2440 // to receive pointer events.
2441 if (aBuilder->HitTestIsForVisibility() ||
2442 IsFrameReceivingPointerEvents(f)) {
2443 writeFrames->AppendElement(f);
2447 if (aBuilder->HitTestIsForVisibility()) {
2448 aState->mHitOccludingItem = [&] {
2449 if (aState->mHitOccludingItem) {
2450 // We already hit something before.
2451 return true;
2453 if (aState->mCurrentOpacity == 1.0f &&
2454 item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) {
2455 // An opaque item always occludes everything. Note that we need to
2456 // check wrapping opacity and such as well.
2457 return true;
2459 float threshold = aBuilder->VisibilityThreshold();
2460 if (threshold == 1.0f) {
2461 return false;
2463 float itemOpacity = [&] {
2464 switch (item->GetType()) {
2465 case DisplayItemType::TYPE_OPACITY:
2466 return static_cast<nsDisplayOpacity*>(item)->GetOpacity();
2467 case DisplayItemType::TYPE_BACKGROUND_COLOR:
2468 return static_cast<nsDisplayBackgroundColor*>(item)
2469 ->GetOpacity();
2470 default:
2471 // Be conservative and assume it won't occlude other items.
2472 return 0.0f;
2474 }();
2475 return itemOpacity * aState->mCurrentOpacity >= threshold;
2476 }();
2478 if (aState->mHitOccludingItem) {
2479 // We're exiting early, so pop the remaining items off the buffer.
2480 aState->mItemBuffer.TruncateLength(itemBufferStart);
2481 break;
2486 // Clear any remaining preserve-3d transforms.
2487 FlushFramesArray(temp, aOutFrames);
2488 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
2489 "How did we forget to pop some elements?");
2492 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, Document* aDoc) {
2493 nsIFrame* f = aItem->Frame();
2494 while (f) {
2495 nsPresContext* pc = f->PresContext();
2496 if (pc->Document() == aDoc) {
2497 return f->GetContent();
2499 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(
2500 pc->PresShell()->GetRootFrame());
2502 return nullptr;
2505 struct ZSortItem {
2506 nsDisplayItem* item;
2507 int32_t zIndex;
2509 explicit ZSortItem(nsDisplayItem* aItem)
2510 : item(aItem), zIndex(aItem->ZIndex()) {}
2512 operator nsDisplayItem*() { return item; }
2515 struct ZOrderComparator {
2516 bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const {
2517 // Note that we can't just take the difference of the two
2518 // z-indices here, because that might overflow a 32-bit int.
2519 return aLeft.zIndex < aRight.zIndex;
2523 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); }
2525 struct ContentComparator {
2526 nsIContent* mCommonAncestor;
2528 explicit ContentComparator(nsIContent* aCommonAncestor)
2529 : mCommonAncestor(aCommonAncestor) {}
2531 bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const {
2532 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2533 // subdocument of commonAncestor, because display items for subdocuments
2534 // have been mixed into the same list. Ensure that we're looking at content
2535 // in commonAncestor's document.
2536 Document* commonAncestorDoc = mCommonAncestor->OwnerDoc();
2537 nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc);
2538 nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc);
2539 if (!content1 || !content2) {
2540 NS_ERROR("Document trees are mixed up!");
2541 // Something weird going on
2542 return true;
2544 return nsContentUtils::CompareTreePosition<TreeKind::Flat>(
2545 content1, content2, mCommonAncestor) < 0;
2549 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) {
2550 Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor));
2553 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2554 static_assert(sizeof(nsDisplayItem) <= 176, "nsDisplayItem has grown");
2555 #endif
2557 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2558 : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
2560 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2561 const ActiveScrolledRoot* aActiveScrolledRoot)
2562 : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) {
2563 MOZ_COUNT_CTOR(nsDisplayItem);
2564 MOZ_ASSERT(mFrame);
2565 if (aBuilder->IsRetainingDisplayList()) {
2566 mFrame->AddDisplayItem(this);
2569 aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
2570 NS_ASSERTION(
2571 aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
2572 "visible rect not set");
2574 mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2576 // The visible rect is for mCurrentFrame, so we have to use
2577 // mCurrentOffsetToReferenceFrame
2578 nsRect visible = aBuilder->GetVisibleRect() +
2579 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
2580 SetBuildingRect(visible);
2582 const nsStyleDisplay* disp = mFrame->StyleDisplay();
2583 if (mFrame->BackfaceIsHidden(disp)) {
2584 mItemFlags += ItemFlag::BackfaceHidden;
2586 if (mFrame->Combines3DTransformWithAncestors()) {
2587 mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
2591 void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
2593 bool nsDisplayItem::HasDeletedFrame() const {
2594 bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) ||
2595 (GetType() == DisplayItemType::TYPE_REMOTE &&
2596 !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader());
2597 MOZ_ASSERT(retval || mFrame);
2598 return retval;
2601 /* static */
2602 bool nsDisplayItem::ForceActiveLayers() {
2603 return StaticPrefs::layers_force_active();
2606 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); }
2608 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
2609 bool aStore) {
2610 mClipChain = aClipChain;
2613 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
2614 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2615 if (const DisplayItemClip* clip =
2616 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
2617 return Some(clip->GetClipRect());
2619 #ifdef DEBUG
2620 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
2621 #endif
2622 return Nothing();
2625 const DisplayItemClip& nsDisplayItem::GetClip() const {
2626 const DisplayItemClip* clip =
2627 DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot);
2628 return clip ? *clip : DisplayItemClip::NoClip();
2631 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
2632 const DisplayItemClipChain* aOther,
2633 bool aStore) {
2634 if (!aOther || mClipChain == aOther) {
2635 return;
2638 // aOther might be a reference to a clip on the stack. We need to make sure
2639 // that CreateClipChainIntersection will allocate the actual intersected
2640 // clip in the builder's arena, so for the mClipChain == nullptr case,
2641 // we supply nullptr as the common ancestor so that
2642 // CreateClipChainIntersection clones the whole chain.
2643 const DisplayItemClipChain* ancestorClip =
2644 mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther)
2645 : nullptr;
2647 SetClipChain(
2648 aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther),
2649 aStore);
2652 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2653 bool snap;
2654 nsRect r = GetBounds(aBuilder, &snap);
2655 return GetClip().ApplyNonRoundedIntersection(r);
2658 nsDisplayContainer::nsDisplayContainer(
2659 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2660 const ActiveScrolledRoot* aActiveScrolledRoot, nsDisplayList* aList)
2661 : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
2662 mChildren(aBuilder) {
2663 MOZ_COUNT_CTOR(nsDisplayContainer);
2664 mChildren.AppendToTop(aList);
2665 UpdateBounds(aBuilder);
2667 // Clear and store the clip chain set by nsDisplayItem constructor.
2668 nsDisplayItem::SetClipChain(nullptr, true);
2671 nsRect nsDisplayItem::GetPaintRect(nsDisplayListBuilder* aBuilder,
2672 gfxContext* aCtx) {
2673 bool dummy;
2674 nsRect result = GetBounds(aBuilder, &dummy);
2675 if (aCtx) {
2676 result.IntersectRect(result,
2677 nsLayoutUtils::RoundGfxRectToAppRect(
2678 aCtx->GetClipExtents(),
2679 mFrame->PresContext()->AppUnitsPerDevPixel()));
2681 return result;
2684 bool nsDisplayContainer::CreateWebRenderCommands(
2685 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2686 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2687 nsDisplayListBuilder* aDisplayListBuilder) {
2688 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2689 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
2690 false);
2691 return true;
2694 nsRect nsDisplayContainer::GetBounds(nsDisplayListBuilder* aBuilder,
2695 bool* aSnap) const {
2696 *aSnap = false;
2697 return mBounds;
2700 nsRect nsDisplayContainer::GetComponentAlphaBounds(
2701 nsDisplayListBuilder* aBuilder) const {
2702 return mChildren.GetComponentAlphaBounds(aBuilder);
2705 static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2706 nsDisplayList* aList,
2707 const nsRect& aListBounds) {
2708 return aList->GetOpaqueRegion(aBuilder);
2711 nsRegion nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2712 bool* aSnap) const {
2713 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
2714 GetBounds(aBuilder, aSnap));
2717 Maybe<nsRect> nsDisplayContainer::GetClipWithRespectToASR(
2718 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2719 // Our children should have finite bounds with respect to |aASR|.
2720 if (aASR == mActiveScrolledRoot) {
2721 return Some(mBounds);
2724 return Some(mChildren.GetClippedBoundsWithRespectToASR(aBuilder, aASR));
2727 void nsDisplayContainer::HitTest(nsDisplayListBuilder* aBuilder,
2728 const nsRect& aRect, HitTestState* aState,
2729 nsTArray<nsIFrame*>* aOutFrames) {
2730 mChildren.HitTest(aBuilder, aRect, aState, aOutFrames);
2733 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder* aBuilder) {
2734 // Container item bounds are expected to be clipped.
2735 mBounds =
2736 mChildren.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
2739 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder,
2740 bool* aSnap) const {
2741 *aSnap = true;
2742 return mBounds;
2745 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
2746 gfxContext* aCtx) {
2747 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2748 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2749 Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx),
2750 appUnitsPerDevPixel, *drawTarget);
2751 drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
2754 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) {
2755 aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
2756 << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
2757 << ")";
2760 bool nsDisplaySolidColor::CreateWebRenderCommands(
2761 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2762 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2763 nsDisplayListBuilder* aDisplayListBuilder) {
2764 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
2765 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
2766 wr::LayoutRect r = wr::ToLayoutRect(bounds);
2767 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, mIsCheckerboardBackground,
2768 wr::ToColorF(ToDeviceColor(mColor)));
2770 return true;
2773 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
2774 bool* aSnap) const {
2775 *aSnap = true;
2776 return mRegion.GetBounds();
2779 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
2780 gfxContext* aCtx) {
2781 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2782 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2783 ColorPattern color(ToDeviceColor(mColor));
2784 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2785 Rect rect =
2786 NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
2787 drawTarget->FillRect(rect, color);
2791 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) {
2792 aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255)
2793 << "," << int(mColor.b * 255) << "," << mColor.a << ")";
2796 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2797 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2798 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2799 nsDisplayListBuilder* aDisplayListBuilder) {
2800 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2801 nsRect rect = iter.Get();
2802 LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
2803 rect, mFrame->PresContext()->AppUnitsPerDevPixel());
2804 wr::LayoutRect r = wr::ToLayoutRect(layerRects);
2805 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
2806 wr::ToColorF(ToDeviceColor(mColor)));
2809 return true;
2812 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder,
2813 nsDisplayItem* aItem, nsIFrame* aFrame,
2814 nsITheme::ThemeGeometryType aType) {
2815 if (aBuilder->IsInChromeDocumentOrPopup()) {
2816 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2817 bool preservesAxisAlignedRectangles = false;
2818 nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(
2819 aFrame, aFrame->GetRectRelativeToSelf(), displayRoot,
2820 &preservesAxisAlignedRectangles);
2821 if (preservesAxisAlignedRectangles) {
2822 aBuilder->RegisterThemeGeometry(
2823 aType, aItem,
2824 LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels(
2825 aFrame->PresContext()->AppUnitsPerDevPixel())));
2830 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2831 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
2832 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
2833 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
2834 nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
2835 nsRect rootRect = rootFrame->GetRectRelativeToSelf();
2836 if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) ==
2837 nsLayoutUtils::TRANSFORM_SUCCEEDED) {
2838 return Some(rootRect + aBuilder->ToReferenceFrame(aFrame));
2840 return Nothing();
2843 /* static */ nsDisplayBackgroundImage::InitData
2844 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder,
2845 nsIFrame* aFrame, uint16_t aLayer,
2846 const nsRect& aBackgroundRect,
2847 const ComputedStyle* aBackgroundStyle) {
2848 nsPresContext* presContext = aFrame->PresContext();
2849 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2850 const nsStyleImageLayers::Layer& layer =
2851 aBackgroundStyle->StyleBackground()->mImage.mLayers[aLayer];
2853 bool isTransformedFixed;
2854 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
2855 presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer,
2856 &isTransformedFixed);
2858 // background-attachment:fixed is treated as background-attachment:scroll
2859 // if it's affected by a transform.
2860 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2861 bool shouldTreatAsFixed =
2862 layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2863 !isTransformedFixed;
2865 bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsNone();
2866 bool isRasterImage = state.mImageRenderer.IsRasterImage();
2867 nsCOMPtr<imgIContainer> image;
2868 if (isRasterImage) {
2869 image = state.mImageRenderer.GetImage();
2871 return InitData{aBuilder, aBackgroundStyle, image,
2872 aBackgroundRect, state.mFillArea, state.mDestArea,
2873 aLayer, isRasterImage, shouldFixToViewport};
2876 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2877 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aInitData,
2878 nsIFrame* aFrameForBounds)
2879 : nsPaintedDisplayItem(aBuilder, aFrame),
2880 mBackgroundStyle(aInitData.backgroundStyle),
2881 mImage(aInitData.image),
2882 mDependentFrame(nullptr),
2883 mBackgroundRect(aInitData.backgroundRect),
2884 mFillRect(aInitData.fillArea),
2885 mDestRect(aInitData.destArea),
2886 mLayer(aInitData.layer),
2887 mIsRasterImage(aInitData.isRasterImage),
2888 mShouldFixToViewport(aInitData.shouldFixToViewport) {
2889 MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
2890 #ifdef DEBUG
2891 if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) {
2892 // If this changes, then you also need to adjust css::ImageLoader to
2893 // invalidate mFrame as needed.
2894 MOZ_ASSERT(mFrame->IsCanvasFrame() || mFrame->IsTablePart());
2896 #endif
2898 mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
2899 if (mShouldFixToViewport) {
2900 // Expand the item's visible rect to cover the entire bounds, limited to the
2901 // viewport rect. This is necessary because the background's clip can move
2902 // asynchronously.
2903 if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
2904 aInitData.builder, mFrame)) {
2905 SetBuildingRect(mBounds.Intersect(*viewportRect));
2910 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2911 MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
2912 if (mDependentFrame) {
2913 mDependentFrame->RemoveDisplayItem(this);
2917 static void SetBackgroundClipRegion(
2918 DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame,
2919 const nsStyleImageLayers::Layer& aLayer, const nsRect& aBackgroundRect,
2920 bool aWillPaintBorder) {
2921 nsCSSRendering::ImageLayerClipState clip;
2922 nsCSSRendering::GetImageLayerClip(
2923 aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect,
2924 aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
2926 if (clip.mHasAdditionalBGClipArea) {
2927 aClipState.ClipContentDescendants(
2928 clip.mAdditionalBGClipArea, clip.mBGClipArea,
2929 clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2930 } else {
2931 aClipState.ClipContentDescendants(
2932 clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2937 * This is used for the find bar highlighter overlay. It's only accessible
2938 * through the AnonymousContent API, so it's not exposed to general web pages.
2940 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
2941 nsIFrame* aFrame,
2942 const nsRect& aBackgroundRect,
2943 nsDisplayList* aList, nscolor aColor) {
2944 nsIContent* content = aFrame->GetContent();
2945 if (!content) {
2946 return false;
2949 void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
2950 if (!cutoutRegion) {
2951 return false;
2954 if (NS_GET_A(aColor) == 0) {
2955 return true;
2958 nsRegion region;
2959 region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
2960 region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
2961 aList->AppendNewToTop<nsDisplaySolidColorRegion>(aBuilder, aFrame, region,
2962 aColor);
2964 return true;
2967 enum class TableType : uint8_t {
2968 Table,
2969 TableCol,
2970 TableColGroup,
2971 TableRow,
2972 TableRowGroup,
2973 TableCell,
2975 MAX,
2978 enum class TableTypeBits : uint8_t { Count = 3 };
2980 static_assert(static_cast<uint8_t>(TableType::MAX) <
2981 (1 << (static_cast<uint8_t>(TableTypeBits::Count) + 1)),
2982 "TableType cannot fit with TableTypeBits::Count");
2983 TableType GetTableTypeFromFrame(nsIFrame* aFrame);
2985 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex,
2986 const TableType aType) {
2987 const uint32_t key = (aIndex << static_cast<uint8_t>(TableTypeBits::Count)) |
2988 static_cast<uint8_t>(aType);
2990 return static_cast<uint16_t>(key);
2993 static nsDisplayBackgroundImage* CreateBackgroundImage(
2994 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
2995 const nsDisplayBackgroundImage::InitData& aBgData) {
2996 const auto index = aBgData.layer;
2998 if (aSecondaryFrame) {
2999 const auto tableType = GetTableTypeFromFrame(aFrame);
3000 const uint16_t tableItemIndex = CalculateTablePerFrameKey(index, tableType);
3002 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundImage>(
3003 aBuilder, aSecondaryFrame, tableItemIndex, aBgData, aFrame);
3006 return MakeDisplayItemWithIndex<nsDisplayBackgroundImage>(aBuilder, aFrame,
3007 index, aBgData);
3010 static nsDisplayThemedBackground* CreateThemedBackground(
3011 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3012 const nsRect& aBgRect) {
3013 if (aSecondaryFrame) {
3014 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3015 return MakeDisplayItemWithIndex<nsDisplayTableThemedBackground>(
3016 aBuilder, aSecondaryFrame, index, aBgRect, aFrame);
3019 return MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, aBgRect);
3022 static nsDisplayBackgroundColor* CreateBackgroundColor(
3023 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3024 nsRect& aBgRect, const ComputedStyle* aBgSC, nscolor aColor) {
3025 if (aSecondaryFrame) {
3026 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3027 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>(
3028 aBuilder, aSecondaryFrame, index, aBgRect, aBgSC, aColor, aFrame);
3031 return MakeDisplayItem<nsDisplayBackgroundColor>(aBuilder, aFrame, aBgRect,
3032 aBgSC, aColor);
3035 static void DealWithWindowsAppearanceHacks(nsIFrame* aFrame,
3036 nsDisplayListBuilder* aBuilder) {
3037 const auto& disp = *aFrame->StyleDisplay();
3039 // We use default appearance rather than effective appearance because we want
3040 // to handle when titlebar buttons that have appearance: none.
3041 const auto defaultAppearance = disp.mDefaultAppearance;
3042 if (MOZ_LIKELY(defaultAppearance == StyleAppearance::None)) {
3043 return;
3046 if (auto type = disp.GetWindowButtonType()) {
3047 if (auto* widget = aFrame->GetNearestWidget()) {
3048 auto rect = LayoutDevicePixel::FromAppUnitsToNearest(
3049 nsRect(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize()),
3050 aFrame->PresContext()->AppUnitsPerDevPixel());
3051 widget->SetWindowButtonRect(*type, rect);
3056 /*static*/
3057 AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3058 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3059 const nsRect& aBackgroundRect, nsDisplayList* aList,
3060 bool aAllowWillPaintBorderOptimization, const nsRect& aBackgroundOriginRect,
3061 nsIFrame* aSecondaryReferenceFrame,
3062 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
3063 aAutoBuildingDisplayList) {
3064 MOZ_ASSERT(!aFrame->IsCanvasFrame(),
3065 "We don't expect propagated canvas backgrounds here");
3066 #ifdef DEBUG
3068 nsIFrame* bgFrame = nsCSSRendering::FindBackgroundFrame(aFrame);
3069 MOZ_ASSERT(
3070 !bgFrame || bgFrame == aFrame,
3071 "Should only suppress backgrounds, never propagate to another frame");
3073 #endif
3075 DealWithWindowsAppearanceHacks(aFrame, aBuilder);
3077 const bool isThemed = aFrame->IsThemed();
3079 const ComputedStyle* bgSC = aFrame->Style();
3080 const nsStyleBackground* bg = bgSC->StyleBackground();
3081 const bool needsBackgroundColor =
3082 aBuilder->IsForEventDelivery() ||
3083 (EffectCompositor::HasAnimationsForCompositor(
3084 aFrame, DisplayItemType::TYPE_BACKGROUND_COLOR) &&
3085 !isThemed);
3086 if (!needsBackgroundColor && !isThemed && bg->IsTransparent(bgSC)) {
3087 return AppendedBackgroundType::None;
3090 bool drawBackgroundColor = false;
3091 bool drawBackgroundImage = false;
3092 nscolor color = NS_RGBA(0, 0, 0, 0);
3093 // Don't get background color / images if we propagated our background to the
3094 // canvas (that is, if FindBackgroundFrame is null). But don't early return
3095 // yet, since we might still need a background-color item for hit-testing.
3096 if (!isThemed && nsCSSRendering::FindBackgroundFrame(aFrame)) {
3097 color = nsCSSRendering::DetermineBackgroundColor(
3098 aFrame->PresContext(), bgSC, aFrame, drawBackgroundImage,
3099 drawBackgroundColor);
3102 if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
3103 color)) {
3104 return AppendedBackgroundType::None;
3107 const nsStyleBorder& border = *aFrame->StyleBorder();
3108 const bool willPaintBorder =
3109 aAllowWillPaintBorderOptimization && !isThemed &&
3110 !aFrame->StyleEffects()->HasBoxShadowWithInset(true) &&
3111 border.HasBorder();
3113 auto EnsureBuildingDisplayList = [&] {
3114 if (!aAutoBuildingDisplayList || *aAutoBuildingDisplayList) {
3115 return;
3117 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3118 aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3119 aBuilder->GetVisibleRect() + offset,
3120 aBuilder->GetDirtyRect() + offset);
3123 // An auxiliary list is necessary in case we have background blending; if that
3124 // is the case, background items need to be wrapped by a blend container to
3125 // isolate blending to the background
3126 nsDisplayList bgItemList(aBuilder);
3127 // Even if we don't actually have a background color to paint, we may still
3128 // need to create an item for hit testing and we still need to create an item
3129 // for background-color animations.
3130 if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) ||
3131 needsBackgroundColor) {
3132 EnsureBuildingDisplayList();
3133 Maybe<DisplayListClipState::AutoSaveRestore> clipState;
3134 nsRect bgColorRect = aBackgroundRect;
3135 if (!isThemed && !aBuilder->IsForEventDelivery()) {
3136 // Disable the will-paint-border optimization for background
3137 // colors with no border-radius. Enabling it for background colors
3138 // doesn't help much (there are no tiling issues) and clipping the
3139 // background breaks detection of the element's border-box being
3140 // opaque. For nonzero border-radius we still need it because we
3141 // want to inset the background if possible to avoid antialiasing
3142 // artifacts along the rounded corners.
3143 const bool useWillPaintBorderOptimization =
3144 willPaintBorder &&
3145 nsLayoutUtils::HasNonZeroCorner(border.mBorderRadius);
3147 nsCSSRendering::ImageLayerClipState clip;
3148 nsCSSRendering::GetImageLayerClip(
3149 bg->BottomLayer(), aFrame, border, aBackgroundRect, aBackgroundRect,
3150 useWillPaintBorderOptimization,
3151 aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3153 bgColorRect = bgColorRect.Intersect(clip.mBGClipArea);
3154 if (clip.mHasAdditionalBGClipArea) {
3155 bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea);
3157 if (clip.mHasRoundedCorners) {
3158 clipState.emplace(aBuilder);
3159 clipState->ClipContentDescendants(clip.mBGClipArea, clip.mRadii);
3163 nsDisplayBackgroundColor* bgItem = CreateBackgroundColor(
3164 aBuilder, aFrame, aSecondaryReferenceFrame, bgColorRect, bgSC,
3165 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0));
3167 if (bgItem) {
3168 bgItemList.AppendToTop(bgItem);
3172 if (isThemed) {
3173 nsDisplayThemedBackground* bgItem = CreateThemedBackground(
3174 aBuilder, aFrame, aSecondaryReferenceFrame, aBackgroundRect);
3176 if (bgItem) {
3177 bgItem->Init(aBuilder);
3178 bgItemList.AppendToTop(bgItem);
3181 if (!bgItemList.IsEmpty()) {
3182 aList->AppendToTop(&bgItemList);
3183 return AppendedBackgroundType::ThemedBackground;
3186 return AppendedBackgroundType::None;
3189 if (!drawBackgroundImage) {
3190 if (!bgItemList.IsEmpty()) {
3191 aList->AppendToTop(&bgItemList);
3192 return AppendedBackgroundType::Background;
3195 return AppendedBackgroundType::None;
3198 const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
3200 bool needBlendContainer = false;
3201 const nsRect& bgOriginRect =
3202 aBackgroundOriginRect.IsEmpty() ? aBackgroundRect : aBackgroundOriginRect;
3204 // Passing bg == nullptr in this macro will result in one iteration with
3205 // i = 0.
3206 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
3207 if (bg->mImage.mLayers[i].mImage.IsNone()) {
3208 continue;
3211 EnsureBuildingDisplayList();
3213 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3214 needBlendContainer = true;
3217 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3218 if (!aBuilder->IsForEventDelivery()) {
3219 const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
3220 SetBackgroundClipRegion(clipState, aFrame, layer, aBackgroundRect,
3221 willPaintBorder);
3224 nsDisplayList thisItemList(aBuilder);
3225 nsDisplayBackgroundImage::InitData bgData =
3226 nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect,
3227 bgSC);
3229 if (bgData.shouldFixToViewport) {
3230 auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
3231 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3232 aBuilder, aFrame, aBuilder->GetVisibleRect(),
3233 aBuilder->GetDirtyRect());
3235 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
3236 aBuilder);
3237 if (displayData) {
3238 asrSetter.SetCurrentActiveScrolledRoot(
3239 displayData->mContainingBlockActiveScrolledRoot);
3240 asrSetter.SetCurrentScrollParentId(displayData->mScrollParentId);
3241 if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
3242 // Override the dirty rect on the builder to be the dirty rect of
3243 // the viewport.
3244 // displayData->mDirtyRect is relative to the presshell's viewport
3245 // frame (the root frame), and we need it to be relative to aFrame.
3246 nsIFrame* rootFrame =
3247 aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
3248 // There cannot be any transforms between aFrame and rootFrame
3249 // because then bgData.shouldFixToViewport would have been false.
3250 nsRect visibleRect =
3251 displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame);
3252 aBuilder->SetVisibleRect(visibleRect);
3253 nsRect dirtyRect =
3254 displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
3255 aBuilder->SetDirtyRect(dirtyRect);
3259 nsDisplayBackgroundImage* bgItem = nullptr;
3261 // The clip is captured by the nsDisplayFixedPosition, so clear the
3262 // clip for the nsDisplayBackgroundImage inside.
3263 DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
3264 bgImageClip.Clear();
3265 bgItem = CreateBackgroundImage(aBuilder, aFrame,
3266 aSecondaryReferenceFrame, bgData);
3268 if (bgItem) {
3269 thisItemList.AppendToTop(
3270 nsDisplayFixedPosition::CreateForFixedBackground(
3271 aBuilder, aFrame, aSecondaryReferenceFrame, bgItem, i, asr));
3273 } else { // bgData.shouldFixToViewport == false
3274 nsDisplayBackgroundImage* bgItem = CreateBackgroundImage(
3275 aBuilder, aFrame, aSecondaryReferenceFrame, bgData);
3276 if (bgItem) {
3277 thisItemList.AppendToTop(bgItem);
3281 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3282 // asr is scrolled. Even if we wrap a fixed background layer, that's
3283 // fine, because the item will have a scrolled clip that limits the
3284 // item with respect to asr.
3285 if (aSecondaryReferenceFrame) {
3286 const auto tableType = GetTableTypeFromFrame(aFrame);
3287 const uint16_t index = CalculateTablePerFrameKey(i + 1, tableType);
3289 thisItemList.AppendNewToTopWithIndex<nsDisplayTableBlendMode>(
3290 aBuilder, aSecondaryReferenceFrame, index, &thisItemList,
3291 bg->mImage.mLayers[i].mBlendMode, asr, aFrame, true);
3292 } else {
3293 thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
3294 aBuilder, aFrame, i + 1, &thisItemList,
3295 bg->mImage.mLayers[i].mBlendMode, asr, true);
3298 bgItemList.AppendToTop(&thisItemList);
3301 if (needBlendContainer) {
3302 bgItemList.AppendToTop(
3303 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3304 aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr));
3307 if (!bgItemList.IsEmpty()) {
3308 aList->AppendToTop(&bgItemList);
3309 return AppendedBackgroundType::Background;
3312 return AppendedBackgroundType::None;
3315 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3316 // intersects aRect. Assumes that the unrounded border has already
3317 // been checked for intersection.
3318 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame,
3319 const nsPoint& aFrameToReferenceFrame,
3320 const nsRect& aTestRect) {
3321 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize())
3322 .Intersects(aTestRect)) {
3323 return false;
3326 nscoord radii[8];
3327 return !aFrame->GetBorderRadii(radii) ||
3328 nsLayoutUtils::RoundedRectIntersectsRect(
3329 nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii,
3330 aTestRect);
3333 // Returns TRUE if aContainedRect is guaranteed to be contained in
3334 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3335 // handled conservatively by returning FALSE in some situations where
3336 // a more thorough analysis could return TRUE.
3338 // See also RoundedRectIntersectsRect.
3339 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
3340 const nscoord aRadii[8],
3341 const nsRect& aContainedRect) {
3342 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii,
3343 aContainedRect);
3344 return rgn.Contains(aContainedRect);
3347 bool nsDisplayBackgroundImage::CanApplyOpacity(
3348 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3349 return CanBuildWebRenderDisplayItems(aManager, aBuilder);
3352 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3353 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3354 return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip !=
3355 StyleGeometryBox::Text &&
3356 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3357 aManager, *StyleFrame()->PresContext(), StyleFrame(),
3358 mBackgroundStyle->StyleBackground(), mLayer,
3359 aBuilder->GetBackgroundPaintFlags());
3362 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3363 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3364 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3365 nsDisplayListBuilder* aDisplayListBuilder) {
3366 if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(),
3367 aDisplayListBuilder)) {
3368 return false;
3371 uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
3372 bool dummy;
3373 nsCSSRendering::PaintBGParams params =
3374 nsCSSRendering::PaintBGParams::ForSingleLayer(
3375 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy),
3376 mBackgroundRect, StyleFrame(), paintFlags, mLayer,
3377 CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity());
3378 params.bgClipRect = &mBounds;
3379 ImgDrawResult result =
3380 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3381 params, aBuilder, aResources, aSc, aManager, this);
3382 if (result == ImgDrawResult::NOT_SUPPORTED) {
3383 return false;
3386 if (nsIContent* content = StyleFrame()->GetContent()) {
3387 if (imgRequestProxy* requestProxy = mBackgroundStyle->StyleBackground()
3388 ->mImage.mLayers[mLayer]
3389 .mImage.GetImageRequest()) {
3390 // LCP don't consider gradient backgrounds.
3391 LCPHelpers::FinalizeLCPEntryForImage(content->AsElement(), requestProxy,
3392 mBounds - ToReferenceFrame());
3396 return true;
3399 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
3400 const nsRect& aRect,
3401 HitTestState* aState,
3402 nsTArray<nsIFrame*>* aOutFrames) {
3403 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3404 aOutFrames->AppendElement(mFrame);
3408 static nsRect GetInsideClipRect(const nsDisplayItem* aItem,
3409 StyleGeometryBox aClip, const nsRect& aRect,
3410 const nsRect& aBackgroundRect) {
3411 if (aRect.IsEmpty()) {
3412 return {};
3415 nsIFrame* frame = aItem->Frame();
3417 nsRect clipRect = aBackgroundRect;
3418 if (frame->IsCanvasFrame()) {
3419 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3420 clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
3421 } else if (aClip == StyleGeometryBox::PaddingBox ||
3422 aClip == StyleGeometryBox::ContentBox) {
3423 nsMargin border = frame->GetUsedBorder();
3424 if (aClip == StyleGeometryBox::ContentBox) {
3425 border += frame->GetUsedPadding();
3427 border.ApplySkipSides(frame->GetSkipSides());
3428 clipRect.Deflate(border);
3431 return clipRect.Intersect(aRect);
3434 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
3435 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3436 nsRegion result;
3437 *aSnap = false;
3439 if (!mBackgroundStyle) {
3440 return result;
3443 *aSnap = true;
3445 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3446 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3447 // which expects frames to be sent to it in content order, not reverse
3448 // content order which we'll produce here.
3449 // Of course, if there's only one frame in the flow, it doesn't matter.
3450 if (mFrame->StyleBorder()->mBoxDecorationBreak ==
3451 StyleBoxDecorationBreak::Clone ||
3452 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
3453 const nsStyleImageLayers::Layer& layer =
3454 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3455 if (layer.mImage.IsOpaque() && layer.mBlendMode == StyleBlend::Normal &&
3456 layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
3457 layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
3458 layer.mClip != StyleGeometryBox::Text) {
3459 result = GetInsideClipRect(this, layer.mClip, mBounds, mBackgroundRect);
3463 return result;
3466 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform(
3467 nsDisplayListBuilder* aBuilder) const {
3468 if (!mBackgroundStyle) {
3469 return Some(NS_RGBA(0, 0, 0, 0));
3471 return Nothing();
3474 nsRect nsDisplayBackgroundImage::GetPositioningArea() const {
3475 if (!mBackgroundStyle) {
3476 return nsRect();
3478 nsIFrame* attachedToFrame;
3479 bool transformedFixed;
3480 return nsCSSRendering::ComputeImageLayerPositioningArea(
3481 mFrame->PresContext(), mFrame, mBackgroundRect,
3482 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer],
3483 &attachedToFrame, &transformedFixed) +
3484 ToReferenceFrame();
3487 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3488 const {
3489 if (!mBackgroundStyle) {
3490 return false;
3493 nscoord radii[8];
3494 if (mFrame->GetBorderRadii(radii)) {
3495 // A change in the size of the positioning area might change the position
3496 // of the rounded corners.
3497 return true;
3500 const nsStyleImageLayers::Layer& layer =
3501 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3502 return layer.RenderingMightDependOnPositioningAreaSizeChange();
3505 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
3506 gfxContext* aCtx) {
3507 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &mBounds);
3510 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
3511 gfxContext* aCtx,
3512 const nsRect& aBounds,
3513 nsRect* aClipRect) {
3514 gfxContext* ctx = aCtx;
3515 StyleGeometryBox clip =
3516 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip;
3518 if (clip == StyleGeometryBox::Text) {
3519 if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect,
3520 aBuilder)) {
3521 return;
3525 nsCSSRendering::PaintBGParams params =
3526 nsCSSRendering::PaintBGParams::ForSingleLayer(
3527 *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
3528 aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER,
3529 1.0f);
3530 params.bgClipRect = aClipRect;
3531 Unused << nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
3533 if (clip == StyleGeometryBox::Text) {
3534 ctx->PopGroupAndBlend();
3538 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3539 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3540 nsRegion* aInvalidRegion) const {
3541 if (!mBackgroundStyle) {
3542 return;
3545 const auto* geometry =
3546 static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
3548 bool snap;
3549 nsRect bounds = GetBounds(aBuilder, &snap);
3550 nsRect positioningArea = GetPositioningArea();
3551 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
3552 (positioningArea.Size() != geometry->mPositioningArea.Size() &&
3553 RenderingMightDependOnPositioningAreaSizeChange())) {
3554 // Positioning area changed in a way that could cause everything to change,
3555 // so invalidate everything (both old and new painting areas).
3556 aInvalidRegion->Or(bounds, geometry->mBounds);
3557 return;
3559 if (!mDestRect.IsEqualInterior(geometry->mDestRect)) {
3560 // Dest area changed in a way that could cause everything to change,
3561 // so invalidate everything (both old and new painting areas).
3562 aInvalidRegion->Or(bounds, geometry->mBounds);
3563 return;
3565 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3566 // Positioning area is unchanged, so invalidate just the change in the
3567 // painting area.
3568 aInvalidRegion->Xor(bounds, geometry->mBounds);
3572 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder,
3573 bool* aSnap) const {
3574 *aSnap = true;
3575 return mBounds;
3578 nsRect nsDisplayBackgroundImage::GetBoundsInternal(
3579 nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) {
3580 // This allows nsDisplayTableBackgroundImage to change the frame used for
3581 // bounds calculation.
3582 nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame;
3584 nsPresContext* presContext = frame->PresContext();
3586 if (!mBackgroundStyle) {
3587 return nsRect();
3590 nsRect clipRect = mBackgroundRect;
3591 if (frame->IsCanvasFrame()) {
3592 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3593 clipRect = canvasFrame->CanvasArea() + ToReferenceFrame();
3595 const nsStyleImageLayers::Layer& layer =
3596 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3597 return nsCSSRendering::GetBackgroundLayerRect(
3598 presContext, frame, mBackgroundRect, clipRect, layer,
3599 aBuilder->GetBackgroundPaintFlags());
3602 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3603 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aData,
3604 nsIFrame* aCellFrame)
3605 : nsDisplayBackgroundImage(aBuilder, aFrame, aData, aCellFrame),
3606 mStyleFrame(aCellFrame) {
3607 if (aBuilder->IsRetainingDisplayList()) {
3608 mStyleFrame->AddDisplayItem(this);
3612 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3613 if (mStyleFrame) {
3614 mStyleFrame->RemoveDisplayItem(this);
3618 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const {
3619 bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
3620 aRect += ToReferenceFrame();
3621 return result;
3624 nsDisplayThemedBackground::nsDisplayThemedBackground(
3625 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3626 const nsRect& aBackgroundRect)
3627 : nsPaintedDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) {
3628 MOZ_COUNT_CTOR(nsDisplayThemedBackground);
3631 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) {
3632 const nsStyleDisplay* disp = StyleFrame()->StyleDisplay();
3633 mAppearance = disp->EffectiveAppearance();
3634 StyleFrame()->IsThemed(disp, &mThemeTransparency);
3636 // Perform necessary RegisterThemeGeometry
3637 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3638 nsITheme::ThemeGeometryType type =
3639 theme->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance);
3640 if (type != nsITheme::eThemeGeometryTypeUnknown) {
3641 RegisterThemeGeometry(aBuilder, this, StyleFrame(), type);
3644 mBounds = GetBoundsInternal();
3647 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) {
3648 aStream << " (themed, appearance:" << (int)mAppearance << ")";
3651 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
3652 const nsRect& aRect,
3653 HitTestState* aState,
3654 nsTArray<nsIFrame*>* aOutFrames) {
3655 // Assume that any point in our background rect is a hit.
3656 if (mBackgroundRect.Intersects(aRect)) {
3657 aOutFrames->AppendElement(mFrame);
3661 nsRegion nsDisplayThemedBackground::GetOpaqueRegion(
3662 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3663 nsRegion result;
3664 *aSnap = false;
3666 if (mThemeTransparency == nsITheme::eOpaque) {
3667 *aSnap = true;
3668 result = mBackgroundRect;
3670 return result;
3673 Maybe<nscolor> nsDisplayThemedBackground::IsUniform(
3674 nsDisplayListBuilder* aBuilder) const {
3675 return Nothing();
3678 nsRect nsDisplayThemedBackground::GetPositioningArea() const {
3679 return mBackgroundRect;
3682 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
3683 gfxContext* aCtx) {
3684 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), nullptr);
3687 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
3688 gfxContext* aCtx,
3689 const nsRect& aBounds,
3690 nsRect* aClipRect) {
3691 // XXXzw this ignores aClipRect.
3692 nsPresContext* presContext = StyleFrame()->PresContext();
3693 nsITheme* theme = presContext->Theme();
3694 nsRect drawing(mBackgroundRect);
3695 theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(),
3696 mAppearance, &drawing);
3697 drawing.IntersectRect(drawing, aBounds);
3698 theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect,
3699 drawing);
3702 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3703 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3704 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3705 nsDisplayListBuilder* aDisplayListBuilder) {
3706 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3707 return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc,
3708 aManager, StyleFrame(),
3709 mAppearance, mBackgroundRect);
3712 bool nsDisplayThemedBackground::IsWindowActive() const {
3713 return !mFrame->PresContext()->Document()->IsTopLevelWindowInactive();
3716 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3717 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3718 nsRegion* aInvalidRegion) const {
3719 const auto* geometry =
3720 static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
3722 bool snap;
3723 nsRect bounds = GetBounds(aBuilder, &snap);
3724 nsRect positioningArea = GetPositioningArea();
3725 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
3726 // Invalidate everything (both old and new painting areas).
3727 aInvalidRegion->Or(bounds, geometry->mBounds);
3728 return;
3730 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3731 // Positioning area is unchanged, so invalidate just the change in the
3732 // painting area.
3733 aInvalidRegion->Xor(bounds, geometry->mBounds);
3735 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3736 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
3737 IsWindowActive() != geometry->mWindowIsActive) {
3738 aInvalidRegion->Or(*aInvalidRegion, bounds);
3742 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder,
3743 bool* aSnap) const {
3744 *aSnap = true;
3745 return mBounds;
3748 nsRect nsDisplayThemedBackground::GetBoundsInternal() {
3749 nsPresContext* presContext = mFrame->PresContext();
3751 nsRect r = mBackgroundRect - ToReferenceFrame();
3752 presContext->Theme()->GetWidgetOverflow(
3753 presContext->DeviceContext(), mFrame,
3754 mFrame->StyleDisplay()->EffectiveAppearance(), &r);
3755 return r + ToReferenceFrame();
3758 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
3759 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder,
3760 gfxContext* aCtx) {
3761 mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(),
3762 mFrame, ToReferenceFrame(), mColor);
3764 #endif
3766 bool nsDisplayBackgroundColor::CanApplyOpacity(
3767 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3768 // Don't apply opacity if the background color is animated since the color is
3769 // going to be changed on the compositor.
3770 return !EffectCompositor::HasAnimationsForCompositor(
3771 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR);
3774 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3775 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3776 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3777 nsDisplayListBuilder* aDisplayListBuilder) {
3778 gfx::sRGBColor color = mColor;
3779 color.a *= aBuilder.GetInheritedOpacity();
3781 if (color == sRGBColor() &&
3782 !EffectCompositor::HasAnimationsForCompositor(
3783 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3784 return true;
3787 if (HasBackgroundClipText()) {
3788 return false;
3791 uint64_t animationsId = 0;
3792 // We don't support background-color animations on table elements yet.
3793 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
3794 animationsId =
3795 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
3798 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
3799 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3800 wr::LayoutRect r = wr::ToLayoutRect(bounds);
3802 if (animationsId) {
3803 wr::WrAnimationProperty prop{
3804 wr::WrAnimationType::BackgroundColor,
3805 animationsId,
3807 aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(),
3808 wr::ToColorF(ToDeviceColor(color)), &prop);
3809 } else {
3810 aBuilder.StartGroup(this);
3811 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
3812 wr::ToColorF(ToDeviceColor(color)));
3813 aBuilder.FinishGroup();
3816 return true;
3819 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder,
3820 gfxContext* aCtx,
3821 const DisplayItemClip& aClip) {
3822 MOZ_ASSERT(!HasBackgroundClipText());
3824 if (mColor == sRGBColor()) {
3825 return;
3828 nsRect fillRect = mBackgroundRect;
3829 if (aClip.HasClip()) {
3830 fillRect.IntersectRect(fillRect, aClip.GetClipRect());
3833 DrawTarget* dt = aCtx->GetDrawTarget();
3834 int32_t A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
3835 Rect bounds = ToRect(nsLayoutUtils::RectToGfxRect(fillRect, A2D));
3836 MaybeSnapToDevicePixels(bounds, *dt);
3837 ColorPattern fill(ToDeviceColor(mColor));
3839 if (aClip.GetRoundedRectCount()) {
3840 MOZ_ASSERT(aClip.GetRoundedRectCount() == 1);
3842 AutoTArray<DisplayItemClip::RoundedRect, 1> roundedRect;
3843 aClip.AppendRoundedRects(&roundedRect);
3845 bool pushedClip = false;
3846 if (!fillRect.Contains(roundedRect[0].mRect)) {
3847 dt->PushClipRect(bounds);
3848 pushedClip = true;
3851 RectCornerRadii pixelRadii;
3852 nsCSSRendering::ComputePixelRadii(roundedRect[0].mRadii, A2D, &pixelRadii);
3853 dt->FillRoundedRect(
3854 RoundedRect(NSRectToSnappedRect(roundedRect[0].mRect, A2D, *dt),
3855 pixelRadii),
3856 fill);
3857 if (pushedClip) {
3858 dt->PopClip();
3860 } else {
3861 dt->FillRect(bounds, fill);
3865 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
3866 gfxContext* aCtx) {
3867 if (mColor == sRGBColor()) {
3868 return;
3871 #if 0
3872 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3873 // results in a precision induced rounding issue that makes the rect one
3874 // pixel shorter in rare cases. Disabled in favor of the old code for now.
3875 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3876 // reproduce the bug.
3878 // TODO:
3879 // This new path does not include support for background-clip:text; need to
3880 // be fixed if/when we switch to this new code path.
3882 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
3884 Rect rect = NSRectToSnappedRect(mBackgroundRect,
3885 mFrame->PresContext()->AppUnitsPerDevPixel(),
3886 aDrawTarget);
3887 ColorPattern color(ToDeviceColor(mColor));
3888 aDrawTarget.FillRect(rect, color);
3889 #else
3890 gfxContext* ctx = aCtx;
3891 gfxRect bounds = nsLayoutUtils::RectToGfxRect(
3892 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3894 if (HasBackgroundClipText()) {
3895 if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
3896 return;
3899 ctx->SetColor(mColor);
3900 ctx->NewPath();
3901 ctx->SnappedRectangle(bounds);
3902 ctx->Fill();
3903 ctx->PopGroupAndBlend();
3904 return;
3907 ctx->SetColor(mColor);
3908 ctx->NewPath();
3909 ctx->SnappedRectangle(bounds);
3910 ctx->Fill();
3911 #endif
3914 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion(
3915 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3916 *aSnap = false;
3918 if (mColor.a != 1 ||
3919 // Even if the current alpha channel is 1, we treat this item as if it's
3920 // non-opaque if there is a background-color animation since the animation
3921 // might change the alpha channel.
3922 EffectCompositor::HasAnimationsForCompositor(
3923 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3924 return nsRegion();
3927 if (!mHasStyle || HasBackgroundClipText()) {
3928 return nsRegion();
3931 *aSnap = true;
3932 return GetInsideClipRect(this, mBottomLayerClip, mBackgroundRect,
3933 mBackgroundRect);
3936 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform(
3937 nsDisplayListBuilder* aBuilder) const {
3938 return Some(mColor.ToABGR());
3941 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
3942 const nsRect& aRect,
3943 HitTestState* aState,
3944 nsTArray<nsIFrame*>* aOutFrames) {
3945 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3946 // aRect doesn't intersect our border-radius curve.
3947 return;
3950 aOutFrames->AppendElement(mFrame);
3953 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) {
3954 aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << ","
3955 << mColor.a << ")";
3956 aStream << " backgroundRect" << mBackgroundRect;
3959 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
3960 bool* aSnap) const {
3961 *aSnap = false;
3962 return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
3965 nsRect nsDisplayOutline::GetInnerRect() const {
3966 if (nsRect* savedOutlineInnerRect =
3967 mFrame->GetProperty(nsIFrame::OutlineInnerRectProperty())) {
3968 return *savedOutlineInnerRect;
3970 return mFrame->GetRectRelativeToSelf();
3973 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
3974 // TODO join outlines together
3975 MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(),
3976 "Should have not created a nsDisplayOutline!");
3978 nsRect rect = GetInnerRect() + ToReferenceFrame();
3979 nsPresContext* pc = mFrame->PresContext();
3980 if (IsThemedOutline()) {
3981 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
3982 pc->Theme()->DrawWidgetBackground(aCtx, mFrame,
3983 StyleAppearance::FocusOutline, rect,
3984 GetPaintRect(aBuilder, aCtx));
3985 return;
3988 nsCSSRendering::PaintNonThemedOutline(
3989 pc, *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), rect, mFrame->Style());
3992 bool nsDisplayOutline::IsThemedOutline() const {
3993 #ifdef DEBUG
3994 nsPresContext* pc = mFrame->PresContext();
3995 MOZ_ASSERT(
3996 pc->Theme()->ThemeSupportsWidget(pc, mFrame,
3997 StyleAppearance::FocusOutline),
3998 "All of our supported platforms have support for themed focus-outlines");
3999 #endif
4000 return mFrame->StyleOutline()->mOutlineStyle.IsAuto();
4003 bool nsDisplayOutline::CreateWebRenderCommands(
4004 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4005 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4006 nsDisplayListBuilder* aDisplayListBuilder) {
4007 nsPresContext* pc = mFrame->PresContext();
4008 nsRect rect = GetInnerRect() + ToReferenceFrame();
4009 if (IsThemedOutline()) {
4010 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
4011 return pc->Theme()->CreateWebRenderCommandsForWidget(
4012 aBuilder, aResources, aSc, aManager, mFrame,
4013 StyleAppearance::FocusOutline, rect);
4016 bool dummy;
4017 Maybe<nsCSSBorderRenderer> borderRenderer =
4018 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4019 pc, /* aDrawTarget = */ nullptr, mFrame,
4020 GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style());
4022 if (!borderRenderer) {
4023 // No border renderer means "there is no outline".
4024 // Paint nothing and return success.
4025 return true;
4028 borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
4029 return true;
4032 bool nsDisplayOutline::HasRadius() const {
4033 const auto& radius = mFrame->StyleBorder()->mBorderRadius;
4034 return !nsLayoutUtils::HasNonZeroCorner(radius);
4037 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const {
4038 const nsStyleOutline* outline = mFrame->StyleOutline();
4039 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
4040 if (borderBox.Contains(aRect) && !HasRadius() &&
4041 outline->mOutlineOffset.ToCSSPixels() >= 0.0f) {
4042 // aRect is entirely inside the border-rect, and the outline isn't rendered
4043 // inside the border-rect, so the outline is not visible.
4044 return true;
4046 return false;
4049 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
4050 const nsRect& aRect, HitTestState* aState,
4051 nsTArray<nsIFrame*>* aOutFrames) {
4052 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4053 // aRect doesn't intersect our border-radius curve.
4054 return;
4057 aOutFrames->AppendElement(mFrame);
4060 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4061 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4062 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4063 nsDisplayListBuilder* aDisplayListBuilder) {
4064 return true;
4067 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4068 return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
4071 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) {
4072 mOverrideZIndex = Some(aZIndex);
4075 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
4076 nsIFrame* aCaretFrame)
4077 : nsPaintedDisplayItem(aBuilder, aCaretFrame),
4078 mCaret(aBuilder->GetCaret()),
4079 mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) {
4080 MOZ_COUNT_CTOR(nsDisplayCaret);
4081 // The presence of a caret doesn't change the overflow rect
4082 // of the owning frame, so the normal building rect might not
4083 // include the caret at all. We use MarkFrameForDisplay to ensure
4084 // we build this item, and here we override the building rect
4085 // to cover the pixels we're going to draw.
4086 SetBuildingRect(mBounds);
4089 #ifdef NS_BUILD_REFCNT_LOGGING
4090 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret); }
4091 #endif
4093 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder,
4094 bool* aSnap) const {
4095 *aSnap = true;
4096 // The caret returns a rect in the coordinates of mFrame.
4097 return mBounds;
4100 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4101 // Note: Because we exist, we know that the caret is visible, so we don't
4102 // need to check for the caret's visibility.
4103 mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
4106 bool nsDisplayCaret::CreateWebRenderCommands(
4107 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4108 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4109 nsDisplayListBuilder* aDisplayListBuilder) {
4110 using namespace layers;
4111 nsRect caretRect;
4112 nsRect hookRect;
4113 nscolor caretColor;
4114 nsIFrame* frame =
4115 mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor);
4116 if (NS_WARN_IF(!frame) || NS_WARN_IF(frame != mFrame)) {
4117 return true;
4120 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
4121 gfx::DeviceColor color = ToDeviceColor(caretColor);
4122 LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
4123 caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
4124 LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
4125 hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
4127 wr::LayoutRect caret = wr::ToLayoutRect(devCaretRect);
4128 wr::LayoutRect hook = wr::ToLayoutRect(devHookRect);
4130 // Note, WR will pixel snap anything that is layout aligned.
4131 aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), false, false,
4132 wr::ToColorF(color));
4134 if (!devHookRect.IsEmpty()) {
4135 aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), false, false,
4136 wr::ToColorF(color));
4138 return true;
4141 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder,
4142 nsIFrame* aFrame)
4143 : nsPaintedDisplayItem(aBuilder, aFrame) {
4144 MOZ_COUNT_CTOR(nsDisplayBorder);
4146 mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
4149 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const {
4150 nsRect paddingRect = GetPaddingRect();
4151 const nsStyleBorder* styleBorder;
4152 if (paddingRect.Contains(aRect) &&
4153 !(styleBorder = mFrame->StyleBorder())->IsBorderImageSizeAvailable() &&
4154 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
4155 // aRect is entirely inside the content rect, and no part
4156 // of the border is rendered inside the content rect, so we are not
4157 // visible
4158 // Skip this if there's a border-image (which draws a background
4159 // too) or if there is a border-radius (which makes the border draw
4160 // further in).
4161 return true;
4164 return false;
4167 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry(
4168 nsDisplayListBuilder* aBuilder) {
4169 return new nsDisplayBorderGeometry(this, aBuilder);
4172 void nsDisplayBorder::ComputeInvalidationRegion(
4173 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4174 nsRegion* aInvalidRegion) const {
4175 const auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
4176 bool snap;
4178 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
4179 // We can probably get away with only invalidating the difference
4180 // between the border and padding rects, but the XUL ui at least
4181 // is apparently painting a background with this?
4182 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4186 bool nsDisplayBorder::CreateWebRenderCommands(
4187 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4188 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4189 nsDisplayListBuilder* aDisplayListBuilder) {
4190 nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
4192 ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder(
4193 this, mFrame, rect, aBuilder, aResources, aSc, aManager,
4194 aDisplayListBuilder);
4196 if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
4197 return false;
4199 return true;
4202 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4203 nsPoint offset = ToReferenceFrame();
4205 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
4206 ? PaintBorderFlags::SyncDecodeImages
4207 : PaintBorderFlags();
4209 Unused << nsCSSRendering::PaintBorder(
4210 mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
4211 nsRect(offset, mFrame->GetSize()), mFrame->Style(), flags,
4212 mFrame->GetSkipSides());
4215 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder,
4216 bool* aSnap) const {
4217 *aSnap = true;
4218 return mBounds;
4221 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
4222 gfxContext* aCtx) {
4223 nsPoint offset = ToReferenceFrame();
4224 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4225 nsPresContext* presContext = mFrame->PresContext();
4227 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
4229 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
4230 GetPaintRect(aBuilder, aCtx), 1.0f);
4233 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
4234 bool* aSnap) const {
4235 *aSnap = false;
4236 return mBounds;
4239 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() {
4240 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
4241 ToReferenceFrame();
4244 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const {
4245 nsPoint origin = ToReferenceFrame();
4246 nsRect frameRect(origin, mFrame->GetSize());
4247 if (!frameRect.Contains(aRect)) {
4248 return false;
4251 // the visible region is entirely inside the border-rect, and box shadows
4252 // never render within the border-rect (unless there's a border radius).
4253 nscoord twipsRadii[8];
4254 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
4255 if (!hasBorderRadii) {
4256 return true;
4259 return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
4262 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4263 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4264 if (shadows.IsEmpty()) {
4265 return false;
4268 bool hasBorderRadius;
4269 // We don't support native themed things yet like box shadows around
4270 // input buttons.
4272 // TODO(emilio): The non-native theme could provide the right rect+radius
4273 // instead relatively painlessly, if we find this causes performance issues or
4274 // what not.
4275 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4278 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4279 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4280 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4281 nsDisplayListBuilder* aDisplayListBuilder) {
4282 if (!CanBuildWebRenderDisplayItems()) {
4283 return false;
4286 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4287 nsPoint offset = ToReferenceFrame();
4288 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4289 bool snap;
4290 nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
4292 bool hasBorderRadius;
4293 bool nativeTheme =
4294 nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4296 // Don't need the full size of the shadow rect like we do in
4297 // nsCSSRendering since WR takes care of calculations for blur
4298 // and spread radius.
4299 nsRect frameRect =
4300 nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
4302 RectCornerRadii borderRadii;
4303 if (hasBorderRadius) {
4304 hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
4305 mFrame, borderRadii);
4308 // Everything here is in app units, change to device units.
4309 LayoutDeviceRect clipRect =
4310 LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel);
4311 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4312 MOZ_ASSERT(!shadows.IsEmpty());
4314 for (const auto& shadow : Reversed(shadows)) {
4315 if (shadow.inset) {
4316 continue;
4319 float blurRadius =
4320 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4321 gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor(
4322 shadow.base, mFrame, aBuilder.GetInheritedOpacity());
4324 // We don't move the shadow rect here since WR does it for us
4325 // Now translate everything to device pixels.
4326 const nsRect& shadowRect = frameRect;
4327 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4328 nsPoint(shadow.base.horizontal.ToAppUnits(),
4329 shadow.base.vertical.ToAppUnits()),
4330 appUnitsPerDevPixel);
4332 LayoutDeviceRect deviceBox =
4333 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4334 wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
4335 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4337 LayoutDeviceSize zeroSize;
4338 wr::BorderRadius borderRadius =
4339 wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
4340 if (hasBorderRadius) {
4341 borderRadius = wr::ToBorderRadius(
4342 LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
4343 LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
4344 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
4345 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
4348 float spreadRadius =
4349 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4351 aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
4352 deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
4353 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius,
4354 spreadRadius, borderRadius,
4355 wr::BoxShadowClipMode::Outset);
4358 return true;
4361 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4362 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4363 nsRegion* aInvalidRegion) const {
4364 const auto* geometry =
4365 static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
4366 bool snap;
4367 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
4368 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
4369 nsRegion oldShadow, newShadow;
4370 nscoord dontCare[8];
4371 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
4372 if (hasBorderRadius) {
4373 // If we have rounded corners then we need to invalidate the frame area
4374 // too since we paint into it.
4375 oldShadow = geometry->mBounds;
4376 newShadow = GetBounds(aBuilder, &snap);
4377 } else {
4378 oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
4379 newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
4381 aInvalidRegion->Or(oldShadow, newShadow);
4385 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
4386 gfxContext* aCtx) {
4387 nsPoint offset = ToReferenceFrame();
4388 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4389 nsPresContext* presContext = mFrame->PresContext();
4391 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS);
4393 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
4396 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4397 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4398 const nsPoint& aReferenceOffset) {
4399 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4400 if (shadows.IsEmpty()) {
4401 // Means we don't have to paint anything
4402 return true;
4405 bool hasBorderRadius;
4406 bool nativeTheme =
4407 nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
4409 // We don't support native themed things yet like box shadows around
4410 // input buttons.
4411 return !nativeTheme;
4414 /* static */
4415 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4416 wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
4417 nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect) {
4418 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) {
4419 return;
4422 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
4424 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4426 LayoutDeviceRect clipRect =
4427 LayoutDeviceRect::FromAppUnits(aVisibleRect, appUnitsPerDevPixel);
4429 for (const auto& shadow : Reversed(shadows)) {
4430 if (!shadow.inset) {
4431 continue;
4434 nsRect shadowRect =
4435 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
4436 RectCornerRadii innerRadii;
4437 nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
4439 // Now translate everything to device pixels.
4440 LayoutDeviceRect deviceBoxRect =
4441 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4442 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4443 sRGBColor shadowColor =
4444 nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0);
4446 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4447 nsPoint(shadow.base.horizontal.ToAppUnits(),
4448 shadow.base.vertical.ToAppUnits()),
4449 appUnitsPerDevPixel);
4451 float blurRadius =
4452 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4454 wr::BorderRadius borderRadius = wr::ToBorderRadius(
4455 LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
4456 LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
4457 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
4458 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
4459 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4460 float spreadRadius =
4461 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4463 aBuilder.PushBoxShadow(
4464 wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
4465 !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
4466 wr::ToLayoutVector2D(shadowOffset),
4467 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, spreadRadius,
4468 borderRadius, wr::BoxShadowClipMode::Inset);
4472 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4473 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4474 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4475 nsDisplayListBuilder* aDisplayListBuilder) {
4476 if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame,
4477 ToReferenceFrame())) {
4478 return false;
4481 bool snap;
4482 nsRect visible = GetBounds(aDisplayListBuilder, &snap);
4483 nsPoint offset = ToReferenceFrame();
4484 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4485 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4486 aBuilder, aSc, visible, mFrame, borderRect);
4488 return true;
4491 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4492 nsIFrame* aFrame, nsDisplayList* aList)
4493 : nsDisplayWrapList(aBuilder, aFrame, aList,
4494 aBuilder->CurrentActiveScrolledRoot(), false) {}
4496 nsDisplayWrapList::nsDisplayWrapList(
4497 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4498 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
4499 : nsPaintedDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
4500 mList(aBuilder),
4501 mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
4502 mOverrideZIndex(0),
4503 mHasZIndexOverride(false),
4504 mClearingClipChain(aClearClipChain) {
4505 MOZ_COUNT_CTOR(nsDisplayWrapList);
4507 mBaseBuildingRect = GetBuildingRect();
4509 mListPtr = &mList;
4510 mListPtr->AppendToTop(aList);
4511 mOriginalClipChain = mClipChain;
4512 nsDisplayWrapList::UpdateBounds(aBuilder);
4515 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4516 nsIFrame* aFrame, nsDisplayItem* aItem)
4517 : nsPaintedDisplayItem(aBuilder, aFrame,
4518 aBuilder->CurrentActiveScrolledRoot()),
4519 mList(aBuilder),
4520 mOverrideZIndex(0),
4521 mHasZIndexOverride(false) {
4522 MOZ_COUNT_CTOR(nsDisplayWrapList);
4524 mBaseBuildingRect = GetBuildingRect();
4526 mListPtr = &mList;
4527 mListPtr->AppendToTop(aItem);
4528 mOriginalClipChain = mClipChain;
4529 nsDisplayWrapList::UpdateBounds(aBuilder);
4531 if (!aFrame || !aFrame->IsTransformed()) {
4532 return;
4535 // See the previous nsDisplayWrapList constructor
4536 if (aItem->Frame() == aFrame) {
4537 mToReferenceFrame = aItem->ToReferenceFrame();
4540 nsRect visible = aBuilder->GetVisibleRect() +
4541 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
4543 SetBuildingRect(visible);
4546 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList); }
4548 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder,
4549 const nsRect& aRect, HitTestState* aState,
4550 nsTArray<nsIFrame*>* aOutFrames) {
4551 mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames);
4554 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder,
4555 bool* aSnap) const {
4556 *aSnap = false;
4557 return mBounds;
4560 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4561 bool* aSnap) const {
4562 *aSnap = false;
4563 bool snap;
4564 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
4565 GetBounds(aBuilder, &snap));
4568 Maybe<nscolor> nsDisplayWrapList::IsUniform(
4569 nsDisplayListBuilder* aBuilder) const {
4570 // We could try to do something but let's conservatively just return Nothing.
4571 return Nothing();
4574 void nsDisplayWrapper::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4575 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4578 nsRect nsDisplayWrapList::GetComponentAlphaBounds(
4579 nsDisplayListBuilder* aBuilder) const {
4580 return mListPtr->GetComponentAlphaBounds(aBuilder);
4583 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4584 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4585 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4586 nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList) {
4587 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4588 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
4589 aNewClipList);
4590 return true;
4593 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder,
4594 nsIFrame* aFrame, nsDisplayList* aList,
4595 nsDisplayItemWrapper* aWrapper) {
4596 if (!aList->GetTop()) {
4597 return NS_OK;
4599 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
4600 if (!item) {
4601 return NS_ERROR_OUT_OF_MEMORY;
4603 // aList was emptied
4604 aList->AppendToTop(item);
4605 return NS_OK;
4608 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
4609 nsDisplayList* aList,
4610 nsDisplayItemWrapper* aWrapper) {
4611 for (nsDisplayItem* item : aList->TakeItems()) {
4612 item = aWrapper->WrapItem(aBuilder, item);
4613 if (!item) {
4614 return NS_ERROR_OUT_OF_MEMORY;
4616 aList->AppendToTop(item);
4618 // aList was emptied
4619 return NS_OK;
4622 nsresult nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
4623 nsIFrame* aFrame,
4624 const nsDisplayListSet& aIn,
4625 const nsDisplayListSet& aOut) {
4626 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
4627 NS_ENSURE_SUCCESS(rv, rv);
4629 if (&aOut == &aIn) {
4630 return NS_OK;
4632 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
4633 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
4634 aOut.Floats()->AppendToTop(aIn.Floats());
4635 aOut.Content()->AppendToTop(aIn.Content());
4636 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
4637 aOut.Outlines()->AppendToTop(aIn.Outlines());
4638 return NS_OK;
4641 nsresult nsDisplayItemWrapper::WrapListsInPlace(
4642 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4643 const nsDisplayListSet& aLists) {
4644 nsresult rv;
4645 if (WrapBorderBackground()) {
4646 // Our border-backgrounds are in-flow
4647 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
4648 NS_ENSURE_SUCCESS(rv, rv);
4650 // Our block border-backgrounds are in-flow
4651 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
4652 NS_ENSURE_SUCCESS(rv, rv);
4653 // The floats are not in flow
4654 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
4655 NS_ENSURE_SUCCESS(rv, rv);
4656 // Our child content is in flow
4657 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
4658 NS_ENSURE_SUCCESS(rv, rv);
4659 // The positioned descendants may not be in-flow
4660 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
4661 NS_ENSURE_SUCCESS(rv, rv);
4662 // The outlines may not be in-flow
4663 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
4666 nsDisplayOpacity::nsDisplayOpacity(
4667 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4668 const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsOnly,
4669 bool aNeedsActiveLayer, bool aWrapsBackdropFilter)
4670 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4671 mOpacity(aFrame->StyleEffects()->mOpacity),
4672 mForEventsOnly(aForEventsOnly),
4673 mNeedsActiveLayer(aNeedsActiveLayer),
4674 mChildOpacityState(ChildOpacityState::Unknown),
4675 mWrapsBackdropFilter(aWrapsBackdropFilter) {
4676 MOZ_COUNT_CTOR(nsDisplayOpacity);
4679 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder,
4680 const nsRect& aRect,
4681 nsDisplayItem::HitTestState* aState,
4682 nsTArray<nsIFrame*>* aOutFrames) {
4683 AutoRestore<float> opacity(aState->mCurrentOpacity);
4684 aState->mCurrentOpacity *= mOpacity;
4686 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4687 // only consider fully opaque items? Or make this configurable somehow?
4688 if (aBuilder->HitTestIsForVisibility() && mOpacity == 0.0f) {
4689 return;
4691 nsDisplayWrapList::HitTest(aBuilder, aRect, aState, aOutFrames);
4694 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4695 bool* aSnap) const {
4696 *aSnap = false;
4697 // The only time where mOpacity == 1.0 should be when we have will-change.
4698 // We could report this as opaque then but when the will-change value starts
4699 // animating the element would become non opaque and could cause repaints.
4700 return nsRegion();
4703 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4704 if (GetOpacity() == 0.0f) {
4705 return;
4708 if (GetOpacity() == 1.0f) {
4709 GetChildren()->Paint(aBuilder, aCtx,
4710 mFrame->PresContext()->AppUnitsPerDevPixel());
4711 return;
4714 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4715 // allocation.
4716 aCtx->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4717 GetChildren()->Paint(aBuilder, aCtx,
4718 mFrame->PresContext()->AppUnitsPerDevPixel());
4719 aCtx->GetDrawTarget()->PopLayer();
4722 /* static */
4723 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
4724 nsIFrame* aFrame) {
4725 return EffectCompositor::HasAnimationsForCompositor(
4726 aFrame, DisplayItemType::TYPE_OPACITY) ||
4727 (ActiveLayerTracker::IsStyleAnimated(
4728 aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()));
4731 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager,
4732 nsDisplayListBuilder* aBuilder) const {
4733 return !EffectCompositor::HasAnimationsForCompositor(
4734 mFrame, DisplayItemType::TYPE_OPACITY);
4737 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4738 // children that don't overlap and can all apply the opacity to themselves.
4739 static const size_t kOpacityMaxChildCount = 3;
4741 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4742 // are likely have more child items than |kOpacityMaxChildCount|.
4743 static const size_t kOpacityMaxListSize = kOpacityMaxChildCount * 2;
4746 * Recursively iterates through |aList| and collects at most
4747 * |kOpacityMaxChildCount| display item pointers to items that return true for
4748 * CanApplyOpacity(). The item pointers are added to |aArray|.
4750 * LayerEventRegions and WrapList items are ignored.
4752 * We need to do this recursively, because the child display items might contain
4753 * nested nsDisplayWrapLists.
4755 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4756 * item that returns false for CanApplyOpacity() is encountered.
4757 * Otherwise returns true.
4759 static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager,
4760 nsDisplayListBuilder* aBuilder,
4761 nsDisplayList* aList,
4762 nsTArray<nsPaintedDisplayItem*>& aArray) {
4763 if (aList->Length() > kOpacityMaxListSize) {
4764 // Exit early, since |aList| will likely contain more than
4765 // |kOpacityMaxChildCount| items.
4766 return false;
4769 for (nsDisplayItem* i : *aList) {
4770 const DisplayItemType type = i->GetType();
4772 if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
4773 continue;
4776 // Descend only into wraplists.
4777 if (type == DisplayItemType::TYPE_WRAP_LIST ||
4778 type == DisplayItemType::TYPE_CONTAINER) {
4779 // The current display item has children, process them first.
4780 if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(),
4781 aArray)) {
4782 return false;
4785 continue;
4788 if (aArray.Length() == kOpacityMaxChildCount) {
4789 return false;
4792 auto* item = i->AsPaintedDisplayItem();
4793 if (!item || !item->CanApplyOpacity(aManager, aBuilder)) {
4794 return false;
4797 aArray.AppendElement(item);
4800 return true;
4803 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager,
4804 nsDisplayListBuilder* aBuilder) {
4805 if (mChildOpacityState == ChildOpacityState::Deferred) {
4806 return false;
4809 // Iterate through the child display list and copy at most
4810 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4811 AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items;
4812 if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) {
4813 mChildOpacityState = ChildOpacityState::Deferred;
4814 return false;
4817 struct {
4818 nsPaintedDisplayItem* item{};
4819 nsRect bounds;
4820 } children[kOpacityMaxChildCount];
4822 bool snap;
4823 size_t childCount = 0;
4824 for (nsPaintedDisplayItem* item : items) {
4825 children[childCount].item = item;
4826 children[childCount].bounds = item->GetBounds(aBuilder, &snap);
4827 childCount++;
4830 for (size_t i = 0; i < childCount; i++) {
4831 for (size_t j = i + 1; j < childCount; j++) {
4832 if (children[i].bounds.Intersects(children[j].bounds)) {
4833 mChildOpacityState = ChildOpacityState::Deferred;
4834 return false;
4839 mChildOpacityState = ChildOpacityState::Applied;
4840 return true;
4844 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4845 * that has the same frame as the opacity item, and that supports painting with
4846 * opacity. In this case the opacity item can be optimized away.
4848 bool nsDisplayOpacity::ApplyToMask() {
4849 if (mList.Length() != 1) {
4850 return false;
4853 nsDisplayItem* item = mList.GetBottom();
4854 if (item->Frame() != mFrame) {
4855 // The effect item needs to have the same frame as the opacity item.
4856 return false;
4859 const DisplayItemType type = item->GetType();
4860 if (type == DisplayItemType::TYPE_MASK) {
4861 return true;
4864 return false;
4867 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4868 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder,
4869 float aInheritedOpacity) {
4870 if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
4871 mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
4872 // If we've been split, then we might need to merge, so
4873 // don't flatten us away.
4874 return false;
4877 if (mNeedsActiveLayer || mOpacity == 0.0) {
4878 // If our opacity is zero then we'll discard all descendant display items
4879 // except for layer event regions, so there's no point in doing this
4880 // optimization (and if we do do it, then invalidations of those descendants
4881 // might trigger repainting).
4882 return false;
4885 if (mList.IsEmpty()) {
4886 return false;
4889 // We can only flatten opacity items into a mask if we haven't
4890 // already flattened an earlier ancestor, since the SVG code pulls the opacity
4891 // from style directly, and won't know about the outer opacity value.
4892 if (aInheritedOpacity == 1.0f && ApplyToMask()) {
4893 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame));
4894 mChildOpacityState = ChildOpacityState::Applied;
4895 return true;
4898 // Return true if we successfully applied opacity to child items.
4899 return CanApplyToChildren(aManager, aBuilder);
4902 void nsDisplayOpacity::ComputeInvalidationRegion(
4903 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4904 nsRegion* aInvalidRegion) const {
4905 const auto* geometry =
4906 static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
4908 bool snap;
4909 if (mOpacity != geometry->mOpacity) {
4910 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4914 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) {
4915 aStream << " (opacity " << mOpacity << ", mChildOpacityState: ";
4916 switch (mChildOpacityState) {
4917 case ChildOpacityState::Unknown:
4918 aStream << "Unknown";
4919 break;
4920 case ChildOpacityState::Applied:
4921 aStream << "Applied";
4922 break;
4923 case ChildOpacityState::Deferred:
4924 aStream << "Deferred";
4925 break;
4926 default:
4927 break;
4930 aStream << ")";
4933 bool nsDisplayOpacity::CreateWebRenderCommands(
4934 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4935 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4936 nsDisplayListBuilder* aDisplayListBuilder) {
4937 MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied);
4938 float oldOpacity = aBuilder.GetInheritedOpacity();
4939 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
4940 aBuilder.SetInheritedOpacity(1.0f);
4941 aBuilder.SetInheritedClipChain(nullptr);
4942 float opacity = mOpacity * oldOpacity;
4943 float* opacityForSC = &opacity;
4945 uint64_t animationsId =
4946 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
4947 wr::WrAnimationProperty prop{
4948 wr::WrAnimationType::Opacity,
4949 animationsId,
4952 wr::StackingContextParams params;
4953 params.animation = animationsId ? &prop : nullptr;
4954 params.opacity = opacityForSC;
4955 params.clip =
4956 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
4957 if (mWrapsBackdropFilter) {
4958 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
4960 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
4961 params);
4963 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4964 &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
4965 aBuilder.SetInheritedOpacity(oldOpacity);
4966 aBuilder.SetInheritedClipChain(oldClipChain);
4967 return true;
4970 nsDisplayBlendMode::nsDisplayBlendMode(
4971 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4972 StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
4973 const bool aIsForBackground)
4974 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4975 mBlendMode(aBlendMode),
4976 mIsForBackground(aIsForBackground) {
4977 MOZ_COUNT_CTOR(nsDisplayBlendMode);
4980 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4981 bool* aSnap) const {
4982 *aSnap = false;
4983 // We are never considered opaque
4984 return nsRegion();
4987 bool nsDisplayBlendMode::CreateWebRenderCommands(
4988 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4989 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4990 nsDisplayListBuilder* aDisplayListBuilder) {
4991 wr::StackingContextParams params;
4992 params.mix_blend_mode =
4993 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
4994 params.clip =
4995 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
4996 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
4997 params);
4999 return nsDisplayWrapList::CreateWebRenderCommands(
5000 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5003 void nsDisplayBlendMode::Paint(nsDisplayListBuilder* aBuilder,
5004 gfxContext* aCtx) {
5005 // This should be switched to use PushLayerWithBlend, once it's
5006 // been implemented for all DrawTarget backends.
5007 DrawTarget* dt = aCtx->GetDrawTarget();
5008 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5009 Rect rect = NSRectToRect(GetPaintRect(aBuilder, aCtx), appUnitsPerDevPixel);
5010 rect.RoundOut();
5012 // Create a temporary DrawTarget that is clipped to the area that
5013 // we're going to draw to. This will include the same transform as
5014 // is currently on |dt|.
5015 RefPtr<DrawTarget> temp =
5016 dt->CreateClippedDrawTarget(rect, SurfaceFormat::B8G8R8A8);
5017 if (!temp) {
5018 return;
5021 gfxContext ctx(temp, /* aPreserveTransform */ true);
5023 GetChildren()->Paint(aBuilder, &ctx,
5024 mFrame->PresContext()->AppUnitsPerDevPixel());
5026 // Draw the temporary DT to the real destination, applying the blend mode, but
5027 // no transform.
5028 temp->Flush();
5029 RefPtr<SourceSurface> surface = temp->Snapshot();
5030 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
5031 dt->SetTransform(Matrix());
5032 dt->DrawSurface(
5033 surface, Rect(surface->GetRect()), Rect(surface->GetRect()),
5034 DrawSurfaceOptions(),
5035 DrawOptions(1.0f, nsCSSRendering::GetGFXBlendMode(mBlendMode)));
5038 gfx::CompositionOp nsDisplayBlendMode::BlendMode() {
5039 return nsCSSRendering::GetGFXBlendMode(mBlendMode);
5042 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const {
5043 // Items for the same content element should be merged into a single
5044 // compositing group.
5045 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
5046 !HasSameContent(aItem)) {
5047 return false;
5050 const auto* item = static_cast<const nsDisplayBlendMode*>(aItem);
5051 if (mIsForBackground || item->mIsForBackground) {
5052 // Don't merge background-blend-mode items
5053 return false;
5056 return true;
5059 /* static */
5060 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForMixBlendMode(
5061 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5062 const ActiveScrolledRoot* aActiveScrolledRoot) {
5063 return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
5064 aActiveScrolledRoot, false);
5067 /* static */
5068 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5069 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5070 nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot) {
5071 if (aSecondaryFrame) {
5072 auto type = GetTableTypeFromFrame(aFrame);
5073 auto index = static_cast<uint16_t>(type);
5075 return MakeDisplayItemWithIndex<nsDisplayTableBlendContainer>(
5076 aBuilder, aSecondaryFrame, index, aList, aActiveScrolledRoot, true,
5077 aFrame);
5080 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>(
5081 aBuilder, aFrame, 1, aList, aActiveScrolledRoot, true);
5084 nsDisplayBlendContainer::nsDisplayBlendContainer(
5085 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5086 const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground)
5087 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5088 mIsForBackground(aIsForBackground) {
5089 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
5092 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder* aBuilder,
5093 gfxContext* aCtx) {
5094 aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5095 GetChildren()->Paint(aBuilder, aCtx,
5096 mFrame->PresContext()->AppUnitsPerDevPixel());
5097 aCtx->GetDrawTarget()->PopLayer();
5100 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5101 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5102 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5103 nsDisplayListBuilder* aDisplayListBuilder) {
5104 wr::StackingContextParams params;
5105 params.flags |= wr::StackingContextFlags::IS_BLEND_CONTAINER;
5106 params.clip =
5107 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5108 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5109 params);
5111 return nsDisplayWrapList::CreateWebRenderCommands(
5112 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5115 nsDisplayOwnLayer::nsDisplayOwnLayer(
5116 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5117 const ActiveScrolledRoot* aActiveScrolledRoot,
5118 nsDisplayOwnLayerFlags aFlags, const ScrollbarData& aScrollbarData,
5119 bool aForceActive, bool aClearClipChain)
5120 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
5121 aClearClipChain),
5122 mFlags(aFlags),
5123 mScrollbarData(aScrollbarData),
5124 mForceActive(aForceActive),
5125 mWrAnimationId(0) {
5126 MOZ_COUNT_CTOR(nsDisplayOwnLayer);
5129 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5130 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb;
5133 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5134 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container;
5137 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5138 return IsScrollbarContainer() && IsScrollbarLayerForRoot();
5141 bool nsDisplayOwnLayer::IsScrollbarLayerForRoot() const {
5142 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5143 mScrollbarData.mTargetViewId ==
5144 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5147 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5148 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM;
5151 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5152 return GetType() == DisplayItemType::TYPE_FIXED_POSITION ||
5153 GetType() == DisplayItemType::TYPE_TABLE_FIXED_POSITION;
5156 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5157 return GetType() == DisplayItemType::TYPE_STICKY_POSITION;
5160 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5161 if (!mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
5162 return false;
5164 return mFrame->PresContext()->HasDynamicToolbar() ||
5165 // For tests on Android, this pref is set to simulate the dynamic
5166 // toolbar
5167 StaticPrefs::apz_fixed_margin_override_enabled();
5170 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5171 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5172 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5173 nsDisplayListBuilder* aDisplayListBuilder) {
5174 Maybe<wr::WrAnimationProperty> prop;
5175 bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() &&
5176 (IsScrollThumbLayer() || IsZoomingLayer() ||
5177 ShouldGetFixedOrStickyAnimationId() ||
5178 (IsRootScrollbarContainer() && HasDynamicToolbar()));
5180 if (needsProp) {
5181 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5182 // to create and set an animation id. That way APZ can adjust the position/
5183 // zoom of this content asynchronously as needed.
5184 RefPtr<WebRenderAPZAnimationData> animationData =
5185 aManager->CommandBuilder()
5186 .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(this);
5187 mWrAnimationId = animationData->GetAnimationId();
5189 prop.emplace();
5190 prop->id = mWrAnimationId;
5191 prop->key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5192 wr::SpatialKeyKind::APZ);
5193 prop->effect_type = wr::WrAnimationType::Transform;
5196 wr::StackingContextParams params;
5197 params.animation = prop.ptrOr(nullptr);
5198 params.clip =
5199 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5200 if (IsScrollbarContainer() && IsRootScrollbarContainer()) {
5201 params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER;
5203 if (IsZoomingLayer() ||
5204 (ShouldGetFixedOrStickyAnimationId() ||
5205 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5206 params.is_2d_scale_translation = true;
5207 params.should_snap = true;
5210 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5211 params);
5213 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
5214 aDisplayListBuilder);
5215 return true;
5218 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData,
5219 WebRenderLayerScrollData* aLayerData) {
5220 bool isRelevantToApz =
5221 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5222 ShouldGetFixedOrStickyAnimationId());
5224 if (!isRelevantToApz) {
5225 return false;
5228 if (!aLayerData) {
5229 return true;
5232 if (IsZoomingLayer()) {
5233 aLayerData->SetZoomAnimationId(mWrAnimationId);
5234 return true;
5237 if (IsFixedPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5238 aLayerData->SetFixedPositionAnimationId(mWrAnimationId);
5239 return true;
5242 if (IsStickyPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5243 aLayerData->SetStickyPositionAnimationId(mWrAnimationId);
5244 return true;
5247 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5249 aLayerData->SetScrollbarData(mScrollbarData);
5251 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5252 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5253 return true;
5256 if (IsScrollThumbLayer()) {
5257 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5258 LayoutDeviceRect bounds = LayoutDeviceIntRect::FromAppUnits(
5259 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
5260 // Subframe scrollbars are subject to the pinch-zoom scale,
5261 // but root scrollbars are not because they are outside of the
5262 // region that is zoomed.
5263 const float resolution =
5264 IsScrollbarLayerForRoot()
5265 ? 1.0f
5266 : mFrame->PresShell()->GetCumulativeResolution();
5267 LayerIntRect layerBounds =
5268 RoundedOut(bounds * LayoutDeviceToLayerScale(resolution));
5269 aLayerData->SetVisibleRect(layerBounds);
5271 return true;
5274 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) {
5275 aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
5276 (int)mFlags, mScrollbarData.mTargetViewId)
5277 .get();
5280 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
5281 nsIFrame* aFrame,
5282 nsSubDocumentFrame* aSubDocFrame,
5283 nsDisplayList* aList,
5284 nsDisplayOwnLayerFlags aFlags)
5285 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5286 aBuilder->CurrentActiveScrolledRoot(), aFlags),
5287 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5288 mShouldFlatten(false),
5289 mSubDocFrame(aSubDocFrame) {
5290 MOZ_COUNT_CTOR(nsDisplaySubDocument);
5292 if (mSubDocFrame && mSubDocFrame != mFrame) {
5293 mSubDocFrame->AddDisplayItem(this);
5297 nsDisplaySubDocument::~nsDisplaySubDocument() {
5298 MOZ_COUNT_DTOR(nsDisplaySubDocument);
5299 if (mSubDocFrame) {
5300 mSubDocFrame->RemoveDisplayItem(this);
5304 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const {
5305 return mSubDocFrame ? mSubDocFrame : mFrame;
5308 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) {
5309 if (aFrame == mSubDocFrame) {
5310 mSubDocFrame = nullptr;
5311 SetDeletedFrame();
5313 nsDisplayOwnLayer::RemoveFrame(aFrame);
5316 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder,
5317 nsIFrame* aFrame) {
5318 return aBuilder->IsPaintingToWindow() &&
5319 DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext());
5322 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder,
5323 bool* aSnap) const {
5324 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5326 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5327 usingDisplayPort) {
5328 *aSnap = false;
5329 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
5332 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
5335 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5336 bool* aSnap) const {
5337 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5339 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5340 usingDisplayPort) {
5341 *aSnap = false;
5342 return nsRegion();
5345 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
5348 /* static */
5349 nsDisplayFixedPosition* nsDisplayFixedPosition::CreateForFixedBackground(
5350 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5351 nsDisplayBackgroundImage* aImage, const uint16_t aIndex,
5352 const ActiveScrolledRoot* aScrollTargetASR) {
5353 nsDisplayList temp(aBuilder);
5354 temp.AppendToTop(aImage);
5356 if (aSecondaryFrame) {
5357 auto tableType = GetTableTypeFromFrame(aFrame);
5358 const uint16_t index = CalculateTablePerFrameKey(aIndex + 1, tableType);
5359 return MakeDisplayItemWithIndex<nsDisplayTableFixedPosition>(
5360 aBuilder, aSecondaryFrame, index, &temp, aFrame, aScrollTargetASR);
5363 return MakeDisplayItemWithIndex<nsDisplayFixedPosition>(
5364 aBuilder, aFrame, aIndex + 1, &temp, aScrollTargetASR);
5367 nsDisplayFixedPosition::nsDisplayFixedPosition(
5368 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5369 const ActiveScrolledRoot* aActiveScrolledRoot,
5370 const ActiveScrolledRoot* aScrollTargetASR)
5371 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5372 mScrollTargetASR(aScrollTargetASR),
5373 mIsFixedBackground(false) {
5374 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5377 nsDisplayFixedPosition::nsDisplayFixedPosition(
5378 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5379 const ActiveScrolledRoot* aScrollTargetASR)
5380 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5381 aBuilder->CurrentActiveScrolledRoot()),
5382 // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5383 mScrollTargetASR(aScrollTargetASR),
5384 mIsFixedBackground(true) {
5385 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5388 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() const {
5389 if (mScrollTargetASR &&
5390 (mIsFixedBackground || !nsLayoutUtils::IsReallyFixedPos(mFrame))) {
5391 return mScrollTargetASR->GetViewId();
5393 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5396 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5397 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5398 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5399 nsDisplayListBuilder* aDisplayListBuilder) {
5400 SideBits sides = SideBits::eNone;
5401 if (!mIsFixedBackground) {
5402 sides = nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5405 // We install this RAII scrolltarget tracker so that any
5406 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5407 // share the same ASR as this item) use the correct scroll target. That way
5408 // attempts to scroll on those items will scroll the root scroll frame.
5409 wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker(
5410 aBuilder, GetActiveScrolledRoot(), GetScrollTargetId(), sides);
5411 return nsDisplayOwnLayer::CreateWebRenderCommands(
5412 aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
5415 bool nsDisplayFixedPosition::UpdateScrollData(
5416 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5417 if (aLayerData) {
5418 if (!mIsFixedBackground) {
5419 aLayerData->SetFixedPositionSides(
5420 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame));
5422 aLayerData->SetFixedPositionScrollContainerId(GetScrollTargetId());
5424 nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5425 return true;
5428 bool nsDisplayFixedPosition::ShouldGetFixedOrStickyAnimationId() {
5429 #if defined(MOZ_WIDGET_ANDROID)
5430 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5431 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) ==
5432 GetScrollTargetId();
5433 #else
5434 return false;
5435 #endif
5438 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream& aStream) {
5439 aStream << nsPrintfCString(
5440 " (containerASR %s) (scrolltarget %" PRIu64 ")",
5441 ActiveScrolledRoot::ToString(mScrollTargetASR).get(),
5442 GetScrollTargetId())
5443 .get();
5446 TableType GetTableTypeFromFrame(nsIFrame* aFrame) {
5447 if (aFrame->IsTableFrame()) {
5448 return TableType::Table;
5451 if (aFrame->IsTableColFrame()) {
5452 return TableType::TableCol;
5455 if (aFrame->IsTableColGroupFrame()) {
5456 return TableType::TableColGroup;
5459 if (aFrame->IsTableRowFrame()) {
5460 return TableType::TableRow;
5463 if (aFrame->IsTableRowGroupFrame()) {
5464 return TableType::TableRowGroup;
5467 if (aFrame->IsTableCellFrame()) {
5468 return TableType::TableCell;
5471 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5472 return TableType::Table;
5475 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5476 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5477 nsIFrame* aAncestorFrame, const ActiveScrolledRoot* aScrollTargetASR)
5478 : nsDisplayFixedPosition(aBuilder, aFrame, aList, aScrollTargetASR),
5479 mAncestorFrame(aAncestorFrame) {
5480 if (aBuilder->IsRetainingDisplayList()) {
5481 mAncestorFrame->AddDisplayItem(this);
5485 nsDisplayStickyPosition::nsDisplayStickyPosition(
5486 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5487 const ActiveScrolledRoot* aActiveScrolledRoot,
5488 const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
5489 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5490 mContainerASR(aContainerASR),
5491 mClippedToDisplayPort(aClippedToDisplayPort),
5492 mShouldFlatten(false) {
5493 MOZ_COUNT_CTOR(nsDisplayStickyPosition);
5496 // Returns the smallest distance from "0" to the range [min, max] where
5497 // min <= max. Despite the name, the return value is actually a 1-D vector,
5498 // and so may be negative if max < 0.
5499 static nscoord DistanceToRange(nscoord min, nscoord max) {
5500 MOZ_ASSERT(min <= max);
5501 if (max < 0) {
5502 return max;
5504 if (min > 0) {
5505 return min;
5507 MOZ_ASSERT(min <= 0 && max >= 0);
5508 return 0;
5511 // Returns the magnitude of the part of the range [min, max] that is greater
5512 // than zero. The return value is always non-negative.
5513 static nscoord PositivePart(nscoord min, nscoord max) {
5514 MOZ_ASSERT(min <= max);
5515 if (min >= 0) {
5516 return max - min;
5518 if (max > 0) {
5519 return max;
5521 return 0;
5524 // Returns the magnitude of the part of the range [min, max] that is less
5525 // than zero. The return value is always non-negative.
5526 static nscoord NegativePart(nscoord min, nscoord max) {
5527 MOZ_ASSERT(min <= max);
5528 if (max <= 0) {
5529 return max - min;
5531 if (min < 0) {
5532 return 0 - min;
5534 return 0;
5537 StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() {
5538 StickyScrollContainer* stickyScrollContainer =
5539 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
5540 if (stickyScrollContainer) {
5541 // If there's no ASR for the scrollframe that this sticky item is attached
5542 // to, then don't create a WR sticky item for it either. Trying to do so
5543 // will end in sadness because WR will interpret some coordinates as
5544 // relative to the nearest enclosing scrollframe, which will correspond
5545 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5546 // same as the scrollframe this sticky item is actually supposed to be
5547 // attached to, thus the sadness.
5548 // Not sending WR the sticky item is ok, because the enclosing scrollframe
5549 // will never be asynchronously scrolled. Instead we will always position
5550 // the sticky items correctly on the gecko side and WR will never need to
5551 // adjust their position itself.
5552 MOZ_ASSERT(
5553 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5554 if (!stickyScrollContainer->ScrollFrame()
5555 ->IsMaybeAsynchronouslyScrolled()) {
5556 stickyScrollContainer = nullptr;
5559 return stickyScrollContainer;
5562 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5563 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5564 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5565 nsDisplayListBuilder* aDisplayListBuilder) {
5566 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5568 Maybe<wr::SpaceAndClipChainHelper> saccHelper;
5570 if (stickyScrollContainer) {
5571 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5573 bool snap;
5574 nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
5576 Maybe<float> topMargin;
5577 Maybe<float> rightMargin;
5578 Maybe<float> bottomMargin;
5579 Maybe<float> leftMargin;
5580 wr::StickyOffsetBounds vBounds = {0.0, 0.0};
5581 wr::StickyOffsetBounds hBounds = {0.0, 0.0};
5582 nsPoint appliedOffset;
5584 nsRectAbsolute outer;
5585 nsRectAbsolute inner;
5586 stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5588 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
5589 nsPoint offset =
5590 scrollFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5592 // Adjust the scrollPort coordinates to be relative to the reference frame,
5593 // so that it is in the same space as everything else.
5594 nsRect scrollPort =
5595 stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
5596 scrollPort += offset;
5598 // The following computations make more sense upon understanding the
5599 // semantics of "inner" and "outer", which is explained in the comment on
5600 // SetStickyPositionData in Layers.h.
5602 if (outer.YMost() != inner.YMost()) {
5603 // Question: How far will itemBounds.y be from the top of the scrollport
5604 // when we have scrolled from the current scroll position of "0" to
5605 // reach the range [inner.YMost(), outer.YMost()] where the item gets
5606 // stuck?
5607 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5608 // needs to be adjusted by the distance to the range, less any other
5609 // sticky ranges that fall between 0 and the range. If the distance is
5610 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5611 // scrolling upwards (decreasing scroll offset) to reach that range,
5612 // which would increase itemBounds.y and make it farther away from the
5613 // top of the scrollport. So in that case the adjustment is -distance.
5614 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5615 // we would be scrolling downwards, itemBounds.y would decrease, and we
5616 // again need to adjust by -distance. If we are already in the range
5617 // then no adjustment is needed and distance is 0 so again using
5618 // -distance works. If the distance is positive, and the item has both
5619 // top and bottom sticky ranges, then the bottom sticky range may fall
5620 // (entirely[1] or partly[2]) between the current scroll position.
5621 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5622 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5623 // In these cases, the item doesn't actually move for that part of the
5624 // distance, so we need to subtract out that bit, which can be computed
5625 // as the positive portion of the range [outer.Y(), inner.Y()].
5626 nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
5627 if (distance > 0) {
5628 distance -= PositivePart(outer.Y(), inner.Y());
5630 topMargin = Some(NSAppUnitsToFloatPixels(
5631 itemBounds.y - scrollPort.y - distance, auPerDevPixel));
5632 // Question: What is the maximum positive ("downward") offset that WR
5633 // will have to apply to this item in order to prevent the item from
5634 // visually moving?
5635 // Answer: Since the item is "sticky" in the range [inner.YMost(),
5636 // outer.YMost()], the maximum offset will be the size of the range, which
5637 // is outer.YMost() - inner.YMost().
5638 vBounds.max =
5639 NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
5640 // Question: how much of an offset has layout already applied to the item?
5641 // Answer: if we are
5642 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5643 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5644 // then layout has already applied some offset to the position of the
5645 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5646 // and |outer.YMost() - inner.YMost()| in case (b).
5647 if (inner.YMost() < 0) {
5648 appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
5649 MOZ_ASSERT(appliedOffset.y > 0);
5652 if (outer.Y() != inner.Y()) {
5653 // Similar logic as in the previous section, but this time we care about
5654 // the distance from itemBounds.YMost() to scrollPort.YMost().
5655 nscoord distance = DistanceToRange(outer.Y(), inner.Y());
5656 if (distance < 0) {
5657 distance += NegativePart(inner.YMost(), outer.YMost());
5659 bottomMargin = Some(NSAppUnitsToFloatPixels(
5660 scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
5661 // And here WR will be moving the item upwards rather than downwards so
5662 // again things are inverted from the previous block.
5663 vBounds.min =
5664 NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
5665 // We can't have appliedOffset be both positive and negative, and the top
5666 // adjustment takes priority. So here we only update appliedOffset.y if
5667 // it wasn't set by the top-sticky case above.
5668 if (appliedOffset.y == 0 && inner.Y() > 0) {
5669 appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
5670 MOZ_ASSERT(appliedOffset.y < 0);
5673 // Same as above, but for the x-axis
5674 if (outer.XMost() != inner.XMost()) {
5675 nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
5676 if (distance > 0) {
5677 distance -= PositivePart(outer.X(), inner.X());
5679 leftMargin = Some(NSAppUnitsToFloatPixels(
5680 itemBounds.x - scrollPort.x - distance, auPerDevPixel));
5681 hBounds.max =
5682 NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
5683 if (inner.XMost() < 0) {
5684 appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
5685 MOZ_ASSERT(appliedOffset.x > 0);
5688 if (outer.X() != inner.X()) {
5689 nscoord distance = DistanceToRange(outer.X(), inner.X());
5690 if (distance < 0) {
5691 distance += NegativePart(inner.XMost(), outer.XMost());
5693 rightMargin = Some(NSAppUnitsToFloatPixels(
5694 scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
5695 hBounds.min =
5696 NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
5697 if (appliedOffset.x == 0 && inner.X() > 0) {
5698 appliedOffset.x = std::max(0, outer.X()) - inner.X();
5699 MOZ_ASSERT(appliedOffset.x < 0);
5703 LayoutDeviceRect bounds =
5704 LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
5705 wr::LayoutVector2D applied = {
5706 NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
5707 NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
5708 wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame(
5709 wr::ToLayoutRect(bounds), topMargin.ptrOr(nullptr),
5710 rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
5711 leftMargin.ptrOr(nullptr), vBounds, hBounds, applied,
5712 wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5713 wr::SpatialKeyKind::Sticky));
5715 saccHelper.emplace(aBuilder, spatialId);
5716 aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId);
5720 wr::StackingContextParams params;
5721 params.clip =
5722 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5723 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
5724 aBuilder, params);
5725 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc,
5726 aManager, aDisplayListBuilder);
5729 if (stickyScrollContainer) {
5730 aManager->CommandBuilder().PopOverrideForASR(mContainerASR);
5733 return true;
5736 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5737 StickyScrollContainer* aStickyScrollContainer, float aAppUnitsPerDevPixel,
5738 float aScaleX, float aScaleY, LayerRectAbsolute& aStickyOuter,
5739 LayerRectAbsolute& aStickyInner) {
5740 nsRectAbsolute outer;
5741 nsRectAbsolute inner;
5742 aStickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5743 aStickyOuter.SetBox(
5744 NSAppUnitsToFloatPixels(outer.X(), aAppUnitsPerDevPixel) * aScaleX,
5745 NSAppUnitsToFloatPixels(outer.Y(), aAppUnitsPerDevPixel) * aScaleY,
5746 NSAppUnitsToFloatPixels(outer.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5747 NSAppUnitsToFloatPixels(outer.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5748 aStickyInner.SetBox(
5749 NSAppUnitsToFloatPixels(inner.X(), aAppUnitsPerDevPixel) * aScaleX,
5750 NSAppUnitsToFloatPixels(inner.Y(), aAppUnitsPerDevPixel) * aScaleY,
5751 NSAppUnitsToFloatPixels(inner.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5752 NSAppUnitsToFloatPixels(inner.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5755 bool nsDisplayStickyPosition::UpdateScrollData(
5756 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5757 bool hasDynamicToolbar = HasDynamicToolbar();
5758 if (aLayerData && hasDynamicToolbar) {
5759 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5760 if (stickyScrollContainer) {
5761 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5762 float cumulativeResolution =
5763 mFrame->PresShell()->GetCumulativeResolution();
5764 LayerRectAbsolute stickyOuter;
5765 LayerRectAbsolute stickyInner;
5766 CalculateLayerScrollRanges(stickyScrollContainer, auPerDevPixel,
5767 cumulativeResolution, cumulativeResolution,
5768 stickyOuter, stickyInner);
5769 aLayerData->SetStickyScrollRangeOuter(stickyOuter);
5770 aLayerData->SetStickyScrollRangeInner(stickyInner);
5772 SideBits sides =
5773 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5774 aLayerData->SetFixedPositionSides(sides);
5776 ScrollableLayerGuid::ViewID scrollId =
5777 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5778 ->GetScrolledFrame()
5779 ->GetContent());
5780 aLayerData->SetStickyPositionScrollContainerId(scrollId);
5783 // Return true if either there is a dynamic toolbar affecting this sticky
5784 // item or the OwnLayer base implementation returns true for some other
5785 // reason.
5786 bool ret = hasDynamicToolbar;
5787 ret |= nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5788 return ret;
5791 bool nsDisplayStickyPosition::ShouldGetFixedOrStickyAnimationId() {
5792 #if defined(MOZ_WIDGET_ANDROID)
5793 if (HasDynamicToolbar()) { // also implies being in the cross-process RCD
5794 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5795 if (stickyScrollContainer) {
5796 ScrollableLayerGuid::ViewID scrollId =
5797 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5798 ->GetScrolledFrame()
5799 ->GetContent());
5800 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) ==
5801 scrollId;
5804 #endif
5805 return false;
5808 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5809 nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
5810 nsIFrame* aScrollFrame, const CompositorHitTestInfo& aHitInfo,
5811 const nsRect& aHitArea)
5812 : nsDisplayWrapList(aBuilder, aScrollFrame),
5813 mScrollFrame(aScrollFrame),
5814 mScrolledFrame(aScrolledFrame),
5815 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5816 mHitInfo(aHitInfo),
5817 mHitArea(aHitArea) {
5818 #ifdef NS_BUILD_REFCNT_LOGGING
5819 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
5820 #endif
5823 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5824 nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) {
5825 ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
5826 mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(),
5827 ToReferenceFrame(), aLayerManager, mScrollParentId,
5828 mScrollFrame->GetSize(), false);
5829 metadata.GetMetrics().SetIsScrollInfoLayer(true);
5830 nsIScrollableFrame* scrollableFrame = mScrollFrame->GetScrollTargetFrame();
5831 if (scrollableFrame) {
5832 aBuilder->AddScrollFrameToNotify(scrollableFrame);
5835 return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
5838 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5839 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5840 if (aLayerData) {
5841 UniquePtr<ScrollMetadata> metadata =
5842 ComputeScrollMetadata(aData->GetBuilder(), aData->GetManager());
5843 MOZ_ASSERT(aData);
5844 MOZ_ASSERT(metadata);
5845 aLayerData->AppendScrollMetadata(*aData, *metadata);
5847 return true;
5850 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5851 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5852 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5853 nsDisplayListBuilder* aDisplayListBuilder) {
5854 ScrollableLayerGuid::ViewID scrollId =
5855 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame->GetContent());
5857 const LayoutDeviceRect devRect = LayoutDeviceRect::FromAppUnits(
5858 mHitArea, mScrollFrame->PresContext()->AppUnitsPerDevPixel());
5860 const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
5862 aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, mHitInfo,
5863 SideBits::eNone);
5865 return true;
5868 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) {
5869 aStream << " (scrollframe " << mScrollFrame << " scrolledFrame "
5870 << mScrolledFrame << ")";
5873 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5874 nsSubDocumentFrame* aSubDocFrame,
5875 nsDisplayList* aList, int32_t aAPD,
5876 int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags)
5877 : nsDisplaySubDocument(aBuilder, aFrame, aSubDocFrame, aList, aFlags),
5878 mAPD(aAPD),
5879 mParentAPD(aParentAPD) {
5880 MOZ_COUNT_CTOR(nsDisplayZoom);
5883 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder,
5884 bool* aSnap) const {
5885 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
5886 *aSnap = false;
5887 return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD);
5890 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5891 HitTestState* aState,
5892 nsTArray<nsIFrame*>* aOutFrames) {
5893 nsRect rect;
5894 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5895 // rect as well instead of possibly rounding the width or height to zero.
5896 if (aRect.width == 1 && aRect.height == 1) {
5897 rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD));
5898 rect.width = rect.height = 1;
5899 } else {
5900 rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
5902 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5905 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5906 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5907 const ActiveScrolledRoot* aActiveScrolledRoot, FrameMetrics::ViewID aViewID)
5908 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5909 mViewID(aViewID) {
5910 MOZ_COUNT_CTOR(nsDisplayAsyncZoom);
5913 #ifdef NS_BUILD_REFCNT_LOGGING
5914 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5915 MOZ_COUNT_DTOR(nsDisplayAsyncZoom);
5917 #endif
5919 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder* aBuilder,
5920 const nsRect& aRect, HitTestState* aState,
5921 nsTArray<nsIFrame*>* aOutFrames) {
5922 #ifdef DEBUG
5923 nsIScrollableFrame* scrollFrame = do_QueryFrame(mFrame);
5924 MOZ_ASSERT(scrollFrame && ViewportUtils::IsZoomedContentRoot(
5925 scrollFrame->GetScrolledFrame()));
5926 #endif
5927 nsRect rect = ViewportUtils::VisualToLayout(aRect, mFrame->PresShell());
5928 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5931 bool nsDisplayAsyncZoom::UpdateScrollData(
5932 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5933 bool ret = nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5934 MOZ_ASSERT(ret);
5935 if (aLayerData) {
5936 aLayerData->SetAsyncZoomContainerId(mViewID);
5938 return ret;
5941 ///////////////////////////////////////////////////
5942 // nsDisplayTransform Implementation
5945 #ifndef DEBUG
5946 static_assert(sizeof(nsDisplayTransform) <= 512,
5947 "nsDisplayTransform has grown");
5948 #endif
5950 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5951 nsIFrame* aFrame, nsDisplayList* aList,
5952 const nsRect& aChildrenBuildingRect)
5953 : nsPaintedDisplayItem(aBuilder, aFrame),
5954 mChildren(aBuilder),
5955 mTransform(Some(Matrix4x4())),
5956 mChildrenBuildingRect(aChildrenBuildingRect),
5957 mPrerenderDecision(PrerenderDecision::No),
5958 mIsTransformSeparator(true),
5959 mHasTransformGetter(false),
5960 mHasAssociatedPerspective(false),
5961 mContainsASRs(false) {
5962 MOZ_COUNT_CTOR(nsDisplayTransform);
5963 MOZ_ASSERT(aFrame, "Must have a frame!");
5964 Init(aBuilder, aList);
5967 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5968 nsIFrame* aFrame, nsDisplayList* aList,
5969 const nsRect& aChildrenBuildingRect,
5970 PrerenderDecision aPrerenderDecision)
5971 : nsPaintedDisplayItem(aBuilder, aFrame),
5972 mChildren(aBuilder),
5973 mChildrenBuildingRect(aChildrenBuildingRect),
5974 mPrerenderDecision(aPrerenderDecision),
5975 mIsTransformSeparator(false),
5976 mHasTransformGetter(false),
5977 mHasAssociatedPerspective(false),
5978 mContainsASRs(false) {
5979 MOZ_COUNT_CTOR(nsDisplayTransform);
5980 MOZ_ASSERT(aFrame, "Must have a frame!");
5981 SetReferenceFrameToAncestor(aBuilder);
5982 Init(aBuilder, aList);
5985 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5986 nsIFrame* aFrame, nsDisplayList* aList,
5987 const nsRect& aChildrenBuildingRect,
5988 decltype(WithTransformGetter))
5989 : nsPaintedDisplayItem(aBuilder, aFrame),
5990 mChildren(aBuilder),
5991 mChildrenBuildingRect(aChildrenBuildingRect),
5992 mPrerenderDecision(PrerenderDecision::No),
5993 mIsTransformSeparator(false),
5994 mHasTransformGetter(true),
5995 mHasAssociatedPerspective(false),
5996 mContainsASRs(false) {
5997 MOZ_COUNT_CTOR(nsDisplayTransform);
5998 MOZ_ASSERT(aFrame, "Must have a frame!");
5999 MOZ_ASSERT(aFrame->GetTransformGetter());
6000 Init(aBuilder, aList);
6003 void nsDisplayTransform::SetReferenceFrameToAncestor(
6004 nsDisplayListBuilder* aBuilder) {
6005 if (mFrame == aBuilder->RootReferenceFrame()) {
6006 return;
6008 // We manually recompute mToReferenceFrame without going through the
6009 // builder, since this won't apply the 'additional offset'. Our
6010 // children will already be painting with that applied, and we don't
6011 // want to include it a second time in our transform. We don't recompute
6012 // our visible/building rects, since those should still include the additional
6013 // offset.
6014 // TODO: Are there are things computed using our ToReferenceFrame that should
6015 // have the additional offset applied? Should we instead just manually remove
6016 // the offset from our transform instead of this more general value?
6017 // Can we instead apply the additional offset to us and not our children, like
6018 // we do for all other offsets (and how reference frames are supposed to
6019 // work)?
6020 nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame);
6021 const nsIFrame* referenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
6022 mToReferenceFrame = mFrame->GetOffsetToCrossDoc(referenceFrame);
6025 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
6026 nsDisplayList* aChildren) {
6027 mChildren.AppendToTop(aChildren);
6028 UpdateBounds(aBuilder);
6031 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
6032 return false;
6035 /* Returns the delta specified by the transform-origin property.
6036 * This is a positive delta, meaning that it indicates the direction to move
6037 * to get from (0, 0) of the frame to the transform origin. This function is
6038 * called off the main thread.
6040 /* static */
6041 Point3D nsDisplayTransform::GetDeltaToTransformOrigin(
6042 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6043 float aAppUnitsPerPixel) {
6044 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6045 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6046 aFrame->Combines3DTransformWithAncestors(),
6047 "Shouldn't get a delta for an untransformed frame!");
6049 if (!aFrame->IsTransformed()) {
6050 return Point3D();
6053 /* For both of the coordinates, if the value of transform is a
6054 * percentage, it's relative to the size of the frame. Otherwise, if it's
6055 * a distance, it's already computed for us!
6057 const nsStyleDisplay* display = aFrame->StyleDisplay();
6059 const StyleTransformOrigin& transformOrigin = display->mTransformOrigin;
6060 CSSPoint origin = nsStyleTransformMatrix::Convert2DPosition(
6061 transformOrigin.horizontal, transformOrigin.vertical, aRefBox);
6063 // Note:
6064 // 1. SVG frames have a reference box that can be (and typically is) offset
6065 // from the TopLeft() of the frame. We need to account for that here.
6066 // 2. If we are using transform-box:content-box in CSS layout, we have the
6067 // offset from TopLeft() of the frame as well.
6068 origin.x += CSSPixel::FromAppUnits(aRefBox.X());
6069 origin.y += CSSPixel::FromAppUnits(aRefBox.Y());
6071 float scale = AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel);
6072 float z = transformOrigin.depth._0;
6073 return Point3D(origin.x * scale, origin.y * scale, z * scale);
6076 /* static */
6077 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
6078 float aAppUnitsPerPixel,
6079 Matrix4x4& aOutMatrix) {
6080 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6081 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6082 aFrame->Combines3DTransformWithAncestors(),
6083 "Shouldn't get a delta for an untransformed frame!");
6084 MOZ_ASSERT(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
6086 if (!aFrame->IsTransformed()) {
6087 return false;
6090 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6091 // correctly yet (similar to the aBoundsOverride case for
6092 // GetResultingTransformMatrix)?
6093 nsIFrame* perspectiveFrame =
6094 aFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6095 if (!perspectiveFrame) {
6096 return false;
6099 /* Grab the values for perspective and perspective-origin (if present) */
6100 const nsStyleDisplay* perspectiveDisplay = perspectiveFrame->StyleDisplay();
6101 if (perspectiveDisplay->mChildPerspective.IsNone()) {
6102 return false;
6105 MOZ_ASSERT(perspectiveDisplay->mChildPerspective.IsLength());
6106 float perspective =
6107 perspectiveDisplay->mChildPerspective.AsLength().ToCSSPixels();
6108 perspective = std::max(1.0f, perspective);
6109 if (perspective < std::numeric_limits<Float>::epsilon()) {
6110 return true;
6113 TransformReferenceBox refBox(perspectiveFrame);
6115 Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition(
6116 perspectiveDisplay->mPerspectiveOrigin.horizontal,
6117 perspectiveDisplay->mPerspectiveOrigin.vertical, refBox,
6118 aAppUnitsPerPixel);
6120 /* GetOffsetTo computes the offset required to move from 0,0 in
6121 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6122 * this, it's faster to compute this way.
6124 nsPoint frameToPerspectiveOffset = -aFrame->GetOffsetTo(perspectiveFrame);
6125 Point frameToPerspectiveGfxOffset(
6126 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.x, aAppUnitsPerPixel),
6127 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.y, aAppUnitsPerPixel));
6129 /* Move the perspective origin to be relative to aFrame, instead of relative
6130 * to the containing block which is how it was specified in the style system.
6132 perspectiveOrigin += frameToPerspectiveGfxOffset;
6134 aOutMatrix._34 =
6135 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective),
6136 aAppUnitsPerPixel);
6138 aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
6139 return true;
6142 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6143 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6144 float aAppUnitsPerPixel)
6145 : mFrame(aFrame),
6146 mTranslate(aFrame->StyleDisplay()->mTranslate),
6147 mRotate(aFrame->StyleDisplay()->mRotate),
6148 mScale(aFrame->StyleDisplay()->mScale),
6149 mTransform(aFrame->StyleDisplay()->mTransform),
6150 mMotion(aFrame->StyleDisplay()->mOffsetPath.IsNone()
6151 ? Nothing()
6152 : MotionPathUtils::ResolveMotionPath(aFrame, aRefBox)),
6153 mToTransformOrigin(
6154 GetDeltaToTransformOrigin(aFrame, aRefBox, aAppUnitsPerPixel)) {}
6156 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6157 * translates from local coordinate space to transform coordinate space, then
6158 * hands it back.
6160 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6161 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6162 float aAppUnitsPerPixel) {
6163 return GetResultingTransformMatrixInternal(aProperties, aRefBox, nsPoint(),
6164 aAppUnitsPerPixel, 0);
6167 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6168 const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel,
6169 uint32_t aFlags) {
6170 TransformReferenceBox refBox(aFrame);
6171 FrameTransformProperties props(aFrame, refBox, aAppUnitsPerPixel);
6172 return GetResultingTransformMatrixInternal(props, refBox, aOrigin,
6173 aAppUnitsPerPixel, aFlags);
6176 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal(
6177 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6178 const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags) {
6179 const nsIFrame* frame = aProperties.mFrame;
6180 NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE),
6181 "Must have a frame to compute perspective!");
6183 // IncrementScaleRestyleCountIfNeeded in ActiveLayerTracker.cpp is a
6184 // simplified copy of this function.
6186 // Get the underlying transform matrix:
6188 /* Get the matrix, then change its basis to factor in the origin. */
6189 Matrix4x4 result;
6190 // Call IsSVGTransformed() regardless of the value of
6191 // aProperties.HasTransform(), since we still need any
6192 // potential parentsChildrenOnlyTransform.
6193 Matrix svgTransform, parentsChildrenOnlyTransform;
6194 const bool hasSVGTransforms =
6195 frame && frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
6196 frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform);
6197 bool shouldRound = nsLayoutUtils::ShouldSnapToGrid(frame);
6199 /* Transformed frames always have a transform, or are preserving 3d (and might
6200 * still have perspective!) */
6201 if (aProperties.HasTransform()) {
6202 result = nsStyleTransformMatrix::ReadTransforms(
6203 aProperties.mTranslate, aProperties.mRotate, aProperties.mScale,
6204 aProperties.mMotion.ptrOr(nullptr), aProperties.mTransform, aRefBox,
6205 aAppUnitsPerPixel);
6206 } else if (hasSVGTransforms) {
6207 // Correct the translation components for zoom:
6208 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6209 svgTransform._31 *= pixelsPerCSSPx;
6210 svgTransform._32 *= pixelsPerCSSPx;
6211 result = Matrix4x4::From2D(svgTransform);
6214 // Apply any translation due to 'transform-origin' and/or 'transform-box':
6215 result.ChangeBasis(aProperties.mToTransformOrigin);
6217 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
6218 // an explanation of what children-only transforms are.
6219 const bool parentHasChildrenOnlyTransform =
6220 hasSVGTransforms && !parentsChildrenOnlyTransform.IsIdentity();
6222 if (parentHasChildrenOnlyTransform) {
6223 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6224 parentsChildrenOnlyTransform._31 *= pixelsPerCSSPx;
6225 parentsChildrenOnlyTransform._32 *= pixelsPerCSSPx;
6227 Point3D frameOffset(
6228 NSAppUnitsToFloatPixels(-frame->GetPosition().x, aAppUnitsPerPixel),
6229 NSAppUnitsToFloatPixels(-frame->GetPosition().y, aAppUnitsPerPixel), 0);
6230 Matrix4x4 parentsChildrenOnlyTransform3D =
6231 Matrix4x4::From2D(parentsChildrenOnlyTransform)
6232 .ChangeBasis(frameOffset);
6234 result *= parentsChildrenOnlyTransform3D;
6237 Matrix4x4 perspectiveMatrix;
6238 bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE;
6239 if (hasPerspective) {
6240 if (ComputePerspectiveMatrix(frame, aAppUnitsPerPixel, perspectiveMatrix)) {
6241 result *= perspectiveMatrix;
6245 if ((aFlags & INCLUDE_PRESERVE3D_ANCESTORS) && frame &&
6246 frame->Combines3DTransformWithAncestors()) {
6247 // Include the transform set on our parent
6248 nsIFrame* parentFrame =
6249 frame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6250 NS_ASSERTION(parentFrame && parentFrame->IsTransformed() &&
6251 parentFrame->Extend3DContext(),
6252 "Preserve3D mismatch!");
6253 TransformReferenceBox refBox(parentFrame);
6254 FrameTransformProperties props(parentFrame, refBox, aAppUnitsPerPixel);
6256 uint32_t flags =
6257 aFlags & (INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE);
6259 // If this frame isn't transformed (but we exist for backface-visibility),
6260 // then we're not a reference frame so no offset to origin will be added.
6261 // Otherwise we need to manually translate into our parent's coordinate
6262 // space.
6263 if (frame->IsTransformed()) {
6264 nsLayoutUtils::PostTranslate(result, frame->GetPosition(),
6265 aAppUnitsPerPixel, shouldRound);
6267 Matrix4x4 parent = GetResultingTransformMatrixInternal(
6268 props, refBox, nsPoint(0, 0), aAppUnitsPerPixel, flags);
6269 result = result * parent;
6272 if (aFlags & OFFSET_BY_ORIGIN) {
6273 nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel,
6274 shouldRound);
6277 return result;
6280 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6281 static constexpr nsCSSPropertyIDSet opacitySet =
6282 nsCSSPropertyIDSet::OpacityProperties();
6283 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, opacitySet)) {
6284 return true;
6287 EffectCompositor::SetPerformanceWarning(
6288 mFrame, opacitySet,
6289 AnimationPerformanceWarning(
6290 AnimationPerformanceWarning::Type::OpacityFrameInactive));
6292 return false;
6295 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6296 return mPrerenderDecision != PrerenderDecision::No;
6299 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
6300 nsDisplayListBuilder* aBuilder) {
6301 return StaticPrefs::gfx_omta_background_color();
6304 static bool IsInStickyPositionedSubtree(const nsIFrame* aFrame) {
6305 for (const nsIFrame* frame = aFrame; frame;
6306 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame)) {
6307 if (frame->IsStickyPositioned()) {
6308 return true;
6311 return false;
6314 static bool ShouldUsePartialPrerender(const nsIFrame* aFrame) {
6315 return StaticPrefs::layout_animation_prerender_partial() &&
6316 // Bug 1642547: Support partial prerender for position:sticky elements.
6317 !IsInStickyPositionedSubtree(aFrame);
6320 /* static */
6321 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
6322 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect)
6323 -> PrerenderInfo {
6324 PrerenderInfo result;
6325 // If we are in a preserve-3d tree, and we've disallowed async animations, we
6326 // return No prerender decision directly.
6327 if ((aFrame->Extend3DContext() ||
6328 aFrame->Combines3DTransformWithAncestors()) &&
6329 !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
6330 return result;
6333 // Elements whose transform has been modified recently, or which
6334 // have a compositor-animated transform, can be prerendered. An element
6335 // might have only just had its transform animated in which case
6336 // the ActiveLayerManager may not have been notified yet.
6337 static constexpr nsCSSPropertyIDSet transformSet =
6338 nsCSSPropertyIDSet::TransformLikeProperties();
6339 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame) &&
6340 !EffectCompositor::HasAnimationsForCompositor(
6341 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
6342 EffectCompositor::SetPerformanceWarning(
6343 aFrame, transformSet,
6344 AnimationPerformanceWarning(
6345 AnimationPerformanceWarning::Type::TransformFrameInactive));
6347 // This case happens when we're sure that the frame is not animated and its
6348 // preserve-3d ancestors are not, either. So we don't need to pre-render.
6349 // However, this decision shouldn't affect the decisions for other frames in
6350 // the preserve-3d context. We need this flag to determine whether we should
6351 // block async animations on other frames in the current preserve-3d tree.
6352 result.mHasAnimations = false;
6353 return result;
6356 // We should not allow prerender if any ancestor container element has
6357 // mask/clip-path effects.
6359 // With prerender and async transform animation, we do not need to restyle an
6360 // animated element to respect position changes, since that transform is done
6361 // by layer animation. As a result, the container element is not aware of
6362 // position change of that containing element and loses the chance to update
6363 // the content of mask/clip-path.
6365 // Why do we need to update a mask? This is relative to how we generate a
6366 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
6367 // mask layer, to reduce memory usage, we did not choose the size of the
6368 // masked element as mask size. Instead, we read the union of bounds of all
6369 // children display items by nsDisplayWrapList::GetBounds, which is smaller
6370 // than or equal to the masked element's boundary, and use it as the position
6371 // size of the mask layer. That union bounds is actually affected by the
6372 // geometry of the animated element. To keep the content of mask up to date,
6373 // forbidding of prerender is required.
6374 for (nsIFrame* container =
6375 nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
6376 container;
6377 container = nsLayoutUtils::GetCrossDocParentFrameInProcess(container)) {
6378 const nsStyleSVGReset* svgReset = container->StyleSVGReset();
6379 if (svgReset->HasMask() || svgReset->HasClipPath()) {
6380 return result;
6384 // If the incoming dirty rect already contains the entire overflow area,
6385 // we are already rendering the entire content.
6386 nsRect overflow = aFrame->InkOverflowRectRelativeToSelf();
6387 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
6388 // in cases of non-invertible transforms, so we set `untransformedRect` to
6389 // `aDirtyRect` as an initial value for such cases.
6390 nsRect untransformedDirtyRect = *aDirtyRect;
6391 UntransformRect(*aDirtyRect, overflow, aFrame, &untransformedDirtyRect);
6392 if (untransformedDirtyRect.Contains(overflow)) {
6393 *aDirtyRect = untransformedDirtyRect;
6394 result.mDecision = PrerenderDecision::Full;
6395 return result;
6398 float viewportRatio =
6399 StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
6400 uint32_t absoluteLimitX =
6401 StaticPrefs::layout_animation_prerender_absolute_limit_x();
6402 uint32_t absoluteLimitY =
6403 StaticPrefs::layout_animation_prerender_absolute_limit_y();
6404 nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
6406 float resolution = aFrame->PresShell()->GetCumulativeResolution();
6407 if (resolution < 1.0f) {
6408 refSize.SizeTo(
6409 NSCoordSaturatingNonnegativeMultiply(refSize.width, 1.0f / resolution),
6410 NSCoordSaturatingNonnegativeMultiply(refSize.height,
6411 1.0f / resolution));
6414 // Only prerender if the transformed frame's size is <= a multiple of the
6415 // reference frame size (~viewport), and less than an absolute limit.
6416 // Both the ratio and the absolute limit are configurable.
6417 nscoord maxLength = std::max(nscoord(refSize.width * viewportRatio),
6418 nscoord(refSize.height * viewportRatio));
6419 nsSize relativeLimit(maxLength, maxLength);
6420 nsSize absoluteLimit(
6421 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
6422 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
6423 nsSize maxSize = Min(relativeLimit, absoluteLimit);
6425 const auto transform = nsLayoutUtils::GetTransformToAncestor(
6426 RelativeTo{aFrame},
6427 RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});
6428 const gfxRect transformedBounds = transform.TransformAndClipBounds(
6429 gfxRect(overflow.x, overflow.y, overflow.width, overflow.height),
6430 gfxRect::MaxIntRect());
6431 const nsSize frameSize =
6432 nsSize(transformedBounds.width, transformedBounds.height);
6434 uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
6435 uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
6436 if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
6437 *aDirtyRect = overflow;
6438 result.mDecision = PrerenderDecision::Full;
6439 return result;
6442 if (ShouldUsePartialPrerender(aFrame)) {
6443 *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(
6444 aFrame, untransformedDirtyRect, overflow, maxSize);
6445 result.mDecision = PrerenderDecision::Partial;
6446 return result;
6449 if (frameArea > maxLimitArea) {
6450 uint64_t appUnitsPerPixel = AppUnitsPerCSSPixel();
6451 EffectCompositor::SetPerformanceWarning(
6452 aFrame, transformSet,
6453 AnimationPerformanceWarning(
6454 AnimationPerformanceWarning::Type::ContentTooLargeArea,
6456 int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
6457 int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
6458 }));
6459 } else {
6460 EffectCompositor::SetPerformanceWarning(
6461 aFrame, transformSet,
6462 AnimationPerformanceWarning(
6463 AnimationPerformanceWarning::Type::ContentTooLarge,
6465 nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
6466 nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
6467 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
6468 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
6469 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
6470 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
6471 }));
6474 return result;
6477 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
6478 * visible or hit. */
6479 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix) {
6480 if (aMatrix.IsSingular()) {
6481 return false;
6483 if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) {
6484 return false;
6486 return true;
6489 const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const {
6490 if (mTransform) {
6491 return *mTransform;
6494 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6496 if (mHasTransformGetter) {
6497 mTransform.emplace((mFrame->GetTransformGetter())(mFrame, scale));
6498 Point3D newOrigin =
6499 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
6500 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f);
6501 mTransform->ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
6502 } else if (!mIsTransformSeparator) {
6503 DebugOnly<bool> isReference = mFrame->IsTransformed() ||
6504 mFrame->Combines3DTransformWithAncestors() ||
6505 mFrame->Extend3DContext();
6506 MOZ_ASSERT(isReference);
6507 mTransform.emplace(
6508 GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale,
6509 INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN));
6510 } else {
6511 // Use identity matrix
6512 mTransform.emplace();
6515 return *mTransform;
6518 const Matrix4x4Flagged& nsDisplayTransform::GetInverseTransform() const {
6519 if (mInverseTransform) {
6520 return *mInverseTransform;
6523 MOZ_ASSERT(!GetTransform().IsSingular());
6525 mInverseTransform.emplace(GetTransform().Inverse());
6527 return *mInverseTransform;
6530 Matrix4x4 nsDisplayTransform::GetTransformForRendering(
6531 LayoutDevicePoint* aOutOrigin) const {
6532 if (!mFrame->HasPerspective() || mHasTransformGetter ||
6533 mIsTransformSeparator) {
6534 if (!mHasTransformGetter && !mIsTransformSeparator && aOutOrigin) {
6535 // If aOutOrigin is provided, put the offset to origin into it, because
6536 // we need to keep it separate for webrender. The combination of
6537 // *aOutOrigin and the returned matrix here should always be equivalent
6538 // to what GetTransform() would have returned.
6539 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6540 *aOutOrigin = LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale);
6542 // The rounding behavior should also be the same as GetTransform().
6543 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6544 aOutOrigin->Round();
6546 return GetResultingTransformMatrix(mFrame, nsPoint(0, 0), scale,
6547 INCLUDE_PERSPECTIVE);
6549 return GetTransform().GetMatrix();
6551 MOZ_ASSERT(!mHasTransformGetter);
6553 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6554 // Don't include perspective transform, or the offset to origin, since
6555 // nsDisplayPerspective will handle both of those.
6556 return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
6559 const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
6560 nsDisplayListBuilder* aBuilder) {
6561 MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
6563 if (!IsLeafOf3DContext()) {
6564 return GetTransform().GetMatrix();
6567 if (!mTransformPreserves3D) {
6568 const nsIFrame* establisher; // Establisher of the 3D rendering context.
6569 for (establisher = mFrame;
6570 establisher && establisher->Combines3DTransformWithAncestors();
6571 establisher =
6572 establisher->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
6574 const nsIFrame* establisherReference = aBuilder->FindReferenceFrameFor(
6575 nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher));
6577 nsPoint offset = establisher->GetOffsetToCrossDoc(establisherReference);
6578 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6579 uint32_t flags =
6580 INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN;
6581 mTransformPreserves3D = MakeUnique<Matrix4x4>(
6582 GetResultingTransformMatrix(mFrame, offset, scale, flags));
6585 return *mTransformPreserves3D;
6588 bool nsDisplayTransform::CreateWebRenderCommands(
6589 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
6590 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
6591 nsDisplayListBuilder* aDisplayListBuilder) {
6592 // We want to make sure we don't pollute the transform property in the WR
6593 // stacking context by including the position of this frame (relative to the
6594 // parent reference frame). We need to keep those separate; the position of
6595 // this frame goes into the stacking context bounds while the transform goes
6596 // into the transform.
6597 LayoutDevicePoint position;
6598 Matrix4x4 newTransformMatrix = GetTransformForRendering(&position);
6600 gfx::Matrix4x4* transformForSC = &newTransformMatrix;
6601 if (newTransformMatrix.IsIdentity()) {
6602 // If the transform is an identity transform, strip it out so that WR
6603 // doesn't turn this stacking context into a reference frame, as it
6604 // affects positioning. Bug 1345577 tracks a better fix.
6605 transformForSC = nullptr;
6607 // In ChooseScaleAndSetTransform, we round the offset from the reference
6608 // frame used to adjust the transform, if there is no transform, or it
6609 // is just a translation. We need to do the same here.
6610 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6611 position.Round();
6615 auto key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
6616 wr::SpatialKeyKind::Transform);
6618 // We don't send animations for transform separator display items.
6619 uint64_t animationsId =
6620 mIsTransformSeparator
6622 : AddAnimationsForWebRender(
6623 this, aManager, aDisplayListBuilder,
6624 IsPartialPrerender() ? Some(position) : Nothing());
6625 wr::WrAnimationProperty prop{wr::WrAnimationType::Transform, animationsId,
6626 key};
6628 nsDisplayTransform* deferredTransformItem = nullptr;
6629 if (ShouldDeferTransform()) {
6630 // If it has perspective, we create a new scroll data via the
6631 // UpdateScrollData call because that scenario is more complex. Otherwise,
6632 // if we don't contain any ASRs then just stash the transform on the
6633 // StackingContextHelper and apply it to any scroll data that are created
6634 // inside this nsDisplayTransform.
6635 deferredTransformItem = this;
6638 // Determine if we're possibly animated (= would need an active layer in FLB).
6639 bool animated = !mIsTransformSeparator &&
6640 ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
6642 wr::StackingContextParams params;
6643 params.mBoundTransform = &newTransformMatrix;
6644 params.animation = animationsId ? &prop : nullptr;
6646 wr::WrTransformInfo transform_info;
6647 if (transformForSC) {
6648 transform_info.transform = wr::ToLayoutTransform(newTransformMatrix);
6649 transform_info.key = key;
6650 params.mTransformPtr = &transform_info;
6651 } else {
6652 params.mTransformPtr = nullptr;
6655 params.prim_flags = !BackfaceIsHidden()
6656 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
6657 : wr::PrimitiveFlags{0};
6658 params.paired_with_perspective = mHasAssociatedPerspective;
6659 params.mDeferredTransformItem = deferredTransformItem;
6660 params.mAnimated = animated;
6661 // Determine if we would have to rasterize any items in local raster space
6662 // (i.e. disable subpixel AA). We don't always need to rasterize locally even
6663 // if the stacking context is possibly animated (at the cost of potentially
6664 // some false negatives with respect to will-change handling), so we pass in
6665 // this determination separately to accurately match with when FLB would
6666 // normally disable subpixel AA.
6667 params.mRasterizeLocally = animated && Frame()->HasAnimationOfTransform();
6668 params.SetPreserve3D(mFrame->Extend3DContext() && !mIsTransformSeparator);
6669 params.clip =
6670 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
6672 LayoutDeviceSize boundsSize = LayoutDeviceSize::FromAppUnits(
6673 mChildBounds.Size(), mFrame->PresContext()->AppUnitsPerDevPixel());
6675 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
6676 params, LayoutDeviceRect(position, boundsSize));
6678 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6679 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
6680 return true;
6683 bool nsDisplayTransform::UpdateScrollData(
6684 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
6685 if (ShouldDeferTransform()) {
6686 // This case is handled in CreateWebRenderCommands by stashing the transform
6687 // on the stacking context.
6688 return false;
6690 if (aLayerData) {
6691 aLayerData->SetTransform(GetTransform().GetMatrix());
6692 aLayerData->SetTransformIsPerspective(mFrame->ChildrenHavePerspective());
6694 return true;
6697 bool nsDisplayTransform::ShouldSkipTransform(
6698 nsDisplayListBuilder* aBuilder) const {
6699 return (aBuilder->RootReferenceFrame() == mFrame) &&
6700 aBuilder->IsForGenerateGlyphMask();
6703 void nsDisplayTransform::Collect3DTransformLeaves(
6704 nsDisplayListBuilder* aBuilder, nsTArray<nsDisplayTransform*>& aLeaves) {
6705 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6706 aLeaves.AppendElement(this);
6707 return;
6710 FlattenedDisplayListIterator iter(aBuilder, &mChildren);
6711 while (iter.HasNext()) {
6712 nsDisplayItem* item = iter.GetNextItem();
6713 if (item->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
6714 auto* perspective = static_cast<nsDisplayPerspective*>(item);
6715 if (!perspective->GetChildren()->GetTop()) {
6716 continue;
6718 item = perspective->GetChildren()->GetTop();
6720 if (item->GetType() != DisplayItemType::TYPE_TRANSFORM) {
6721 gfxCriticalError() << "Invalid child item within 3D transform of type: "
6722 << item->Name();
6723 continue;
6725 static_cast<nsDisplayTransform*>(item)->Collect3DTransformLeaves(aBuilder,
6726 aLeaves);
6730 static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
6731 const gfx::Polygon& aPolygon) {
6732 MOZ_ASSERT(!aPolygon.IsEmpty());
6734 RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
6735 const nsTArray<Point4D>& points = aPolygon.GetPoints();
6737 pathBuilder->MoveTo(points[0].As2DPoint());
6739 for (size_t i = 1; i < points.Length(); ++i) {
6740 pathBuilder->LineTo(points[i].As2DPoint());
6743 pathBuilder->Close();
6744 return pathBuilder->Finish();
6747 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6748 nsDisplayListBuilder* aBuilder, nsTArray<TransformPolygon>& aLeaves) {
6749 std::list<TransformPolygon> inputLayers;
6751 nsTArray<nsDisplayTransform*> leaves;
6752 Collect3DTransformLeaves(aBuilder, leaves);
6753 for (nsDisplayTransform* item : leaves) {
6754 auto bounds = LayoutDeviceRect::FromAppUnits(
6755 item->mChildBounds, item->mFrame->PresContext()->AppUnitsPerDevPixel());
6756 Matrix4x4 transform = item->GetAccumulatedPreserved3DTransform(aBuilder);
6758 if (!IsFrameVisible(item->mFrame, transform)) {
6759 continue;
6761 gfx::Polygon polygon =
6762 gfx::Polygon::FromRect(gfx::Rect(bounds.ToUnknownRect()));
6764 polygon.TransformToScreenSpace(transform);
6766 if (polygon.GetPoints().Length() >= 3) {
6767 inputLayers.push_back(TransformPolygon(item, std::move(polygon)));
6771 if (inputLayers.empty()) {
6772 return;
6775 BSPTree<nsDisplayTransform> tree(inputLayers);
6776 nsTArray<TransformPolygon> orderedLayers(tree.GetDrawOrder());
6778 for (TransformPolygon& polygon : orderedLayers) {
6779 Matrix4x4 inverse =
6780 polygon.data->GetAccumulatedPreserved3DTransform(aBuilder).Inverse();
6782 MOZ_ASSERT(polygon.geometry);
6783 polygon.geometry->TransformToLayerSpace(inverse);
6786 aLeaves = std::move(orderedLayers);
6789 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder,
6790 gfxContext* aCtx) {
6791 Paint(aBuilder, aCtx, Nothing());
6794 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
6795 const Maybe<gfx::Polygon>& aPolygon) {
6796 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6797 MOZ_ASSERT(!aPolygon);
6798 nsTArray<TransformPolygon> leaves;
6799 CollectSorted3DTransformLeaves(aBuilder, leaves);
6800 for (TransformPolygon& item : leaves) {
6801 item.data->Paint(aBuilder, aCtx, item.geometry);
6803 return;
6806 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
6807 Matrix4x4 trans = ShouldSkipTransform(aBuilder)
6808 ? Matrix4x4()
6809 : GetAccumulatedPreserved3DTransform(aBuilder);
6810 if (!IsFrameVisible(mFrame, trans)) {
6811 return;
6814 Matrix trans2d;
6815 if (trans.CanDraw2D(&trans2d)) {
6816 aCtx->Multiply(ThebesMatrix(trans2d));
6818 if (aPolygon) {
6819 RefPtr<gfx::Path> path =
6820 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6821 aCtx->GetDrawTarget()->PushClip(path);
6824 GetChildren()->Paint(aBuilder, aCtx,
6825 mFrame->PresContext()->AppUnitsPerDevPixel());
6827 if (aPolygon) {
6828 aCtx->GetDrawTarget()->PopClip();
6830 return;
6833 // TODO: Implement 3d transform handling, including plane splitting and
6834 // sorting. See BasicCompositor.
6835 auto pixelBounds = LayoutDeviceRect::FromAppUnitsToOutside(
6836 mChildBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
6837 RefPtr<DrawTarget> untransformedDT =
6838 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6839 IntSize(pixelBounds.Width(), pixelBounds.Height()),
6840 SurfaceFormat::B8G8R8A8, true);
6841 if (!untransformedDT || !untransformedDT->IsValid()) {
6842 return;
6844 untransformedDT->SetTransform(
6845 Matrix::Translation(-Point(pixelBounds.X(), pixelBounds.Y())));
6847 gfxContext groupTarget(untransformedDT, /* aPreserveTransform */ true);
6849 if (aPolygon) {
6850 RefPtr<gfx::Path> path =
6851 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6852 aCtx->GetDrawTarget()->PushClip(path);
6855 GetChildren()->Paint(aBuilder, &groupTarget,
6856 mFrame->PresContext()->AppUnitsPerDevPixel());
6858 if (aPolygon) {
6859 aCtx->GetDrawTarget()->PopClip();
6862 RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
6864 trans.PreTranslate(pixelBounds.X(), pixelBounds.Y(), 0);
6865 aCtx->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf, trans);
6868 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
6869 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6870 // completely bypass the main thread for this animation, so it is always
6871 // worthwhile.
6872 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6873 // already involved so there is less to be gained.
6874 // Therefore we check that the *post-transform* bounds of this item are
6875 // big enough to justify an active layer.
6876 return EffectCompositor::HasAnimationsForCompositor(
6877 mFrame, DisplayItemType::TYPE_TRANSFORM) ||
6878 (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame));
6881 nsRect nsDisplayTransform::TransformUntransformedBounds(
6882 nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
6883 bool snap;
6884 const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
6885 // GetTransform always operates in dev pixels.
6886 const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
6887 return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
6888 factor);
6892 * Returns the bounds for this transform. The bounds are calculated during
6893 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6895 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
6896 bool* aSnap) const {
6897 *aSnap = false;
6898 return mBounds;
6901 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
6902 MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
6904 /* Some transforms can get empty bounds in 2D, but might get transformed again
6905 * and get non-empty bounds. A simple example of this would be a 180 degree
6906 * rotation getting applied twice.
6907 * We should not depend on transforming bounds level by level.
6909 * This function collects the bounds of this transform and stores it in
6910 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6911 * down and include the bounds of the child transforms.
6912 * The bounds are transformed with the accumulated transformation matrix up to
6913 * the 3D context root coordinate space.
6915 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6916 accTransform.Accumulate(GetTransform().GetMatrix());
6918 // Do not dive into another 3D context.
6919 if (!IsLeafOf3DContext()) {
6920 for (nsDisplayItem* i : *GetChildren()) {
6921 i->DoUpdateBoundsPreserves3D(aBuilder);
6925 /* The child transforms that extend 3D context further will have empty bounds,
6926 * so the untransformed bounds here is the bounds of all the non-preserve-3d
6927 * content under this transform.
6929 const nsRect rect = TransformUntransformedBounds(
6930 aBuilder, accTransform.GetCurrentTransform());
6931 aBuilder->AccumulateRect(rect);
6934 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6935 nsDisplayListBuilder* aBuilder) {
6936 MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
6937 IsTransformSeparator());
6938 // Updating is not going through to child 3D context.
6939 ComputeBounds(aBuilder);
6942 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
6943 UpdateUntransformedBounds(aBuilder);
6945 if (IsTransformSeparator()) {
6946 MOZ_ASSERT(GetTransform().IsIdentity());
6947 mBounds = mChildBounds;
6948 return;
6951 if (mFrame->Extend3DContext()) {
6952 if (!Combines3DTransformWithAncestors()) {
6953 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6954 // collect the bounds from the child transforms.
6955 UpdateBoundsFor3D(aBuilder);
6956 } else {
6957 // With nested 3D transforms, the 2D bounds might not be useful.
6958 mBounds = nsRect();
6961 return;
6964 MOZ_ASSERT(!mFrame->Extend3DContext());
6966 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
6967 // but mix-blend-mode relies on having bounds set. See bug 1556956.
6969 // A stand-alone transform.
6970 mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
6973 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
6974 MOZ_ASSERT(mFrame->Extend3DContext() &&
6975 !mFrame->Combines3DTransformWithAncestors() &&
6976 !IsTransformSeparator());
6978 // Always start updating from an establisher of a 3D rendering context.
6979 nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
6980 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6981 accTransform.StartRoot();
6982 ComputeBounds(aBuilder);
6983 mBounds = aBuilder->GetAccumulatedRect();
6986 void nsDisplayTransform::UpdateUntransformedBounds(
6987 nsDisplayListBuilder* aBuilder) {
6988 mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
6989 aBuilder, mActiveScrolledRoot);
6992 #ifdef DEBUG_HIT
6993 # include <time.h>
6994 #endif
6996 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
6997 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
6998 const nsRect& aRect, HitTestState* aState,
6999 nsTArray<nsIFrame*>* aOutFrames) {
7000 if (aState->mInPreserves3D) {
7001 GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
7002 return;
7005 /* Here's how this works:
7006 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
7007 * anything).
7008 * 2. Invert the matrix.
7009 * 3. Use it to transform the rect into the correct space.
7010 * 4. Pass that rect down through to the list's version of HitTest.
7012 // GetTransform always operates in dev pixels.
7013 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7014 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7016 if (!IsFrameVisible(mFrame, matrix)) {
7017 return;
7020 const bool oldHitOccludingItem = aState->mHitOccludingItem;
7022 /* We want to go from transformed-space to regular space.
7023 * Thus we have to invert the matrix, which normally does
7024 * the reverse operation (e.g. regular->transformed)
7027 /* Now, apply the transform and pass it down the channel. */
7028 matrix.Invert();
7029 nsRect resultingRect;
7030 // Magic width/height indicating we're hit testing a point, not a rect
7031 const bool testingPoint = aRect.width == 1 && aRect.height == 1;
7032 if (testingPoint) {
7033 Point4D point =
7034 matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
7035 NSAppUnitsToFloatPixels(aRect.y, factor)));
7036 if (!point.HasPositiveWCoord()) {
7037 return;
7040 Point point2d = point.As2DPoint();
7042 resultingRect =
7043 nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
7044 NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1);
7046 } else {
7047 Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
7048 NSAppUnitsToFloatPixels(aRect.y, factor),
7049 NSAppUnitsToFloatPixels(aRect.width, factor),
7050 NSAppUnitsToFloatPixels(aRect.height, factor));
7052 Rect childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds.x, factor),
7053 NSAppUnitsToFloatPixels(mChildBounds.y, factor),
7054 NSAppUnitsToFloatPixels(mChildBounds.width, factor),
7055 NSAppUnitsToFloatPixels(mChildBounds.height, factor));
7057 Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
7059 resultingRect =
7060 nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
7061 NSFloatPixelsToAppUnits(float(rect.Y()), factor),
7062 NSFloatPixelsToAppUnits(float(rect.Width()), factor),
7063 NSFloatPixelsToAppUnits(float(rect.Height()), factor));
7066 if (resultingRect.IsEmpty()) {
7067 return;
7070 #ifdef DEBUG_HIT
7071 printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
7072 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(),
7073 resultingRect.Y());
7074 uint32_t originalFrameCount = aOutFrames.Length();
7075 #endif
7077 GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
7079 if (aState->mHitOccludingItem && !testingPoint &&
7080 !mChildBounds.Contains(aRect)) {
7081 MOZ_ASSERT(aBuilder->HitTestIsForVisibility());
7082 // We're hit-testing a rect that's bigger than our child bounds, but
7083 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so
7084 // we can't stop hit-testing altogether.
7086 // FIXME(emilio): I think this means that theoretically we might include
7087 // some frames fully behind other transformed-but-opaque frames? Then again
7088 // that's our pre-existing behavior for other untransformed content that
7089 // doesn't fill the whole rect. To be fully correct I think we'd need proper
7090 // "known occluded region" tracking, but that might be overkill for our
7091 // purposes here.
7092 aState->mHitOccludingItem = oldHitOccludingItem;
7095 #ifdef DEBUG_HIT
7096 if (originalFrameCount != aOutFrames.Length())
7097 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7098 dynamic_cast<void*>(aOutFrames.ElementAt(0)));
7099 printf("=== end of hit test ===\n");
7100 #endif
7103 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
7104 const nsPoint& aPoint) {
7105 // GetTransform always operates in dev pixels.
7106 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7107 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7109 NS_ASSERTION(IsFrameVisible(mFrame, matrix),
7110 "We can't have hit a frame that isn't visible!");
7112 Matrix4x4 inverse = matrix;
7113 inverse.Invert();
7114 Point4D point =
7115 inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
7116 NSAppUnitsToFloatPixels(aPoint.y, factor)));
7118 Point point2d = point.As2DPoint();
7120 Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
7121 return transformed.z;
7124 /* The transform is opaque iff the transform consists solely of scales and
7125 * translations and if the underlying content is opaque. Thus if the transform
7126 * is of the form
7128 * |a c e|
7129 * |b d f|
7130 * |0 0 1|
7132 * We need b and c to be zero.
7134 * We also need to check whether the underlying opaque content completely fills
7135 * our visible rect. We use UntransformRect which expands to the axis-aligned
7136 * bounding rect, but that's OK since if
7137 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7138 * certainly contains the actual (non-axis-aligned) untransformed rect.
7140 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7141 bool* aSnap) const {
7142 *aSnap = false;
7144 nsRect untransformedVisible;
7145 if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
7146 return nsRegion();
7149 const Matrix4x4Flagged& matrix = GetTransform();
7150 Matrix matrix2d;
7151 if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
7152 return nsRegion();
7155 nsRegion result;
7157 bool tmpSnap;
7158 const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
7159 const nsRegion opaque =
7160 ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
7162 if (opaque.Contains(untransformedVisible)) {
7163 result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
7165 return result;
7168 nsRect nsDisplayTransform::GetComponentAlphaBounds(
7169 nsDisplayListBuilder* aBuilder) const {
7170 if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
7171 return nsRect();
7174 bool snap;
7175 return GetBounds(aBuilder, &snap);
7178 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7179 * the smallest rectangle (in app space) containing the transformed image of
7180 * that rectangle. That is, it takes the four corners of the rectangle,
7181 * transforms them according to the matrix associated with the specified frame,
7182 * then returns the smallest rectangle containing the four transformed points.
7184 * @param aUntransformedBounds The rectangle (in app units) to transform.
7185 * @param aFrame The frame whose transformation should be applied.
7186 * @param aOrigin The delta from the frame origin to the coordinate space origin
7187 * @return The smallest rectangle containing the image of the transformed
7188 * rectangle.
7190 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds,
7191 const nsIFrame* aFrame,
7192 TransformReferenceBox& aRefBox) {
7193 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7195 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7197 FrameTransformProperties props(aFrame, aRefBox, factor);
7198 return nsLayoutUtils::MatrixTransformRect(
7199 aUntransformedBounds,
7200 GetResultingTransformMatrixInternal(props, aRefBox, nsPoint(), factor,
7201 kTransformRectFlags),
7202 factor);
7205 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7206 const nsRect& aChildBounds,
7207 const nsIFrame* aFrame,
7208 nsRect* aOutRect) {
7209 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7211 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7212 Matrix4x4 transform = GetResultingTransformMatrix(aFrame, nsPoint(), factor,
7213 kTransformRectFlags);
7214 return UntransformRect(aTransformedBounds, aChildBounds, transform, factor,
7215 aOutRect);
7218 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7219 const nsRect& aChildBounds,
7220 const Matrix4x4& aMatrix,
7221 float aAppUnitsPerPixel,
7222 nsRect* aOutRect) {
7223 if (aMatrix.IsSingular()) {
7224 return false;
7227 RectDouble result(
7228 NSAppUnitsToFloatPixels(aTransformedBounds.x, aAppUnitsPerPixel),
7229 NSAppUnitsToFloatPixels(aTransformedBounds.y, aAppUnitsPerPixel),
7230 NSAppUnitsToFloatPixels(aTransformedBounds.width, aAppUnitsPerPixel),
7231 NSAppUnitsToFloatPixels(aTransformedBounds.height, aAppUnitsPerPixel));
7233 RectDouble childGfxBounds(
7234 NSAppUnitsToFloatPixels(aChildBounds.x, aAppUnitsPerPixel),
7235 NSAppUnitsToFloatPixels(aChildBounds.y, aAppUnitsPerPixel),
7236 NSAppUnitsToFloatPixels(aChildBounds.width, aAppUnitsPerPixel),
7237 NSAppUnitsToFloatPixels(aChildBounds.height, aAppUnitsPerPixel));
7239 result = aMatrix.Inverse().ProjectRectBounds(result, childGfxBounds);
7240 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result),
7241 aAppUnitsPerPixel);
7242 return true;
7245 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder,
7246 const nsRect& aRect,
7247 nsRect* aOutRect) const {
7248 if (GetTransform().IsSingular()) {
7249 return false;
7252 // GetTransform always operates in dev pixels.
7253 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7254 RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor),
7255 NSAppUnitsToFloatPixels(aRect.y, factor),
7256 NSAppUnitsToFloatPixels(aRect.width, factor),
7257 NSAppUnitsToFloatPixels(aRect.height, factor));
7259 bool snap;
7260 nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
7261 RectDouble childGfxBounds(
7262 NSAppUnitsToFloatPixels(childBounds.x, factor),
7263 NSAppUnitsToFloatPixels(childBounds.y, factor),
7264 NSAppUnitsToFloatPixels(childBounds.width, factor),
7265 NSAppUnitsToFloatPixels(childBounds.height, factor));
7267 /* We want to untransform the matrix, so invert the transformation first! */
7268 result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds);
7270 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
7272 return true;
7275 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) {
7276 aStream << GetTransform().GetMatrix();
7277 if (IsTransformSeparator()) {
7278 aStream << " transform-separator";
7280 if (IsLeafOf3DContext()) {
7281 aStream << " 3d-context-leaf";
7283 if (mFrame->Extend3DContext()) {
7284 aStream << " extends-3d-context";
7286 if (mFrame->Combines3DTransformWithAncestors()) {
7287 aStream << " combines-3d-with-ancestors";
7290 aStream << " prerender(";
7291 switch (mPrerenderDecision) {
7292 case PrerenderDecision::No:
7293 aStream << "no";
7294 break;
7295 case PrerenderDecision::Partial:
7296 aStream << "partial";
7297 break;
7298 case PrerenderDecision::Full:
7299 aStream << "full";
7300 break;
7302 aStream << ")";
7303 aStream << " childrenBuildingRect" << mChildrenBuildingRect;
7306 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
7307 nsIFrame* aFrame,
7308 nsDisplayList* aList)
7309 : nsPaintedDisplayItem(aBuilder, aFrame), mList(aBuilder) {
7310 mList.AppendToTop(aList);
7311 MOZ_ASSERT(mList.Length() == 1);
7312 MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
7315 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder,
7316 gfxContext* aCtx) {
7317 // Just directly recurse into children, since we'll include the persepctive
7318 // value in any nsDisplayTransform children.
7319 GetChildren()->Paint(aBuilder, aCtx,
7320 mFrame->PresContext()->AppUnitsPerDevPixel());
7323 nsRegion nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7324 bool* aSnap) const {
7325 if (!GetChildren()->GetTop()) {
7326 *aSnap = false;
7327 return nsRegion();
7330 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder, aSnap);
7333 bool nsDisplayPerspective::CreateWebRenderCommands(
7334 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7335 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7336 nsDisplayListBuilder* aDisplayListBuilder) {
7337 float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
7338 Matrix4x4 perspectiveMatrix;
7339 DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
7340 mFrame, appUnitsPerPixel, perspectiveMatrix);
7341 MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
7344 * ClipListToRange can remove our child after we were created.
7346 if (!GetChildren()->GetTop()) {
7347 return false;
7351 * The resulting matrix is still in the coordinate space of the transformed
7352 * frame. Append a translation to the reference frame coordinates.
7354 nsDisplayTransform* transform =
7355 static_cast<nsDisplayTransform*>(GetChildren()->GetTop());
7357 Point3D newOrigin =
7358 Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
7359 appUnitsPerPixel),
7360 NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
7361 appUnitsPerPixel),
7362 0.0f);
7363 Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
7365 perspectiveMatrix.PostTranslate(roundedOrigin);
7367 nsIFrame* perspectiveFrame =
7368 mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
7370 // Passing true here is always correct, since perspective always combines
7371 // transforms with the descendants. However that'd make WR do a lot of work
7372 // that it doesn't really need to do if there aren't other transforms forming
7373 // part of the 3D context.
7375 // WR knows how to treat perspective in that case, so the only thing we need
7376 // to do is to ensure we pass true when we're involved in a 3d context in any
7377 // other way via the transform-style property on either the transformed frame
7378 // or the perspective frame in order to not confuse WR's preserve-3d code in
7379 // very awful ways.
7380 bool preserve3D =
7381 mFrame->Extend3DContext() || perspectiveFrame->Extend3DContext();
7383 wr::StackingContextParams params;
7385 wr::WrTransformInfo transform_info;
7386 transform_info.transform = wr::ToLayoutTransform(perspectiveMatrix);
7387 transform_info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
7388 wr::SpatialKeyKind::Perspective);
7389 params.mTransformPtr = &transform_info;
7391 params.reference_frame_kind = wr::WrReferenceFrameKind::Perspective;
7392 params.prim_flags = !BackfaceIsHidden()
7393 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7394 : wr::PrimitiveFlags{0};
7395 params.SetPreserve3D(preserve3D);
7396 params.clip =
7397 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
7399 Maybe<uint64_t> scrollingRelativeTo;
7400 for (const auto* asr = GetActiveScrolledRoot(); asr; asr = asr->mParent) {
7401 // In OOP documents, the root scrollable frame of the in-process root
7402 // document is always active, so using IsAncestorFrameCrossDocInProcess
7403 // should be fine here.
7404 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7405 asr->mScrollableFrame->GetScrolledFrame(), perspectiveFrame)) {
7406 scrollingRelativeTo.emplace(asr->GetViewId());
7407 break;
7411 // We put the perspective reference frame wrapping the transformed frame,
7412 // even though there may be arbitrarily nested scroll frames in between.
7414 // We need to know how many ancestor scroll-frames are we nested in, in order
7415 // for the async scrolling code in WebRender to calculate the right
7416 // transformation for the perspective contents.
7417 params.scrolling_relative_to = scrollingRelativeTo.ptrOr(nullptr);
7419 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
7420 params);
7422 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7423 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
7425 return true;
7428 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
7429 nsTextFrame* aFrame)
7430 : nsPaintedDisplayItem(aBuilder, aFrame),
7431 mVisIStartEdge(0),
7432 mVisIEndEdge(0) {
7433 MOZ_COUNT_CTOR(nsDisplayText);
7434 mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7435 // Bug 748228
7436 mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
7437 mVisibleRect = aBuilder->GetVisibleRect() +
7438 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
7441 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager,
7442 nsDisplayListBuilder* aBuilder) const {
7443 auto* f = static_cast<nsTextFrame*>(mFrame);
7445 if (f->IsSelected()) {
7446 return false;
7449 const nsStyleText* textStyle = f->StyleText();
7450 if (textStyle->HasTextShadow()) {
7451 return false;
7454 nsTextFrame::TextDecorations decorations;
7455 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7456 decorations);
7457 return !decorations.HasDecorationLines();
7460 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
7461 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
7462 // We don't pass mVisibleRect here, since this can be called from within
7463 // the WebRender fallback painting path, and we don't want to issue
7464 // recorded commands that are dependent on the visible/building rect.
7465 RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
7467 auto* textFrame = static_cast<nsTextFrame*>(mFrame);
7468 LCPTextFrameHelper::MaybeUnionTextFrame(textFrame,
7469 mBounds - ToReferenceFrame());
7472 bool nsDisplayText::CreateWebRenderCommands(
7473 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7474 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7475 nsDisplayListBuilder* aDisplayListBuilder) {
7476 auto* f = static_cast<nsTextFrame*>(mFrame);
7477 auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
7479 nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
7480 // Bug 748228
7481 bounds.Inflate(appUnitsPerDevPixel);
7483 if (bounds.IsEmpty()) {
7484 return true;
7487 // For large font sizes, punt to a blob image, to avoid the blurry rendering
7488 // that results from WR clamping the glyph size used for rasterization.
7490 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7492 // This is not strictly accurate, as final used font sizes might not be the
7493 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7494 // altering the used size of the font actually used).
7495 // It also fails to consider how transforms might affect the device-font-size
7496 // that webrender uses (and clamps).
7497 // But it should be near enough for practical purposes; the limitations just
7498 // mean we might sometimes end up with webrender still applying some bitmap
7499 // scaling, or bail out when we didn't really need to.
7500 constexpr float kWebRenderFontSizeLimit = 320.0;
7501 f->EnsureTextRun(nsTextFrame::eInflated);
7502 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7503 if (textRun &&
7504 textRun->GetFontGroup()->GetStyle()->size > kWebRenderFontSizeLimit) {
7505 return false;
7508 gfx::Point deviceOffset =
7509 LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
7510 .ToUnknownPoint();
7512 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7513 // frames) lets us early reject a bunch of things.
7514 nsRect visible = mVisibleRect;
7516 // Add the "source rect" area from which the given shadows could intersect
7517 // with mVisibleRect, and which therefore needs to included in the paint
7518 // operation, to the `visible` rect that we will use to limit the bounds of
7519 // what we send to the renderer.
7520 auto addShadowSourceToVisible = [&](Span<const StyleSimpleShadow> aShadows) {
7521 for (const auto& shadow : aShadows) {
7522 nsRect sourceRect = mVisibleRect;
7523 // Negate the offsets, because we're looking for the "source" rect that
7524 // could cast a shadow into the visible rect, rather than a "target" area
7525 // onto which the visible rect would cast a shadow.
7526 sourceRect.MoveBy(-shadow.horizontal.ToAppUnits(),
7527 -shadow.vertical.ToAppUnits());
7528 // Inflate to account for the shadow blur.
7529 sourceRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
7530 shadow.blur.ToAppUnits(), appUnitsPerDevPixel));
7531 visible.OrWith(sourceRect);
7535 // Shadows can translate things back into view, so we enlarge the notional
7536 // "visible" rect to ensure we don't skip painting relevant parts that might
7537 // cast a shadow within the visible area.
7538 addShadowSourceToVisible(f->StyleText()->mTextShadow.AsSpan());
7540 // Similarly for shadows that may be cast by ::selection.
7541 if (f->IsSelected()) {
7542 nsTextPaintStyle textPaint(f);
7543 Span<const StyleSimpleShadow> shadows;
7544 f->GetSelectionTextShadow(SelectionType::eNormal, textPaint, &shadows);
7545 addShadowSourceToVisible(shadows);
7548 // Inflate a little extra to allow for potential antialiasing "blur".
7549 visible.Inflate(3 * appUnitsPerDevPixel);
7550 bounds = bounds.Intersect(visible);
7552 gfxContext* textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager,
7553 this, bounds, deviceOffset);
7555 LCPTextFrameHelper::MaybeUnionTextFrame(f, bounds - ToReferenceFrame());
7557 aBuilder.StartGroup(this);
7559 RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
7560 aBuilder.GetInheritedOpacity(), true);
7561 const bool result = textDrawer->GetTextDrawer()->Finish();
7563 if (result) {
7564 aBuilder.FinishGroup();
7565 } else {
7566 aBuilder.CancelGroup(true);
7569 return result;
7572 void nsDisplayText::RenderToContext(gfxContext* aCtx,
7573 nsDisplayListBuilder* aBuilder,
7574 const nsRect& aVisibleRect, float aOpacity,
7575 bool aIsRecording) {
7576 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7578 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7579 // antialiased pixels beyond the measured text extents.
7580 // This is temporary until we do this in the actual calculation of text
7581 // extents.
7582 auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
7583 LayoutDeviceRect extraVisible =
7584 LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D);
7585 extraVisible.Inflate(1);
7587 gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width,
7588 extraVisible.height);
7589 pixelVisible.Inflate(2);
7590 pixelVisible.RoundOut();
7592 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7593 if (!aBuilder->IsForGenerateGlyphMask() && !aIsRecording) {
7594 autoSaveClip.Clip(pixelVisible);
7597 NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
7598 NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
7600 gfxContextMatrixAutoSaveRestore matrixSR;
7602 nsPoint framePt = ToReferenceFrame();
7603 if (f->Style()->IsTextCombined()) {
7604 float scaleFactor = nsTextFrame::GetTextCombineScaleFactor(f);
7605 if (scaleFactor != 1.0f) {
7606 if (auto* textDrawer = aCtx->GetTextDrawer()) {
7607 // WebRender doesn't support scaling text like this yet
7608 textDrawer->FoundUnsupportedFeature();
7609 return;
7611 matrixSR.SetContext(aCtx);
7612 // Setup matrix to compress text for text-combine-upright if
7613 // necessary. This is done here because we want selection be
7614 // compressed at the same time as text.
7615 gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
7616 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7617 if (textRun && textRun->IsRightToLeft()) {
7618 pt.x += gfxFloat(f->GetSize().width) / A2D;
7620 gfxMatrix mat = aCtx->CurrentMatrixDouble()
7621 .PreTranslate(pt)
7622 .PreScale(scaleFactor, 1.0)
7623 .PreTranslate(-pt);
7624 aCtx->SetMatrixDouble(mat);
7627 nsTextFrame::PaintTextParams params(aCtx);
7628 params.framePt = gfx::Point(framePt.x, framePt.y);
7629 params.dirtyRect = extraVisible;
7631 if (aBuilder->IsForGenerateGlyphMask()) {
7632 params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
7633 } else {
7634 params.state = nsTextFrame::PaintTextParams::PaintText;
7637 f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
7638 f->IsSelected(), aOpacity);
7641 // This could go to nsDisplayListInvalidation.h, but
7642 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7643 // would produce circular dependencies.
7644 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
7645 public:
7646 nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
7647 : nsDisplayItemGenericGeometry(aItem, aBuilder),
7648 mVisIStartEdge(aItem->VisIStartEdge()),
7649 mVisIEndEdge(aItem->VisIEndEdge()) {
7650 nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
7651 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7652 mDecorations);
7656 * We store the computed text decorations here since they are
7657 * computed using style data from parent frames. Any changes to these
7658 * styles will only invalidate the parent frame and not this frame.
7660 nsTextFrame::TextDecorations mDecorations;
7661 nscoord mVisIStartEdge;
7662 nscoord mVisIEndEdge;
7665 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry(
7666 nsDisplayListBuilder* aBuilder) {
7667 return new nsDisplayTextGeometry(this, aBuilder);
7670 void nsDisplayText::ComputeInvalidationRegion(
7671 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7672 nsRegion* aInvalidRegion) const {
7673 const nsDisplayTextGeometry* geometry =
7674 static_cast<const nsDisplayTextGeometry*>(aGeometry);
7675 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7677 nsTextFrame::TextDecorations decorations;
7678 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7679 decorations);
7681 bool snap;
7682 const nsRect& newRect = geometry->mBounds;
7683 nsRect oldRect = GetBounds(aBuilder, &snap);
7684 if (decorations != geometry->mDecorations ||
7685 mVisIStartEdge != geometry->mVisIStartEdge ||
7686 mVisIEndEdge != geometry->mVisIEndEdge ||
7687 !oldRect.IsEqualInterior(newRect) ||
7688 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
7689 aInvalidRegion->Or(oldRect, newRect);
7693 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) {
7694 #ifdef DEBUG
7695 aStream << " (\"";
7697 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7698 nsCString buf;
7699 f->ToCString(buf);
7701 aStream << buf.get() << "\")";
7702 #endif
7705 nsDisplayEffectsBase::nsDisplayEffectsBase(
7706 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7707 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
7708 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
7709 aClearClipChain) {
7710 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7713 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
7714 nsIFrame* aFrame,
7715 nsDisplayList* aList)
7716 : nsDisplayWrapList(aBuilder, aFrame, aList) {
7717 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7720 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7721 bool* aSnap) const {
7722 *aSnap = false;
7723 return nsRegion();
7726 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder* aBuilder,
7727 const nsRect& aRect, HitTestState* aState,
7728 nsTArray<nsIFrame*>* aOutFrames) {
7729 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
7730 if (SVGIntegrationUtils::HitTestFrameForEffects(
7731 mFrame, rectCenter - ToReferenceFrame())) {
7732 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
7736 gfxRect nsDisplayEffectsBase::BBoxInUserSpace() const {
7737 return SVGUtils::GetBBox(mFrame);
7740 gfxPoint nsDisplayEffectsBase::UserSpaceOffset() const {
7741 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
7744 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7745 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7746 nsRegion* aInvalidRegion) const {
7747 const auto* geometry =
7748 static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
7749 bool snap;
7750 nsRect bounds = GetBounds(aBuilder, &snap);
7751 if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
7752 geometry->mUserSpaceOffset != UserSpaceOffset() ||
7753 !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
7754 // Filter and mask output can depend on the location of the frame's user
7755 // space and on the frame's BBox. We need to invalidate if either of these
7756 // change relative to the reference frame.
7757 // Invalidations from our inactive layer manager are not enough to catch
7758 // some of these cases because filters can produce output even if there's
7759 // nothing in the filter input.
7760 aInvalidRegion->Or(bounds, geometry->mBounds);
7764 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7765 if (mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
7766 ISVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame);
7767 if (!svgFrame) {
7768 return false;
7770 if (auto* svgElement = SVGElement::FromNode(mFrame->GetContent())) {
7771 // The SVG spec says only to draw filters if the element
7772 // has valid dimensions.
7773 return svgElement->HasValidDimensions();
7775 return false;
7778 return true;
7781 using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams;
7783 static void ComputeMaskGeometry(PaintFramesParams& aParams) {
7784 // Properties are added lazily and may have been removed by a restyle, so
7785 // make sure all applicable ones are set again.
7786 nsIFrame* firstFrame =
7787 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
7789 const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
7791 nsTArray<SVGMaskFrame*> maskFrames;
7792 // XXX check return value?
7793 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
7795 if (maskFrames.Length() == 0) {
7796 return;
7799 gfxContext& ctx = aParams.ctx;
7800 nsIFrame* frame = aParams.frame;
7802 nsPoint offsetToUserSpace =
7803 nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame);
7805 auto cssToDevScale = frame->PresContext()->CSSToDevPixelScale();
7806 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
7808 gfxPoint devPixelOffsetToUserSpace =
7809 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, appUnitsPerDevPixel);
7811 gfxContextMatrixAutoSaveRestore matSR(&ctx);
7812 ctx.SetMatrixDouble(
7813 ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace));
7815 // Convert boaderArea and dirtyRect to user space.
7816 nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
7817 nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
7819 // Union all mask layer rectangles in user space.
7820 LayoutDeviceRect maskInUserSpace;
7821 for (size_t i = 0; i < maskFrames.Length(); i++) {
7822 SVGMaskFrame* maskFrame = maskFrames[i];
7823 LayoutDeviceRect currentMaskSurfaceRect;
7825 if (maskFrame) {
7826 auto rect = maskFrame->GetMaskArea(aParams.frame);
7827 currentMaskSurfaceRect =
7828 CSSRect::FromUnknownRect(ToRect(rect)) * cssToDevScale;
7829 } else {
7830 nsCSSRendering::ImageLayerClipState clipState;
7831 nsCSSRendering::GetImageLayerClip(
7832 svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(),
7833 userSpaceBorderArea, userSpaceDirtyRect,
7834 /* aWillPaintBorder = */ false, appUnitsPerDevPixel, &clipState);
7835 currentMaskSurfaceRect = LayoutDeviceRect::FromUnknownRect(
7836 ToRect(clipState.mDirtyRectInDevPx));
7839 maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
7842 if (!maskInUserSpace.IsEmpty()) {
7843 aParams.maskRect = Some(maskInUserSpace);
7844 } else {
7845 aParams.maskRect = Nothing();
7849 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7850 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7851 const ActiveScrolledRoot* aActiveScrolledRoot, bool aWrapsBackdropFilter)
7852 : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
7853 mWrapsBackdropFilter(aWrapsBackdropFilter) {
7854 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
7856 nsPresContext* presContext = mFrame->PresContext();
7857 uint32_t flags =
7858 aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
7859 const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
7860 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7861 const auto& layer = svgReset->mMask.mLayers[i];
7862 if (!layer.mImage.IsResolved()) {
7863 continue;
7865 const nsRect& borderArea = mFrame->GetRectRelativeToSelf();
7866 // NOTE(emilio): We only care about the dest rect so we don't bother
7867 // computing a clip.
7868 bool isTransformedFixed = false;
7869 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
7870 presContext, aFrame, flags, borderArea, borderArea, layer,
7871 &isTransformedFixed);
7872 mDestRects.AppendElement(state.mDestArea);
7876 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) {
7877 // Do not merge items for box-decoration-break:clone elements,
7878 // since each box should have its own mask in that case.
7879 if (aFrame->StyleBorder()->mBoxDecorationBreak ==
7880 StyleBoxDecorationBreak::Clone) {
7881 return false;
7884 // Do not merge if either frame has a mask. Continuation frames should apply
7885 // the mask independently (just like nsDisplayBackgroundImage).
7886 if (aFrame->StyleSVGReset()->HasMask()) {
7887 return false;
7890 return true;
7893 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem* aItem) const {
7894 // Items for the same content element should be merged into a single
7895 // compositing group.
7896 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
7897 !HasSameContent(aItem)) {
7898 return false;
7901 return CanMergeDisplayMaskFrame(mFrame) &&
7902 CanMergeDisplayMaskFrame(aItem->Frame());
7905 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7906 if (!ValidateSVGFrame()) {
7907 return false;
7910 return SVGUtils::DetermineMaskUsage(mFrame, false).UsingMaskOrClipPath();
7913 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder,
7914 gfxContext* aMaskContext,
7915 bool aHandleOpacity,
7916 bool* aMaskPainted) {
7917 MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
7919 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7920 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7921 PaintFramesParams params(*aMaskContext, mFrame, mBounds, borderArea, aBuilder,
7922 aHandleOpacity, imgParams);
7923 ComputeMaskGeometry(params);
7924 bool maskIsComplete = false;
7925 bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete);
7926 if (aMaskPainted) {
7927 *aMaskPainted = painted;
7930 return maskIsComplete &&
7931 (imgParams.result == ImgDrawResult::SUCCESS ||
7932 imgParams.result == ImgDrawResult::SUCCESS_NOT_COMPLETE ||
7933 imgParams.result == ImgDrawResult::WRONG_SIZE);
7936 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7937 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7938 nsRegion* aInvalidRegion) const {
7939 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
7940 aInvalidRegion);
7942 const auto* geometry =
7943 static_cast<const nsDisplayMasksAndClipPathsGeometry*>(aGeometry);
7944 bool snap;
7945 nsRect bounds = GetBounds(aBuilder, &snap);
7947 if (mDestRects.Length() != geometry->mDestRects.Length()) {
7948 aInvalidRegion->Or(bounds, geometry->mBounds);
7949 } else {
7950 for (size_t i = 0; i < mDestRects.Length(); i++) {
7951 if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) {
7952 aInvalidRegion->Or(bounds, geometry->mBounds);
7953 break;
7959 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7960 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
7961 const std::function<void()>& aPaintChildren) {
7962 // Clip the drawing target by mVisibleRect, which contains the visible
7963 // region of the target frame and its out-of-flow and inflow descendants.
7964 Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx),
7965 mFrame->PresContext()->AppUnitsPerDevPixel());
7966 bounds.RoundOut();
7967 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7968 autoSaveClip.Clip(bounds);
7970 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7971 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7972 PaintFramesParams params(*aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
7973 borderArea, aBuilder, false, imgParams);
7975 ComputeMaskGeometry(params);
7977 SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren);
7980 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder* aBuilder,
7981 gfxContext* aCtx) {
7982 if (!IsValidMask()) {
7983 return;
7985 PaintWithContentsPaintCallback(aBuilder, aCtx, [&] {
7986 GetChildren()->Paint(aBuilder, aCtx,
7987 mFrame->PresContext()->AppUnitsPerDevPixel());
7991 static Maybe<wr::WrClipChainId> CreateSimpleClipRegion(
7992 const nsDisplayMasksAndClipPaths& aDisplayItem,
7993 wr::DisplayListBuilder& aBuilder) {
7994 nsIFrame* frame = aDisplayItem.Frame();
7995 const auto* style = frame->StyleSVGReset();
7996 MOZ_ASSERT(style->HasClipPath() || style->HasMask());
7997 if (!SVGUtils::DetermineMaskUsage(frame, false).IsSimpleClipShape()) {
7998 return Nothing();
8001 const auto& clipPath = style->mClipPath;
8002 const auto& shape = *clipPath.AsShape()._0;
8004 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8005 const nsRect refBox =
8006 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8008 wr::WrClipId clipId{};
8010 switch (shape.tag) {
8011 case StyleBasicShape::Tag::Rect: {
8012 const nsRect rect =
8013 ShapeUtils::ComputeInsetRect(shape.AsRect().rect, refBox) +
8014 aDisplayItem.ToReferenceFrame();
8016 nscoord radii[8] = {0};
8017 if (ShapeUtils::ComputeRectRadii(shape.AsRect().round, refBox, rect,
8018 radii)) {
8019 clipId = aBuilder.DefineRoundedRectClip(
8020 Nothing(),
8021 wr::ToComplexClipRegion(rect, radii, appUnitsPerDevPixel));
8022 } else {
8023 clipId = aBuilder.DefineRectClip(
8024 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8025 rect, appUnitsPerDevPixel)));
8028 break;
8030 case StyleBasicShape::Tag::Ellipse:
8031 case StyleBasicShape::Tag::Circle: {
8032 nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox);
8034 nsSize radii;
8035 if (shape.IsEllipse()) {
8036 radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox);
8037 } else {
8038 nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox);
8039 radii = {radius, radius};
8042 nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center -
8043 nsPoint(radii.width, radii.height),
8044 radii * 2);
8046 nscoord ellipseRadii[8];
8047 for (const auto corner : AllPhysicalHalfCorners()) {
8048 ellipseRadii[corner] =
8049 HalfCornerIsX(corner) ? radii.width : radii.height;
8052 clipId = aBuilder.DefineRoundedRectClip(
8053 Nothing(), wr::ToComplexClipRegion(ellipseRect, ellipseRadii,
8054 appUnitsPerDevPixel));
8056 break;
8058 default:
8059 // Please don't add more exceptions, try to find a way to define the clip
8060 // without using a mask image.
8062 // And if you _really really_ need to add an exception, add it to
8063 // SVGUtils::DetermineMaskUsage
8064 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8065 return Nothing();
8068 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8070 return Some(clipChainId);
8073 static void FillPolygonDataForDisplayItem(
8074 const nsDisplayMasksAndClipPaths& aDisplayItem,
8075 nsTArray<wr::LayoutPoint>& aPoints, wr::FillRule& aFillRule) {
8076 nsIFrame* frame = aDisplayItem.Frame();
8077 const auto* style = frame->StyleSVGReset();
8078 bool isPolygon = style->HasClipPath() && style->mClipPath.IsShape() &&
8079 style->mClipPath.AsShape()._0->IsPolygon();
8080 if (!isPolygon) {
8081 return;
8084 const auto& clipPath = style->mClipPath;
8085 const auto& shape = *clipPath.AsShape()._0;
8086 const nsRect refBox =
8087 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8089 // We only fill polygon data for polygons that are below a complexity
8090 // limit.
8091 nsTArray<nsPoint> vertices =
8092 ShapeUtils::ComputePolygonVertices(shape, refBox);
8093 if (vertices.Length() > wr::POLYGON_CLIP_VERTEX_MAX) {
8094 return;
8097 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8099 for (size_t i = 0; i < vertices.Length(); ++i) {
8100 wr::LayoutPoint point = wr::ToLayoutPoint(
8101 LayoutDevicePoint::FromAppUnits(vertices[i], appUnitsPerDevPixel));
8102 aPoints.AppendElement(point);
8105 aFillRule = (shape.AsPolygon().fill == StyleFillRule::Nonzero)
8106 ? wr::FillRule::Nonzero
8107 : wr::FillRule::Evenodd;
8110 static Maybe<wr::WrClipChainId> CreateWRClipPathAndMasks(
8111 nsDisplayMasksAndClipPaths* aDisplayItem, const LayoutDeviceRect& aBounds,
8112 wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder,
8113 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8114 nsDisplayListBuilder* aDisplayListBuilder) {
8115 if (auto clip = CreateSimpleClipRegion(*aDisplayItem, aBuilder)) {
8116 return clip;
8119 Maybe<wr::ImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(
8120 aDisplayItem, aBuilder, aResources, aSc, aDisplayListBuilder, aBounds);
8121 if (!mask) {
8122 return Nothing();
8125 // We couldn't create a simple clip region, but before we create an image
8126 // mask clip, see if we can get a polygon clip to add to it.
8127 nsTArray<wr::LayoutPoint> points;
8128 wr::FillRule fillRule = wr::FillRule::Nonzero;
8129 FillPolygonDataForDisplayItem(*aDisplayItem, points, fillRule);
8131 wr::WrClipId clipId =
8132 aBuilder.DefineImageMaskClip(mask.ref(), points, fillRule);
8134 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8136 return Some(clipChainId);
8139 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8140 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8141 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8142 nsDisplayListBuilder* aDisplayListBuilder) {
8143 bool snap;
8144 auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8145 nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
8146 LayoutDeviceRect bounds =
8147 LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel);
8149 Maybe<wr::WrClipChainId> clip = CreateWRClipPathAndMasks(
8150 this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder);
8152 float oldOpacity = aBuilder.GetInheritedOpacity();
8154 Maybe<StackingContextHelper> layer;
8155 const StackingContextHelper* sc = &aSc;
8156 if (clip) {
8157 // Create a new stacking context to attach the mask to, ensuring the mask is
8158 // applied to the aggregate, and not the individual elements.
8160 // The stacking context shouldn't have any offset.
8161 bounds.MoveTo(0, 0);
8163 Maybe<float> opacity =
8164 (SVGUtils::DetermineMaskUsage(mFrame, false).IsSimpleClipShape() &&
8165 aBuilder.GetInheritedOpacity() != 1.0f)
8166 ? Some(aBuilder.GetInheritedOpacity())
8167 : Nothing();
8169 wr::StackingContextParams params;
8170 params.clip = wr::WrStackingContextClip::ClipChain(clip->id);
8171 params.opacity = opacity.ptrOr(nullptr);
8172 if (mWrapsBackdropFilter) {
8173 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8175 layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params,
8176 bounds);
8177 sc = layer.ptr();
8180 aBuilder.SetInheritedOpacity(1.0f);
8181 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8182 aBuilder.SetInheritedClipChain(nullptr);
8183 CreateWebRenderCommandsNewClipListOption(aBuilder, aResources, *sc, aManager,
8184 aDisplayListBuilder, layer.isSome());
8185 aBuilder.SetInheritedOpacity(oldOpacity);
8186 aBuilder.SetInheritedClipChain(oldClipChain);
8188 return true;
8191 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8192 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
8193 if (const DisplayItemClip* clip =
8194 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
8195 return Some(clip->GetClipRect());
8197 // This item does not have a clip with respect to |aASR|. However, we
8198 // might still have finite bounds with respect to |aASR|. Check our
8199 // children.
8200 nsDisplayList* childList = GetSameCoordinateSystemChildren();
8201 if (childList) {
8202 return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR));
8204 #ifdef DEBUG
8205 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
8206 #endif
8207 return Nothing();
8210 #ifdef MOZ_DUMP_PAINTING
8211 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) {
8212 nsIFrame* firstFrame =
8213 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8214 bool first = true;
8215 aTo += " effects=(";
8216 SVGClipPathFrame* clipPathFrame;
8217 // XXX Check return value?
8218 SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
8219 if (clipPathFrame) {
8220 if (!first) {
8221 aTo += ", ";
8223 aTo += nsPrintfCString(
8224 "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
8225 first = false;
8226 } else if (mFrame->StyleSVGReset()->HasClipPath()) {
8227 if (!first) {
8228 aTo += ", ";
8230 aTo += "clip(basic-shape)";
8231 first = false;
8234 nsTArray<SVGMaskFrame*> masks;
8235 // XXX check return value?
8236 SVGObserverUtils::GetAndObserveMasks(firstFrame, &masks);
8237 if (!masks.IsEmpty() && masks[0]) {
8238 if (!first) {
8239 aTo += ", ";
8241 aTo += "mask";
8243 aTo += ")";
8245 #endif
8247 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8248 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8249 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8250 nsDisplayListBuilder* aDisplayListBuilder) {
8251 WrFiltersHolder wrFilters;
8252 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8253 auto filterChain = style.StyleEffects()->mBackdropFilters.AsSpan();
8254 bool initialized = true;
8255 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8256 wrFilters) &&
8257 !SVGIntegrationUtils::BuildWebRenderFilters(
8258 mFrame, filterChain, StyleFilterType::BackdropFilter, wrFilters,
8259 initialized)) {
8260 // TODO: If painting backdrop-filters on the content side is implemented,
8261 // consider returning false to fall back to that.
8262 wrFilters = {};
8265 if (!initialized) {
8266 wrFilters = {};
8269 nsCSSRendering::ImageLayerClipState clip;
8270 nsCSSRendering::GetImageLayerClip(
8271 style.StyleBackground()->BottomLayer(), mFrame, *style.StyleBorder(),
8272 mBackdropRect, mBackdropRect, false,
8273 mFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
8275 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
8276 mBackdropRect, mFrame->PresContext()->AppUnitsPerDevPixel());
8278 wr::ComplexClipRegion region =
8279 wr::ToComplexClipRegion(clip.mBGClipArea, clip.mRadii,
8280 mFrame->PresContext()->AppUnitsPerDevPixel());
8282 aBuilder.PushBackdropFilter(wr::ToLayoutRect(bounds), region,
8283 wrFilters.filters, wrFilters.filter_datas,
8284 !BackfaceIsHidden());
8286 wr::StackingContextParams params;
8287 params.clip =
8288 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8289 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8290 params);
8292 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8293 aDisplayListBuilder);
8294 return true;
8297 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder* aBuilder,
8298 gfxContext* aCtx) {
8299 // TODO: Implement backdrop filters
8300 GetChildren()->Paint(aBuilder, aCtx,
8301 mFrame->PresContext()->AppUnitsPerDevPixel());
8304 nsRect nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder* aBuilder,
8305 bool* aSnap) const {
8306 nsRect childBounds = nsDisplayWrapList::GetBounds(aBuilder, aSnap);
8308 *aSnap = false;
8310 return mBackdropRect.Union(childBounds);
8313 /* static */
8314 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder,
8315 nsIFrame* aFrame, nsDisplayList* aList,
8316 nsIFrame* aStyleFrame,
8317 bool aWrapsBackdropFilter)
8318 : nsDisplayEffectsBase(aBuilder, aFrame, aList),
8319 mStyle(aFrame == aStyleFrame ? nullptr : aStyleFrame->Style()),
8320 mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()),
8321 mWrapsBackdropFilter(aWrapsBackdropFilter) {
8322 MOZ_COUNT_CTOR(nsDisplayFilters);
8323 mVisibleRect = aBuilder->GetVisibleRect() +
8324 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
8327 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8328 PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) {
8329 GetChildren()->Paint(aBuilder, aContext,
8330 mFrame->PresContext()->AppUnitsPerDevPixel());
8334 void nsDisplayFilters::PaintWithContentsPaintCallback(
8335 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
8336 const std::function<void(gfxContext* aContext)>& aPaintChildren) {
8337 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
8338 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8339 PaintFramesParams params(*aCtx, mFrame, mVisibleRect, borderArea, aBuilder,
8340 false, imgParams);
8342 gfxPoint userSpaceToFrameSpaceOffset =
8343 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params);
8345 auto filterChain = mStyle ? mStyle->StyleEffects()->mFilters.AsSpan()
8346 : mFrame->StyleEffects()->mFilters.AsSpan();
8347 SVGIntegrationUtils::PaintFilter(
8348 params, filterChain,
8349 [&](gfxContext& aContext, imgDrawingParams&, const gfxMatrix*,
8350 const nsIntRect*) {
8351 gfxContextMatrixAutoSaveRestore autoSR(&aContext);
8352 aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(
8353 -userSpaceToFrameSpaceOffset));
8354 aPaintChildren(&aContext);
8358 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8359 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame);
8362 bool nsDisplayFilters::CreateWebRenderCommands(
8363 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8364 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8365 nsDisplayListBuilder* aDisplayListBuilder) {
8366 WrFiltersHolder wrFilters;
8367 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8368 auto filterChain = style.StyleEffects()->mFilters.AsSpan();
8369 bool initialized = true;
8370 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8371 wrFilters) &&
8372 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame, filterChain,
8373 StyleFilterType::Filter,
8374 wrFilters, initialized)) {
8375 if (mStyle) {
8376 // TODO(bug 1769223): Support fallback filters in the root code-path,
8377 // perhaps. For now treat it the same way as invalid filters.
8378 wrFilters = {};
8379 } else {
8380 // Draw using fallback.
8381 return false;
8385 if (!initialized) {
8386 // https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
8388 // If the filter references a non-existent object or the referenced object
8389 // is not a filter element, then the whole filter chain is ignored. No
8390 // filter is applied to the object.
8392 // Note that other engines have a weird discrepancy between SVG and HTML
8393 // content here, but the spec is clear.
8394 wrFilters = {};
8397 uint64_t clipChainId;
8398 if (wrFilters.post_filters_clip) {
8399 auto devPxRect = LayoutDeviceRect::FromAppUnits(
8400 wrFilters.post_filters_clip.value() + ToReferenceFrame(),
8401 mFrame->PresContext()->AppUnitsPerDevPixel());
8402 auto clipId =
8403 aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect));
8404 clipChainId = aBuilder.DefineClipChain({clipId}, true).id;
8405 } else {
8406 clipChainId = aBuilder.CurrentClipChainId();
8408 wr::WrStackingContextClip clip =
8409 wr::WrStackingContextClip::ClipChain(clipChainId);
8411 float opacity = aBuilder.GetInheritedOpacity();
8412 aBuilder.SetInheritedOpacity(1.0f);
8413 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8414 aBuilder.SetInheritedClipChain(nullptr);
8415 wr::StackingContextParams params;
8416 params.mFilters = std::move(wrFilters.filters);
8417 params.mFilterDatas = std::move(wrFilters.filter_datas);
8418 params.opacity = opacity != 1.0f ? &opacity : nullptr;
8419 params.clip = clip;
8420 if (mWrapsBackdropFilter) {
8421 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8423 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8424 params);
8426 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
8427 aManager, aDisplayListBuilder);
8428 aBuilder.SetInheritedOpacity(opacity);
8429 aBuilder.SetInheritedClipChain(oldClipChain);
8431 return true;
8434 #ifdef MOZ_DUMP_PAINTING
8435 void nsDisplayFilters::PrintEffects(nsACString& aTo) {
8436 nsIFrame* firstFrame =
8437 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8438 bool first = true;
8439 aTo += " effects=(";
8440 // We may exist for a mix of CSS filter functions and/or references to SVG
8441 // filters. If we have invalid references to SVG filters then we paint
8442 // nothing, but otherwise we will apply one or more filters.
8443 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) !=
8444 SVGObserverUtils::eHasRefsSomeInvalid) {
8445 if (!first) {
8446 aTo += ", ";
8448 aTo += "filter";
8450 aTo += ")";
8452 #endif
8454 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder,
8455 nsIFrame* aFrame, nsDisplayList* aList)
8456 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8457 MOZ_COUNT_CTOR(nsDisplaySVGWrapper);
8460 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8461 return !aBuilder->GetWidgetLayerManager();
8464 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8465 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8466 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8467 nsDisplayListBuilder* aDisplayListBuilder) {
8468 return CreateWebRenderCommandsNewClipListOption(
8469 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8472 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder* aBuilder,
8473 nsIFrame* aFrame,
8474 nsDisplayList* aList)
8475 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8476 MOZ_COUNT_CTOR(nsDisplayForeignObject);
8479 #ifdef NS_BUILD_REFCNT_LOGGING
8480 nsDisplayForeignObject::~nsDisplayForeignObject() {
8481 MOZ_COUNT_DTOR(nsDisplayForeignObject);
8483 #endif
8485 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8486 return !aBuilder->GetWidgetLayerManager();
8489 bool nsDisplayForeignObject::CreateWebRenderCommands(
8490 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8491 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8492 nsDisplayListBuilder* aDisplayListBuilder) {
8493 AutoRestore<bool> restoreDoGrouping(aManager->CommandBuilder().mDoGrouping);
8494 aManager->CommandBuilder().mDoGrouping = false;
8495 return CreateWebRenderCommandsNewClipListOption(
8496 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8499 void nsDisplayLink::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8500 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8501 aCtx->GetDrawTarget()->Link(
8502 mLinkSpec.get(), NSRectToRect(GetPaintRect(aBuilder, aCtx), appPerDev));
8505 void nsDisplayDestination::Paint(nsDisplayListBuilder* aBuilder,
8506 gfxContext* aCtx) {
8507 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8508 aCtx->GetDrawTarget()->Destination(
8509 mDestinationName.get(),
8510 NSPointToPoint(GetPaintRect(aBuilder, aCtx).TopLeft(), appPerDev));
8513 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8514 nsDisplayList* aOutResultList, nsIContent* aContent) {
8515 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
8516 // in content document order and SortByZOrder is a stable sort which
8517 // guarantees that boxes produced by the same element are placed together
8518 // in the sort. Consider a position:relative inline element that breaks
8519 // across lines and has absolutely positioned children; all the abs-pos
8520 // children should be z-ordered after all the boxes for the position:relative
8521 // element itself.
8522 PositionedDescendants()->SortByZOrder();
8524 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8525 // 1,2: backgrounds and borders
8526 aOutResultList->AppendToTop(BorderBackground());
8527 // 3: negative z-index children.
8528 for (auto* item : PositionedDescendants()->TakeItems()) {
8529 if (item->ZIndex() < 0) {
8530 aOutResultList->AppendToTop(item);
8531 } else {
8532 PositionedDescendants()->AppendToTop(item);
8536 // 4: block backgrounds
8537 aOutResultList->AppendToTop(BlockBorderBackgrounds());
8538 // 5: floats
8539 aOutResultList->AppendToTop(Floats());
8540 // 7: general content
8541 aOutResultList->AppendToTop(Content());
8542 // 7.5: outlines, in content tree order. We need to sort by content order
8543 // because an element with outline that breaks and has children with outline
8544 // might have placed child outline items between its own outline items.
8545 // The element's outline items need to all come before any child outline
8546 // items.
8547 if (aContent) {
8548 Outlines()->SortByContentOrder(aContent);
8550 aOutResultList->AppendToTop(Outlines());
8551 // 8, 9: non-negative z-index children
8552 aOutResultList->AppendToTop(PositionedDescendants());
8555 uint32_t PaintTelemetry::sPaintLevel = 0;
8557 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8558 // Don't record nested paints.
8559 if (sPaintLevel++ > 0) {
8560 return;
8563 mStart = TimeStamp::Now();
8566 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8567 MOZ_ASSERT(sPaintLevel != 0);
8568 if (--sPaintLevel > 0) {
8569 return;
8572 // If we're in multi-process mode, don't include paint times for the parent
8573 // process.
8574 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8575 return;
8578 // Record the total time.
8579 mozilla::glean::gfx_content::paint_time.AccumulateRawDuration(
8580 TimeStamp::Now() - mStart);
8583 static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
8584 if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
8585 return aFrame;
8588 if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
8589 !aFrame->GetPrevInFlow()) {
8590 return aFrame->GetPlaceholderFrame();
8593 return aFrame;
8596 static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
8597 nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
8598 MOZ_ASSERT(f);
8599 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
8602 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8603 nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
8604 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
8605 const bool aIsTransformed)
8606 : mBuilder(aBuilder),
8607 mPrevFrame(aBuilder->mCurrentFrame),
8608 mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
8609 mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
8610 mPrevAdditionalOffset(aBuilder->mAdditionalOffset),
8611 mPrevVisibleRect(aBuilder->mVisibleRect),
8612 mPrevDirtyRect(aBuilder->mDirtyRect),
8613 mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo),
8614 mPrevAncestorHasApzAwareEventHandler(
8615 aBuilder->mAncestorHasApzAwareEventHandler),
8616 mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
8617 mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
8618 if (aIsTransformed) {
8619 aBuilder->mCurrentOffsetToReferenceFrame =
8620 aBuilder->AdditionalOffset().refOr(nsPoint());
8621 aBuilder->mCurrentReferenceFrame = aForChild;
8622 } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
8623 aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
8624 } else {
8625 aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
8626 aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
8629 // If aForChild is being visited from a frame other than it's ancestor frame,
8630 // mInInvalidSubtree will need to be recalculated the slow way.
8631 if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) {
8632 aBuilder->mInInvalidSubtree =
8633 aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
8634 } else {
8635 aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
8638 aBuilder->mCurrentFrame = aForChild;
8639 aBuilder->mVisibleRect = aVisibleRect;
8640 aBuilder->mDirtyRect =
8641 aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
8644 } // namespace mozilla