Bug 1890793: Assert CallArgs::newTarget is not gray. r=spidermonkey-reviewers,sfink...
[gecko.git] / layout / painting / nsDisplayList.cpp
blobe96a9daf88e3ce1aa82e63030274403ca967c0ac
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 // FIXME(emilio): This whole business should ideally not be needed at all, but
387 // there are a variety of hard-to-deal-with caret invalidation issues, like
388 // bug 1888583, and caret changes are relatively uncommon, enough that it
389 // probably isn't worth chasing all them down.
390 void nsDisplayListBuilder::InvalidateCaretFramesIfNeeded() {
391 if (mPaintedCarets.IsEmpty()) {
392 return;
394 size_t i = mPaintedCarets.Length();
395 while (i--) {
396 nsCaret* caret = mPaintedCarets[i];
397 nsIFrame* oldCaret = caret->GetLastPaintedFrame();
398 nsRect caretRect;
399 nsIFrame* currentCaret = caret->GetPaintGeometry(&caretRect);
400 if (oldCaret == currentCaret) {
401 // Keep tracking this caret, it hasn't changed.
402 continue;
404 if (oldCaret) {
405 oldCaret->MarkNeedsDisplayItemRebuild();
407 if (currentCaret) {
408 currentCaret->MarkNeedsDisplayItemRebuild();
410 // If / when we paint this caret, we'll track it again.
411 caret->SetLastPaintedFrame(nullptr);
412 mPaintedCarets.RemoveElementAt(i);
416 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
417 SetCurrentActiveScrolledRoot(
418 const ActiveScrolledRoot* aActiveScrolledRoot) {
419 MOZ_ASSERT(!mUsed);
421 // Set the builder's mCurrentActiveScrolledRoot.
422 mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;
424 // We also need to adjust the builder's mCurrentContainerASR.
425 // mCurrentContainerASR needs to be an ASR that all the container's
426 // contents have finite bounds with respect to. If aActiveScrolledRoot
427 // is an ancestor ASR of mCurrentContainerASR, that means we need to
428 // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
429 // the items that will be created with aActiveScrolledRoot wouldn't
430 // have finite bounds with respect to mCurrentContainerASR. There's one
431 // exception, in the case where there's a content clip on the builder
432 // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
433 // content clip will clip all items that are created while this
434 // AutoCurrentActiveScrolledRootSetter exists. This means that the items
435 // created during our lifetime will have finite bounds with respect to
436 // the content clip's ASR, even if the items' actual ASR is an ancestor
437 // of that. And it also means that mCurrentContainerASR only needs to be
438 // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
439 // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
440 // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
442 // finiteBoundsASR is the leafmost ASR that all items created during
443 // object's lifetime have finite bounds with respect to.
444 const ActiveScrolledRoot* finiteBoundsASR =
445 ActiveScrolledRoot::PickDescendant(mContentClipASR, aActiveScrolledRoot);
447 // mCurrentContainerASR is adjusted so that it's still an ancestor of
448 // finiteBoundsASR.
449 mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
450 mBuilder->mCurrentContainerASR, finiteBoundsASR);
452 // If we are entering out-of-flow content inside a CSS filter, mark
453 // scroll frames wrt. which the content is fixed as containing such content.
454 if (mBuilder->mFilterASR && ActiveScrolledRoot::IsAncestor(
455 aActiveScrolledRoot, mBuilder->mFilterASR)) {
456 for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
457 asr && asr != aActiveScrolledRoot; asr = asr->mParent) {
458 asr->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
462 mUsed = true;
465 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
466 InsertScrollFrame(nsIScrollableFrame* aScrollableFrame) {
467 MOZ_ASSERT(!mUsed);
468 size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
469 const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot;
470 const ActiveScrolledRoot* asr =
471 mBuilder->AllocateActiveScrolledRoot(parentASR, aScrollableFrame);
472 mBuilder->mCurrentActiveScrolledRoot = asr;
474 // All child ASRs of parentASR that were created while this
475 // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
476 // now. Reparent them to asr.
477 for (size_t i = mDescendantsStartIndex; i < descendantsEndIndex; i++) {
478 ActiveScrolledRoot* descendantASR = mBuilder->mActiveScrolledRoots[i];
479 if (ActiveScrolledRoot::IsAncestor(parentASR, descendantASR)) {
480 descendantASR->IncrementDepth();
481 if (descendantASR->mParent == parentASR) {
482 descendantASR->mParent = asr;
487 mUsed = true;
490 nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
491 nsDisplayListBuilder* aBuilder)
492 : mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) {
493 mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot;
496 nsPresContext* nsDisplayListBuilder::CurrentPresContext() {
497 return CurrentPresShellState()->mPresShell->GetPresContext();
500 /* static */
501 nsRect nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
502 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
503 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
504 nsRect* aOutDirtyRect) {
505 nsRect visible = aVisibleRect;
506 nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
508 bool inPartialUpdate =
509 aBuilder->IsRetainingDisplayList() && aBuilder->IsPartialUpdate();
510 if (StaticPrefs::apz_allow_zooming() &&
511 DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame) &&
512 aBuilder->IsPaintingToWindow() && !inPartialUpdate) {
513 dirtyRectRelativeToDirtyFrame =
514 nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize());
516 // If there's a visual viewport size set, restrict the amount of the
517 // fixed-position element we paint to the visual viewport. (In general
518 // the fixed-position element can be as large as the layout viewport,
519 // which at a high zoom level can cause us to paint too large of an
520 // area.)
521 PresShell* presShell = aFrame->PresShell();
522 if (presShell->IsVisualViewportSizeSet()) {
523 dirtyRectRelativeToDirtyFrame =
524 nsRect(presShell->GetVisualViewportOffsetRelativeToLayoutViewport(),
525 presShell->GetVisualViewportSize());
526 // But if we have a displayport, expand it to the displayport, so
527 // that async-scrolling the visual viewport within the layout viewport
528 // will not checkerboard.
529 if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
530 nsRect displayport;
531 // Note that the displayport here is already in the right coordinate
532 // space: it's relative to the scroll port (= layout viewport), but
533 // covers the visual viewport with some margins around it, which is
534 // exactly what we want.
535 if (DisplayPortUtils::GetDisplayPort(
536 rootScrollFrame->GetContent(), &displayport,
537 DisplayPortOptions().With(ContentGeometryType::Fixed))) {
538 dirtyRectRelativeToDirtyFrame = displayport;
542 visible = dirtyRectRelativeToDirtyFrame;
543 if (StaticPrefs::apz_test_logging_enabled() &&
544 presShell->GetDocument()->IsContentDocument()) {
545 nsLayoutUtils::LogAdditionalTestData(
546 aBuilder, "fixedPosDisplayport",
547 ToString(CSSSize::FromAppUnits(visible)));
551 *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
552 visible -= aFrame->GetPosition();
554 nsRect overflowRect = aFrame->InkOverflowRect();
556 if (aFrame->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
557 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
559 * Add a fuzz factor to the overflow rectangle so that elements only
560 * just out of view are pulled into the display list, so they can be
561 * prerendered if necessary.
563 overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
566 visible.IntersectRect(visible, overflowRect);
567 aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect);
569 return visible;
572 nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder* aBuilder,
573 nsIFrame* aFrame,
574 nsDisplayList* aList)
575 : mList(aList) {
576 // Find the element that we need to check for link-ness, bailing out if
577 // we can't find one.
578 Element* elem = Element::FromNodeOrNull(aFrame->GetContent());
579 if (!elem) {
580 return;
583 // If the element has an id and/or name attribute, generate a destination
584 // for possible internal linking.
585 auto maybeGenerateDest = [&](const nsAtom* aAttr) {
586 nsAutoString attrValue;
587 elem->GetAttr(aAttr, attrValue);
588 if (!attrValue.IsEmpty()) {
589 NS_ConvertUTF16toUTF8 dest(attrValue);
590 // Ensure that we only emit a given destination once, although there may
591 // be multiple frames associated with a given element; we'll simply use
592 // the first of them as the target of any links to it.
593 // XXX(jfkthame) This prevents emitting duplicate destinations *on the
594 // same page*, but does not prevent duplicates on subsequent pages, as
595 // each new page is handled by a new temporary DisplayListBuilder. This
596 // seems to be harmless in practice, though a bit wasteful of space. To
597 // fix, we need to maintain the set of already-seen destinations globally
598 // for the print job, rather than attached to the (per-page) builder.
599 if (aBuilder->mDestinations.EnsureInserted(dest)) {
600 auto* destination = MakeDisplayItem<nsDisplayDestination>(
601 aBuilder, aFrame, dest.get(), aFrame->GetRect().TopLeft());
602 mList->AppendToTop(destination);
607 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
608 if (elem->HasID()) {
609 maybeGenerateDest(nsGkAtoms::id);
611 if (elem->HasName()) {
612 maybeGenerateDest(nsGkAtoms::name);
616 // Links don't nest, so if the builder already has a destination, no need to
617 // check for a link element here.
618 if (!aBuilder->mLinkSpec.IsEmpty()) {
619 return;
622 // Check if we have actually found a link.
623 if (!elem->IsLink()) {
624 return;
627 nsCOMPtr<nsIURI> uri = elem->GetHrefURI();
628 if (!uri) {
629 return;
632 // Is it a local (in-page) destination?
633 bool hasRef, eqExRef;
634 nsIURI* docURI;
635 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
636 NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef &&
637 (docURI = aFrame->PresContext()->Document()->GetDocumentURI()) &&
638 NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &eqExRef)) && eqExRef) {
639 if (NS_FAILED(uri->GetRef(aBuilder->mLinkSpec)) ||
640 aBuilder->mLinkSpec.IsEmpty()) {
641 return;
643 // The destination name is simply a string; we don't want URL-escaping
644 // applied to it.
645 NS_UnescapeURL(aBuilder->mLinkSpec);
646 // Mark the link spec as being an internal destination
647 aBuilder->mLinkSpec.Insert('#', 0);
648 } else {
649 if (NS_FAILED(uri->GetSpec(aBuilder->mLinkSpec)) ||
650 aBuilder->mLinkSpec.IsEmpty()) {
651 return;
655 // Record that we need to reset the builder's state on destruction.
656 mBuilderToReset = aBuilder;
659 void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
660 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
661 // Note that we may generate a link here even if the constructor bailed out
662 // without updating aBuilder->LinkSpec(), because it may have been set by
663 // an ancestor that was associated with a link element.
664 if (!aBuilder->mLinkSpec.IsEmpty()) {
665 auto* link = MakeDisplayItem<nsDisplayLink>(
666 aBuilder, aFrame, aBuilder->mLinkSpec.get(), aFrame->GetRect());
667 mList->AppendToTop(link);
671 uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1);
673 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
674 nsDisplayListBuilderMode aMode,
675 bool aBuildCaret,
676 bool aRetainingDisplayList)
677 : mReferenceFrame(aReferenceFrame),
678 mIgnoreScrollFrame(nullptr),
679 mCurrentActiveScrolledRoot(nullptr),
680 mCurrentContainerASR(nullptr),
681 mCurrentFrame(aReferenceFrame),
682 mCurrentReferenceFrame(aReferenceFrame),
683 mScrollInfoItemsForHoisting(nullptr),
684 mFirstClipChainToDestroy(nullptr),
685 mTableBackgroundSet(nullptr),
686 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
687 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
688 mFilterASR(nullptr),
689 mDirtyRect(-1, -1, -1, -1),
690 mBuildingExtraPagesForPageNum(0),
691 mMode(aMode),
692 mContainsBlendMode(false),
693 mIsBuildingScrollbar(false),
694 mCurrentScrollbarWillHaveLayer(false),
695 mBuildCaret(aBuildCaret),
696 mRetainingDisplayList(aRetainingDisplayList),
697 mPartialUpdate(false),
698 mIgnoreSuppression(false),
699 mIncludeAllOutOfFlows(false),
700 mDescendIntoSubdocuments(true),
701 mSelectedFramesOnly(false),
702 mAllowMergingAndFlattening(true),
703 mInTransform(false),
704 mInEventsOnly(false),
705 mInFilter(false),
706 mInPageSequence(false),
707 mIsInChromePresContext(false),
708 mSyncDecodeImages(false),
709 mIsPaintingToWindow(false),
710 mUseHighQualityScaling(false),
711 mIsPaintingForWebRender(false),
712 mIsCompositingCheap(false),
713 mAncestorHasApzAwareEventHandler(false),
714 mHaveScrollableDisplayPort(false),
715 mWindowDraggingAllowed(false),
716 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
717 mForceLayerForScrollParent(false),
718 mContainsNonMinimalDisplayPort(false),
719 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
720 mBuildingInvisibleItems(false),
721 mIsBuilding(false),
722 mInInvalidSubtree(false),
723 mDisablePartialUpdates(false),
724 mPartialBuildFailed(false),
725 mIsInActiveDocShell(false),
726 mBuildAsyncZoomContainer(false),
727 mIsRelativeToLayoutViewport(false),
728 mUseOverlayScrollbars(false),
729 mAlwaysLayerizeScrollbars(false) {
730 MOZ_COUNT_CTOR(nsDisplayListBuilder);
732 mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting();
734 ShouldRebuildDisplayListDueToPrefChange();
736 mUseOverlayScrollbars =
737 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
739 mAlwaysLayerizeScrollbars =
740 StaticPrefs::layout_scrollbars_always_layerize_track();
742 static_assert(
743 static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
744 "Check TYPE_MAX should not overflow");
746 mIsReusingStackingContextItems =
747 mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
750 void nsDisplayListBuilder::BeginFrame() {
751 nsCSSRendering::BeginFrameTreesLocked();
753 mIsPaintingToWindow = false;
754 mUseHighQualityScaling = false;
755 mIgnoreSuppression = false;
756 mInTransform = false;
757 mInFilter = false;
758 mSyncDecodeImages = false;
761 void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser* aBrowser,
762 const dom::EffectsInfo& aUpdate) {
763 dom::EffectsInfo update = aUpdate;
764 // For printing we create one display item for each page that an iframe
765 // appears on, the proper visible rect is the union of all the visible rects
766 // we get from each display item.
767 nsPresContext* pc =
768 mReferenceFrame ? mReferenceFrame->PresContext() : nullptr;
769 if (pc && pc->Type() != nsPresContext::eContext_Galley) {
770 Maybe<dom::EffectsInfo> existing = mEffectsUpdates.MaybeGet(aBrowser);
771 if (existing) {
772 // Only the visible rect should differ, the scales should match.
773 MOZ_ASSERT(existing->mRasterScale == aUpdate.mRasterScale &&
774 existing->mTransformToAncestorScale ==
775 aUpdate.mTransformToAncestorScale);
776 if (existing->mVisibleRect) {
777 if (update.mVisibleRect) {
778 update.mVisibleRect =
779 Some(update.mVisibleRect->Union(*existing->mVisibleRect));
780 } else {
781 update.mVisibleRect = existing->mVisibleRect;
786 mEffectsUpdates.InsertOrUpdate(aBrowser, update);
789 void nsDisplayListBuilder::EndFrame() {
790 NS_ASSERTION(!mInInvalidSubtree,
791 "Someone forgot to cleanup mInInvalidSubtree!");
792 mCurrentContainerASR = nullptr;
793 mActiveScrolledRoots.Clear();
794 mEffectsUpdates.Clear();
795 FreeClipChains();
796 FreeTemporaryItems();
797 nsCSSRendering::EndFrameTreesLocked();
800 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
801 const nsIFrame* aStopAtFrame) {
802 mFramesMarkedForDisplay.AppendElement(aFrame);
803 for (nsIFrame* f = aFrame; f;
804 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
805 if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
806 return;
808 f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
809 if (f == aStopAtFrame) {
810 // we've reached a frame that we know will be painted, so we can stop.
811 break;
816 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) {
817 mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
820 static void MarkFrameForDisplayIfVisibleInternal(nsIFrame* aFrame,
821 const nsIFrame* aStopAtFrame) {
822 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
823 if (f->ForceDescendIntoIfVisible()) {
824 return;
826 f->SetForceDescendIntoIfVisible(true);
828 // This condition must match the condition in
829 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
830 // nsLayoutUtils::GetDisplayListParent
831 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
832 nsIFrame* parent = f->GetParent();
833 if (parent && !parent->ForceDescendIntoIfVisible()) {
834 // If the GetDisplayListParent call is going to walk to a placeholder,
835 // in rare cases the placeholder might be contained in a different
836 // continuation from the oof. So we have to make sure to mark the oofs
837 // parent. In the common case this doesn't make us do any extra work,
838 // just changes the order in which we visit the frames since walking
839 // through placeholders will walk through the parent, and we stop when
840 // we find a ForceDescendIntoIfVisible bit set.
841 MarkFrameForDisplayIfVisibleInternal(parent, aStopAtFrame);
845 if (f == aStopAtFrame) {
846 // we've reached a frame that we know will be painted, so we can stop.
847 break;
852 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
853 nsIFrame* aFrame, const nsIFrame* aStopAtFrame) {
854 AddFrameMarkedForDisplayIfVisible(aFrame);
856 MarkFrameForDisplayIfVisibleInternal(aFrame, aStopAtFrame);
859 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
860 mIsRelativeToLayoutViewport = true;
861 UpdateShouldBuildAsyncZoomContainer();
864 void nsDisplayListBuilder::ForceLayerForScrollParent() {
865 mForceLayerForScrollParent = true;
866 mNumActiveScrollframesEncountered++;
869 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
870 const Document* document = mReferenceFrame->PresContext()->Document();
871 mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
872 !document->Fullscreen() &&
873 nsLayoutUtils::AllowZoomingForDocument(document);
876 // Certain prefs may cause display list items to be added or removed when they
877 // are toggled. In those cases, we need to fully rebuild the display list.
878 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
879 // If we transition between wrapping the RCD-RSF contents into an async
880 // zoom container vs. not, we need to rebuild the display list. This only
881 // happens when the zooming or container scrolling prefs are toggled
882 // (manually by the user, or during test setup).
883 bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer;
884 UpdateShouldBuildAsyncZoomContainer();
886 bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars;
887 mUseOverlayScrollbars =
888 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
890 bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars;
891 mAlwaysLayerizeScrollbars =
892 StaticPrefs::layout_scrollbars_always_layerize_track();
894 if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) {
895 return true;
898 if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) {
899 return true;
902 if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) {
903 return true;
906 return false;
909 void nsDisplayListBuilder::AddScrollFrameToNotify(
910 nsIScrollableFrame* aScrollFrame) {
911 mScrollFramesToNotify.insert(aScrollFrame);
914 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
915 for (const auto& it : mScrollFramesToNotify) {
916 it->NotifyApzTransaction();
918 mScrollFramesToNotify.clear();
921 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
922 nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect,
923 const nsRect& aDirtyRect) {
924 MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
925 nsRect dirty;
926 nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
927 this, aFrame, aVisibleRect, aDirtyRect, &dirty);
928 if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
929 visible.IsEmpty()) {
930 return false;
933 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
934 // frame, then it will also mark any outer frames to ensure that building
935 // reaches the dirty feame.
936 if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
937 MarkFrameForDisplay(aFrame, aDirtyFrame);
940 return true;
943 static void UnmarkFrameForDisplay(nsIFrame* aFrame,
944 const nsIFrame* aStopAtFrame) {
945 for (nsIFrame* f = aFrame; f;
946 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
947 if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
948 return;
950 f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
951 if (f == aStopAtFrame) {
952 // we've reached a frame that we know will be painted, so we can stop.
953 break;
958 static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
959 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
960 if (!f->ForceDescendIntoIfVisible()) {
961 return;
963 f->SetForceDescendIntoIfVisible(false);
965 // This condition must match the condition in
966 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by
967 // nsLayoutUtils::GetDisplayListParent
968 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
969 nsIFrame* parent = f->GetParent();
970 if (parent && parent->ForceDescendIntoIfVisible()) {
971 // If the GetDisplayListParent call is going to walk to a placeholder,
972 // in rare cases the placeholder might be contained in a different
973 // continuation from the oof. So we have to make sure to mark the oofs
974 // parent. In the common case this doesn't make us do any extra work,
975 // just changes the order in which we visit the frames since walking
976 // through placeholders will walk through the parent, and we stop when
977 // we find a ForceDescendIntoIfVisible bit set.
978 UnmarkFrameForDisplayIfVisible(f);
984 nsDisplayListBuilder::~nsDisplayListBuilder() {
985 NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
986 "All frames should have been unmarked");
987 NS_ASSERTION(mFramesWithOOFData.Length() == 0,
988 "All OOF data should have been removed");
989 NS_ASSERTION(mPresShellStates.Length() == 0,
990 "All presshells should have been exited");
992 DisplayItemClipChain* c = mFirstClipChainToDestroy;
993 while (c) {
994 DisplayItemClipChain* next = c->mNextClipChainToDestroy;
995 c->DisplayItemClipChain::~DisplayItemClipChain();
996 c = next;
999 MOZ_COUNT_DTOR(nsDisplayListBuilder);
1002 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
1003 uint32_t flags = 0;
1004 if (mSyncDecodeImages) {
1005 flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
1007 if (mIsPaintingToWindow) {
1008 flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
1010 if (mUseHighQualityScaling) {
1011 flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING;
1013 return flags;
1016 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
1017 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
1018 uint32_t flags = 0;
1019 if (mSyncDecodeImages) {
1020 flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
1022 if (mIsPaintingToWindow) {
1023 flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
1025 if (mUseHighQualityScaling) {
1026 flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
1028 return flags;
1031 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1032 uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
1033 if (mSyncDecodeImages) {
1034 flags |= imgIContainer::FLAG_SYNC_DECODE;
1035 } else {
1036 flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
1038 if (mIsPaintingToWindow || mUseHighQualityScaling) {
1039 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1041 return flags;
1044 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
1045 const nsRegion& aRegion) {
1046 if (aRegion.IsEmpty()) {
1047 return;
1050 nsRegion tmp;
1051 tmp.Sub(*aVisibleRegion, aRegion);
1052 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1053 // to its bounds either, which can be very bad (see bug 516740).
1054 // Do let aVisibleRegion get more complex if by doing so we reduce its
1055 // area by at least half.
1056 if (tmp.GetNumRects() <= 15 || tmp.Area() <= aVisibleRegion->Area() / 2) {
1057 *aVisibleRegion = tmp;
1061 nsCaret* nsDisplayListBuilder::GetCaret() {
1062 RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
1063 return caret;
1066 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) {
1067 if (mIsPaintingToWindow) {
1068 aPresShell->IncrementPaintCount();
1072 void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
1073 bool aPointerEventsNoneDoc) {
1074 PresShellState* state = mPresShellStates.AppendElement();
1075 state->mPresShell = aReferenceFrame->PresShell();
1076 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
1077 state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
1079 nsIScrollableFrame* sf = state->mPresShell->GetRootScrollFrameAsScrollable();
1080 if (sf && IsInSubdocument()) {
1081 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1082 // that the canvas background color will be set correctly, and that only one
1083 // unscrollable item will be created.
1084 // This is done to avoid, for example, a case where only scrollbar frames
1085 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1086 // and possibly end up with an extra nsDisplaySolidColor item.
1087 // We skip this for the root document, since we don't want to use
1088 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1089 // do it manually there.
1090 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
1091 if (canvasFrame) {
1092 MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
1096 #ifdef DEBUG
1097 state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
1098 nsLayoutPhase::DisplayListBuilding);
1099 #endif
1101 state->mPresShell->UpdateCanvasBackground();
1103 bool buildCaret = mBuildCaret;
1104 if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
1105 state->mIsBackgroundOnly = false;
1106 } else {
1107 state->mIsBackgroundOnly = true;
1108 buildCaret = false;
1111 bool pointerEventsNone = aPointerEventsNoneDoc;
1112 if (IsInSubdocument()) {
1113 pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
1114 .mInsidePointerEventsNoneDoc;
1116 state->mInsidePointerEventsNoneDoc = pointerEventsNone;
1118 state->mPresShellIgnoreScrollFrame =
1119 state->mPresShell->IgnoringViewportScrolling()
1120 ? state->mPresShell->GetRootScrollFrame()
1121 : nullptr;
1123 nsPresContext* pc = aReferenceFrame->PresContext();
1124 mIsInChromePresContext = pc->IsChrome();
1125 nsIDocShell* docShell = pc->GetDocShell();
1127 if (docShell) {
1128 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1131 state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);
1133 if (!buildCaret) {
1134 return;
1137 state->mCaretFrame = [&]() -> nsIFrame* {
1138 RefPtr<nsCaret> caret = state->mPresShell->GetCaret();
1139 nsIFrame* currentCaret = caret->GetPaintGeometry(&mCaretRect);
1140 if (!currentCaret) {
1141 return nullptr;
1144 // Check if the display root for the caret matches the display root that
1145 // we're painting, and only use it if it matches. Likely we only need this
1146 // for carets inside popups.
1147 if (nsLayoutUtils::GetDisplayRootFrame(currentCaret) !=
1148 nsLayoutUtils::GetDisplayRootFrame(aReferenceFrame)) {
1149 return nullptr;
1152 // Caret frames add visual area to their frame, but we don't update the
1153 // overflow area. Use flags to make sure we build display items for that
1154 // frame instead.
1155 MOZ_ASSERT(currentCaret->PresShell() == state->mPresShell);
1156 MarkFrameForDisplay(currentCaret, aReferenceFrame);
1157 caret->SetLastPaintedFrame(currentCaret);
1158 if (!mPaintedCarets.Contains(caret)) {
1159 mPaintedCarets.AppendElement(std::move(caret));
1161 return currentCaret;
1162 }();
1165 // A non-blank paint is a paint that does not just contain the canvas
1166 // background.
1167 static bool DisplayListIsNonBlank(nsDisplayList* aList) {
1168 for (nsDisplayItem* i : *aList) {
1169 switch (i->GetType()) {
1170 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
1171 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
1172 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
1173 continue;
1174 case DisplayItemType::TYPE_SOLID_COLOR:
1175 case DisplayItemType::TYPE_BACKGROUND:
1176 case DisplayItemType::TYPE_BACKGROUND_COLOR:
1177 if (i->Frame()->IsCanvasFrame()) {
1178 continue;
1180 return true;
1181 default:
1182 return true;
1185 return false;
1188 // A contentful paint is a paint that does contains DOM content (text,
1189 // images, non-blank canvases, SVG): "First Contentful Paint entry
1190 // contains a DOMHighResTimeStamp reporting the time when the browser
1191 // first rendered any text, image (including background images),
1192 // non-white canvas or SVG. This excludes any content of iframes, but
1193 // includes text with pending webfonts. This is the first time users
1194 // could start consuming page content."
1195 static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
1196 nsDisplayList* aList) {
1197 for (nsDisplayItem* i : *aList) {
1198 DisplayItemType type = i->GetType();
1199 nsDisplayList* children = i->GetChildren();
1201 switch (type) {
1202 case DisplayItemType::TYPE_SUBDOCUMENT: // iframes are ignored
1203 break;
1204 // CANVASes check if they may have been modified (as a stand-in
1205 // actually tracking all modifications)
1206 default:
1207 if (i->IsContentful()) {
1208 bool dummy;
1209 nsRect bound = i->GetBounds(aBuilder, &dummy);
1210 if (!bound.IsEmpty()) {
1211 return true;
1214 if (children) {
1215 if (DisplayListIsContentful(aBuilder, children)) {
1216 return true;
1219 break;
1222 return false;
1225 void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
1226 nsDisplayList* aPaintedContents) {
1227 NS_ASSERTION(
1228 CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
1229 "Presshell mismatch");
1231 if (mIsPaintingToWindow && aPaintedContents) {
1232 nsPresContext* pc = aReferenceFrame->PresContext();
1233 if (!pc->HadNonBlankPaint()) {
1234 if (!CurrentPresShellState()->mIsBackgroundOnly &&
1235 DisplayListIsNonBlank(aPaintedContents)) {
1236 pc->NotifyNonBlankPaint();
1239 nsRootPresContext* rootPresContext = pc->GetRootPresContext();
1240 if (!pc->HasStoppedGeneratingLCP() && rootPresContext) {
1241 if (!CurrentPresShellState()->mIsBackgroundOnly) {
1242 if (pc->HasEverBuiltInvisibleText() ||
1243 DisplayListIsContentful(this, aPaintedContents)) {
1244 pc->NotifyContentfulPaint();
1250 ResetMarkedFramesForDisplayList(aReferenceFrame);
1251 mPresShellStates.RemoveLastElement();
1253 if (!mPresShellStates.IsEmpty()) {
1254 nsPresContext* pc = CurrentPresContext();
1255 nsIDocShell* docShell = pc->GetDocShell();
1256 if (docShell) {
1257 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1259 mIsInChromePresContext = pc->IsChrome();
1260 } else {
1261 for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
1262 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
1264 mFramesMarkedForDisplayIfVisible.SetLength(0);
1268 void nsDisplayListBuilder::FreeClipChains() {
1269 // Iterate the clip chains from newest to oldest (forward
1270 // iteration), so that we destroy descendants first which
1271 // will drop the ref count on their ancestors.
1272 DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;
1274 while (*indirect) {
1275 if (!(*indirect)->mRefCount) {
1276 DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;
1278 mClipDeduplicator.erase(*indirect);
1279 (*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
1280 Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);
1282 *indirect = next;
1283 } else {
1284 indirect = &(*indirect)->mNextClipChainToDestroy;
1289 void nsDisplayListBuilder::FreeTemporaryItems() {
1290 for (nsDisplayItem* i : mTemporaryItems) {
1291 // Temporary display items are not added to the frames.
1292 MOZ_ASSERT(i->Frame());
1293 i->RemoveFrame(i->Frame());
1294 i->Destroy(this);
1297 mTemporaryItems.Clear();
1300 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1301 const nsIFrame* aReferenceFrame) {
1302 // Unmark and pop off the frames marked for display in this pres shell.
1303 uint32_t firstFrameForShell =
1304 CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1305 for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
1306 ++i) {
1307 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
1309 mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1311 firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
1312 for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
1313 mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
1315 mFramesWithOOFData.SetLength(firstFrameForShell);
1318 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1319 CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
1322 void nsDisplayListBuilder::MarkFramesForDisplayList(
1323 nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
1324 nsRect visibleRect = GetVisibleRect();
1325 nsRect dirtyRect = GetDirtyRect();
1327 // If we are entering content that is fixed to the RCD-RSF, we are
1328 // crossing the async zoom container boundary, and need to convert from
1329 // visual to layout coordinates.
1330 if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
1331 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1332 viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
1333 if (viewportFrame->PresShell()->GetRootScrollFrame()) {
1334 #ifdef DEBUG
1335 for (nsIFrame* f : aFrames) {
1336 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
1338 #endif
1339 visibleRect = ViewportUtils::VisualToLayout(visibleRect,
1340 viewportFrame->PresShell());
1341 dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
1342 viewportFrame->PresShell());
1344 #ifdef DEBUG
1345 else {
1346 // This is an edge case that should only happen if we are in a
1347 // document with a XUL root element so that it does not have a root
1348 // scroll frame but it has fixed pos content and all of the frames in
1349 // aFrames are that fixed pos content.
1350 for (nsIFrame* f : aFrames) {
1351 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
1352 f->GetParent() == aDirtyFrame &&
1353 f->StyleDisplay()->mPosition ==
1354 StylePositionProperty::Fixed);
1356 // There's no root scroll frame so there can't be any zooming or async
1357 // panning so we don't need to adjust the visible and dirty rects.
1359 #endif
1363 bool markedFrames = false;
1364 for (nsIFrame* e : aFrames) {
1365 // Skip the AccessibleCaret frame when building no caret.
1366 if (!IsBuildingCaret()) {
1367 nsIContent* content = e->GetContent();
1368 if (content && content->IsInNativeAnonymousSubtree() &&
1369 content->IsElement()) {
1370 const nsAttrValue* classes = content->AsElement()->GetClasses();
1371 if (classes &&
1372 classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) {
1373 continue;
1377 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
1378 markedFrames = true;
1382 if (markedFrames) {
1383 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1384 // to objects on the stack, so we need to clone the chain.
1385 const DisplayItemClipChain* clipChain =
1386 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1387 const DisplayItemClipChain* combinedClipChain =
1388 mClipState.GetCurrentCombinedClipChain(this);
1389 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1391 OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
1392 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1393 visibleRect, dirtyRect);
1394 aDirtyFrame->SetProperty(
1395 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
1396 mFramesWithOOFData.AppendElement(aDirtyFrame);
1399 if (!aDirtyFrame->GetParent()) {
1400 // This is the viewport frame of aDirtyFrame's presshell.
1401 // Store the current display data so that it can be used for fixed
1402 // background images.
1403 NS_ASSERTION(
1404 CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
1405 "Presshell mismatch");
1406 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
1407 "already traversed this presshell's root frame?");
1409 const DisplayItemClipChain* clipChain =
1410 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1411 const DisplayItemClipChain* combinedClipChain =
1412 mClipState.GetCurrentCombinedClipChain(this);
1413 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1414 CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
1415 clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
1416 GetVisibleRect(), GetDirtyRect());
1421 * Mark all preserve-3d children with
1422 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1423 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1424 * dirty rect for preserve-3d children.
1426 * @param aDirtyFrame is the frame to mark children extending context.
1428 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1429 nsIFrame* aDirtyFrame) {
1430 for (const auto& childList : aDirtyFrame->ChildLists()) {
1431 for (nsIFrame* child : childList.mList) {
1432 if (child->Combines3DTransformWithAncestors()) {
1433 MarkFrameForDisplay(child, aDirtyFrame);
1436 if (child->IsBlockWrapper()) {
1437 // Mark preserve-3d frames inside the block wrapper.
1438 MarkPreserve3DFramesForDisplayList(child);
1444 ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1445 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame) {
1446 RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
1447 aParent, aScrollableFrame, IsRetainingDisplayList());
1448 mActiveScrolledRoots.AppendElement(asr);
1449 return asr;
1452 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1453 const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
1454 const DisplayItemClipChain* aParent) {
1455 MOZ_DIAGNOSTIC_ASSERT(!(aParent && aParent->mOnStack));
1456 void* p = Allocate(sizeof(DisplayItemClipChain),
1457 DisplayListArenaObjectId::CLIPCHAIN);
1458 DisplayItemClipChain* c = new (KnownNotNull, p)
1459 DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy);
1460 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
1461 c->mOnStack = false;
1462 #endif
1463 auto result = mClipDeduplicator.insert(c);
1464 if (!result.second) {
1465 // An equivalent clip chain item was already created, so let's return that
1466 // instead. Destroy the one we just created.
1467 // Note that this can cause clip chains from different coordinate systems to
1468 // collapse into the same clip chain object, because clip chains do not keep
1469 // track of the reference frame that they were created in.
1470 c->DisplayItemClipChain::~DisplayItemClipChain();
1471 Destroy(DisplayListArenaObjectId::CLIPCHAIN, c);
1472 return *(result.first);
1474 mFirstClipChainToDestroy = c;
1475 return c;
1478 struct ClipChainItem {
1479 DisplayItemClip clip;
1480 const ActiveScrolledRoot* asr;
1483 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
1484 const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
1485 for (const ActiveScrolledRoot* asr =
1486 ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
1487 asr; asr = asr->mParent) {
1488 if (aOne == aTwo) {
1489 return aOne;
1491 if (aOne->mASR == asr) {
1492 aOne = aOne->mParent;
1494 if (aTwo->mASR == asr) {
1495 aTwo = aTwo->mParent;
1497 if (!aOne) {
1498 return aTwo;
1500 if (!aTwo) {
1501 return aOne;
1504 return nullptr;
1507 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1508 const DisplayItemClipChain* aAncestor,
1509 const DisplayItemClipChain* aLeafClip1,
1510 const DisplayItemClipChain* aLeafClip2) {
1511 AutoTArray<ClipChainItem, 8> intersectedClips;
1513 const DisplayItemClipChain* clip1 = aLeafClip1;
1514 const DisplayItemClipChain* clip2 = aLeafClip2;
1516 const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
1517 clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);
1519 // Build up the intersection from the leaf to the root and put it into
1520 // intersectedClips. The loop below will convert intersectedClips into an
1521 // actual DisplayItemClipChain.
1522 // (We need to do this in two passes because we need the parent clip in order
1523 // to create the DisplayItemClipChain object, but the parent clip has not
1524 // been created at that point.)
1525 while (!aAncestor || asr != aAncestor->mASR) {
1526 if (clip1 && clip1->mASR == asr) {
1527 if (clip2 && clip2->mASR == asr) {
1528 DisplayItemClip intersection = clip1->mClip;
1529 intersection.IntersectWith(clip2->mClip);
1530 intersectedClips.AppendElement(ClipChainItem{intersection, asr});
1531 clip2 = clip2->mParent;
1532 } else {
1533 intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
1535 clip1 = clip1->mParent;
1536 } else if (clip2 && clip2->mASR == asr) {
1537 intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
1538 clip2 = clip2->mParent;
1540 if (!asr) {
1541 MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
1542 break;
1544 asr = asr->mParent;
1547 // Convert intersectedClips into a DisplayItemClipChain.
1548 const DisplayItemClipChain* parentSC = aAncestor;
1549 for (auto& sc : Reversed(intersectedClips)) {
1550 parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
1552 return parentSC;
1555 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1556 const DisplayItemClipChain* aLeafClip1,
1557 const DisplayItemClipChain* aLeafClip2) {
1558 // aLeafClip2 might be a reference to a clip on the stack. We need to make
1559 // sure that CreateClipChainIntersection will allocate the actual intersected
1560 // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1561 // we supply nullptr as the common ancestor so that
1562 // CreateClipChainIntersection clones the whole chain.
1563 const DisplayItemClipChain* ancestorClip =
1564 aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
1565 : nullptr;
1567 return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2);
1570 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
1571 const DisplayItemClipChain* aClipChain) {
1572 return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
1575 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
1576 const nsIFrame* aFrame, nsPoint* aOffset) const {
1577 auto MaybeApplyAdditionalOffset = [&]() {
1578 if (auto offset = AdditionalOffset()) {
1579 *aOffset += *offset;
1583 if (aFrame == mCurrentFrame) {
1584 if (aOffset) {
1585 *aOffset = mCurrentOffsetToReferenceFrame;
1587 return mCurrentReferenceFrame;
1590 for (const nsIFrame* f = aFrame; f;
1591 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
1592 if (f == mReferenceFrame || f->IsTransformed()) {
1593 if (aOffset) {
1594 *aOffset = aFrame->GetOffsetToCrossDoc(f);
1595 MaybeApplyAdditionalOffset();
1597 return f;
1601 if (aOffset) {
1602 *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1603 MaybeApplyAdditionalOffset();
1606 return mReferenceFrame;
1609 // Sticky frames are active if their nearest scrollable frame is also active.
1610 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
1611 nsIFrame* aFrame) {
1612 MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
1613 StylePositionProperty::Sticky);
1615 StickyScrollContainer* stickyScrollContainer =
1616 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
1617 return stickyScrollContainer &&
1618 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1621 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
1622 nsIFrame** aParent) {
1623 if (aFrame == mReferenceFrame) {
1624 return true;
1627 if (!IsPaintingToWindow()) {
1628 if (aParent) {
1629 *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1631 return false;
1634 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1635 if (!parent) {
1636 return true;
1638 *aParent = parent;
1640 if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
1641 IsStickyFrameActive(this, aFrame)) {
1642 return true;
1645 if (aFrame->IsTransformed()) {
1646 if (EffectCompositor::HasAnimationsForCompositor(
1647 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
1648 return true;
1652 LayoutFrameType parentType = parent->Type();
1653 if (parentType == LayoutFrameType::Scroll ||
1654 parentType == LayoutFrameType::ListControl) {
1655 nsIScrollableFrame* sf = do_QueryFrame(parent);
1656 if (sf->GetScrolledFrame() == aFrame) {
1657 MOZ_ASSERT(!aFrame->IsTransformed());
1658 return sf->IsMaybeAsynchronouslyScrolled();
1662 return false;
1665 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1666 nsIFrame* aFrame) {
1667 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1668 RootReferenceFrame(), aFrame));
1669 nsIFrame* cursor = aFrame;
1670 while (cursor != RootReferenceFrame()) {
1671 nsIFrame* next;
1672 if (IsAnimatedGeometryRoot(cursor, &next)) {
1673 return cursor;
1675 cursor = next;
1677 return cursor;
1680 static nsRect ApplyAllClipNonRoundedIntersection(
1681 const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
1682 nsRect result = aRect;
1683 while (aClipChain) {
1684 result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
1685 aClipChain = aClipChain->mParent;
1687 return result;
1690 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
1691 if (!mWindowDraggingAllowed || !IsForPainting()) {
1692 return;
1695 const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
1696 if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
1697 // This frame has the default value and doesn't influence the window
1698 // dragging region.
1699 return;
1702 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;
1704 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1705 nsIFrame* referenceFrame =
1706 const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1708 if (IsInTransform()) {
1709 // Only support 2d rectilinear transforms. Transform support is needed for
1710 // the horizontal flip transform that's applied to the urlbar textbox in
1711 // RTL mode - it should be able to exclude itself from the draggable region.
1712 referenceFrameToRootReferenceFrame =
1713 ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
1714 nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
1715 RelativeTo{mReferenceFrame})
1716 .GetMatrix());
1717 Matrix referenceFrameToRootReferenceFrame2d;
1718 if (!referenceFrameToRootReferenceFrame.Is2D(
1719 &referenceFrameToRootReferenceFrame2d) ||
1720 !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1721 return;
1723 } else {
1724 MOZ_ASSERT(referenceFrame == mReferenceFrame,
1725 "referenceFrameToRootReferenceFrame needs to be adjusted");
1728 // We do some basic visibility checking on the frame's border box here.
1729 // We intersect it both with the current dirty rect and with the current
1730 // clip. Either one is just a conservative approximation on its own, but
1731 // their intersection luckily works well enough for our purposes, so that
1732 // we don't have to do full-blown visibility computations.
1733 // The most important case we need to handle is the scrolled-off tab:
1734 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1735 // should not be allowed to interfere with the window dragging region. Using
1736 // just the current DisplayItemClip is not enough to cover this case
1737 // completely because clips are reset while building stacking context
1738 // contents, so for example we'd fail to clip frames that have a clip path
1739 // applied to them. But the current dirty rect doesn't get reset in that
1740 // case, so we use it to make this case work.
1741 nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
1742 borderBox += ToReferenceFrame(aFrame);
1743 const DisplayItemClipChain* clip =
1744 ClipState().GetCurrentCombinedClipChain(this);
1745 borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
1746 if (borderBox.IsEmpty()) {
1747 return;
1750 LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
1751 borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1753 LayoutDeviceRect transformedDevPixelBorderBox =
1754 TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1755 transformedDevPixelBorderBox.Round();
1756 LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1758 if (!transformedDevPixelBorderBox.ToIntRect(
1759 &transformedDevPixelBorderBoxInt)) {
1760 return;
1763 LayoutDeviceIntRegion& region =
1764 styleUI->mWindowDragging == StyleWindowDragging::Drag
1765 ? mWindowDraggingRegion
1766 : mWindowNoDraggingRegion;
1768 if (!IsRetainingDisplayList()) {
1769 region.OrWith(transformedDevPixelBorderBoxInt);
1770 return;
1773 gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
1774 if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
1775 mRetainedWindowDraggingRegion.Add(aFrame, rect);
1776 } else {
1777 mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
1781 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
1782 LayoutDeviceIntRegion result;
1783 if (!IsRetainingDisplayList()) {
1784 result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
1785 return result;
1788 LayoutDeviceIntRegion dragRegion =
1789 mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();
1791 LayoutDeviceIntRegion noDragRegion =
1792 mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();
1794 result.Sub(dragRegion, noDragRegion);
1795 return result;
1798 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1799 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes);
1800 aSizes.mLayoutRetainedDisplayListSize +=
1801 aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get());
1804 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1805 mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);
1807 size_t n = 0;
1808 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
1809 n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1810 n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1811 n += mEffectsUpdates.ShallowSizeOfExcludingThis(mallocSizeOf);
1812 n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1813 n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1814 n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
1815 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1817 aSizes.mLayoutRetainedDisplayListSize += n;
1820 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1821 for (nsDisplayItem* item : *this) {
1822 item->AddSizeOfExcludingThis(aSizes);
1823 if (RetainedDisplayList* children = item->GetChildren()) {
1824 children->AddSizeOfExcludingThis(aSizes);
1828 size_t n = 0;
1830 n += mDAG.mDirectPredecessorList.ShallowSizeOfExcludingThis(
1831 aSizes.mState.mMallocSizeOf);
1832 n += mDAG.mNodesInfo.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1833 n += mOldItems.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1835 aSizes.mLayoutRetainedDisplayListSize += n;
1838 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1839 MallocSizeOf aMallocSizeOf) const {
1840 size_t n = 0;
1841 n += mFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
1842 for (const auto& frame : mFrames) {
1843 const UniquePtr<WeakFrame>& weakFrame = frame.mWeakFrame;
1844 n += aMallocSizeOf(weakFrame.get());
1846 n += mRects.ShallowSizeOfExcludingThis(aMallocSizeOf);
1847 return n;
1851 * Removes modified frames and rects from this WeakFrameRegion.
1853 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1854 MOZ_ASSERT(mFrames.Length() == mRects.Length());
1856 uint32_t i = 0;
1857 uint32_t length = mFrames.Length();
1859 while (i < length) {
1860 auto& wrapper = mFrames[i];
1862 if (!wrapper.mWeakFrame->IsAlive() ||
1863 AnyContentAncestorModified(wrapper.mWeakFrame->GetFrame())) {
1864 // To avoid multiple O(n) shifts in the array, move the last element of
1865 // the array to the current position and decrease the array length.
1866 mFrameSet.Remove(wrapper.mFrame);
1867 mFrames[i] = std::move(mFrames[length - 1]);
1868 mRects[i] = std::move(mRects[length - 1]);
1869 length--;
1870 } else {
1871 i++;
1875 mFrames.TruncateLength(length);
1876 mRects.TruncateLength(length);
1879 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1880 mRetainedWindowDraggingRegion.RemoveModifiedFramesAndRects();
1881 mRetainedWindowNoDraggingRegion.RemoveModifiedFramesAndRects();
1882 mRetainedWindowOpaqueRegion.RemoveModifiedFramesAndRects();
1885 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1886 mRetainedWindowDraggingRegion.Clear();
1887 mRetainedWindowNoDraggingRegion.Clear();
1888 mRetainedWindowOpaqueRegion.Clear();
1891 const uint32_t gWillChangeAreaMultiplier = 3;
1892 static uint32_t GetLayerizationCost(const nsSize& aSize) {
1893 // There's significant overhead for each layer created from Gecko
1894 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1895 // Therefore we set a minimum cost threshold of a 64x64 area.
1896 const int minBudgetCost = 64 * 64;
1898 const uint32_t budgetCost = std::max(
1899 minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
1900 nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
1902 return budgetCost;
1905 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
1906 const nsSize& aSize) {
1907 MOZ_ASSERT(IsForPainting());
1909 if (aFrame->MayHaveWillChangeBudget()) {
1910 // The frame is already in the will-change budget.
1911 return true;
1914 const nsPresContext* presContext = aFrame->PresContext();
1915 const nsRect area = presContext->GetVisibleArea();
1916 const uint32_t budgetLimit =
1917 nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1918 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1919 const uint32_t cost = GetLayerizationCost(aSize);
1921 DocumentWillChangeBudget& documentBudget =
1922 mDocumentWillChangeBudgets.LookupOrInsert(presContext);
1924 const bool onBudget =
1925 (documentBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
1927 if (onBudget) {
1928 documentBudget += cost;
1929 mFrameWillChangeBudgets.InsertOrUpdate(
1930 aFrame, FrameWillChangeBudget(presContext, cost));
1931 aFrame->SetMayHaveWillChangeBudget(true);
1934 return onBudget;
1937 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
1938 const nsSize& aSize) {
1939 if (!IsForPainting()) {
1940 // If this nsDisplayListBuilder is not for painting, the layerization should
1941 // not matter. Do the simple thing and return false.
1942 return false;
1945 const bool onBudget = AddToWillChangeBudget(aFrame, aSize);
1946 if (onBudget) {
1947 return true;
1950 auto* pc = aFrame->PresContext();
1951 auto* doc = pc->Document();
1952 if (!doc->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget)) {
1953 AutoTArray<nsString, 2> params;
1954 params.AppendElement()->AppendInt(gWillChangeAreaMultiplier);
1956 nsRect area = pc->GetVisibleArea();
1957 uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1958 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1959 params.AppendElement()->AppendInt(budgetLimit);
1961 doc->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget, false, params);
1964 return false;
1967 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame* aFrame) {
1968 MOZ_ASSERT(IsForPainting());
1970 if (!aFrame->MayHaveWillChangeBudget()) {
1971 return;
1974 aFrame->SetMayHaveWillChangeBudget(false);
1975 RemoveFromWillChangeBudgets(aFrame);
1978 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame* aFrame) {
1979 if (auto entry = mFrameWillChangeBudgets.Lookup(aFrame)) {
1980 const FrameWillChangeBudget& frameBudget = entry.Data();
1982 auto documentBudget =
1983 mDocumentWillChangeBudgets.Lookup(frameBudget.mPresContext);
1985 if (documentBudget) {
1986 *documentBudget -= frameBudget.mUsage;
1989 entry.Remove();
1993 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1994 mFrameWillChangeBudgets.Clear();
1995 mDocumentWillChangeBudgets.Clear();
1998 void nsDisplayListBuilder::EnterSVGEffectsContents(
1999 nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) {
2000 MOZ_ASSERT(aHoistedItemsStorage);
2001 if (mSVGEffectsFrames.IsEmpty()) {
2002 MOZ_ASSERT(!mScrollInfoItemsForHoisting);
2003 mScrollInfoItemsForHoisting = aHoistedItemsStorage;
2005 mSVGEffectsFrames.AppendElement(aEffectsFrame);
2008 void nsDisplayListBuilder::ExitSVGEffectsContents() {
2009 MOZ_ASSERT(!mSVGEffectsFrames.IsEmpty());
2010 mSVGEffectsFrames.RemoveLastElement();
2011 MOZ_ASSERT(mScrollInfoItemsForHoisting);
2012 if (mSVGEffectsFrames.IsEmpty()) {
2013 mScrollInfoItemsForHoisting = nullptr;
2017 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
2019 * Note: if changing the conditions under which scroll info layers
2020 * are created, make a corresponding change to
2021 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
2023 for (nsIFrame* frame : mSVGEffectsFrames) {
2024 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame)) {
2025 return true;
2028 return false;
2031 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
2032 nsDisplayScrollInfoLayer* aScrollInfoItem) {
2033 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2034 MOZ_ASSERT(mScrollInfoItemsForHoisting);
2035 mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
2038 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2039 nsIFrame* aFrame, nsDisplayList* aList) {
2040 MOZ_ASSERT(aFrame);
2041 MOZ_ASSERT(aList);
2043 if (!BuildCompositorHitTestInfo()) {
2044 return;
2047 const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
2048 if (info != CompositorHitTestInvisibleToHit) {
2049 aList->AppendNewToTop<nsDisplayCompositorHitTestInfo>(this, aFrame);
2053 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem* aItem) {
2054 mReuseableItems.Insert(aItem);
2057 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem* aItem) {
2058 MOZ_ASSERT(aItem->IsReusedItem());
2059 mReuseableItems.Remove(aItem);
2062 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2063 const size_t total = mReuseableItems.Count();
2065 size_t reused = 0;
2066 for (auto* item : mReuseableItems) {
2067 if (item->IsReusedItem()) {
2068 reused++;
2069 item->SetReusable();
2070 } else {
2071 item->Destroy(this);
2075 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
2076 mReuseableItems.Clear();
2079 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
2080 const auto* previous = mCurrentContainerASR;
2081 const auto* asr = aItem->GetActiveScrolledRoot();
2082 mCurrentContainerASR =
2083 ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
2085 if (previous != mCurrentContainerASR) {
2086 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
2087 mCurrentContainerASR);
2090 aItem->SetReusedItem();
2093 void nsDisplayListSet::CopyTo(const nsDisplayListSet& aDestination) const {
2094 for (size_t i = 0; i < mLists.size(); ++i) {
2095 auto* from = mLists[i];
2096 auto* to = aDestination.mLists[i];
2098 from->CopyTo(to);
2102 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
2103 aDestination.BorderBackground()->AppendToTop(BorderBackground());
2104 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2105 aDestination.Floats()->AppendToTop(Floats());
2106 aDestination.Content()->AppendToTop(Content());
2107 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
2108 aDestination.Outlines()->AppendToTop(Outlines());
2111 nsRect nsDisplayList::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2112 nsRect bounds;
2113 for (nsDisplayItem* i : *this) {
2114 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
2116 return bounds;
2119 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR(
2120 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
2121 nsRect* aBuildingRect) const {
2122 nsRect bounds;
2123 for (nsDisplayItem* i : *this) {
2124 nsRect r = i->GetClippedBounds(aBuilder);
2125 if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
2126 if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) {
2127 r = clip.ref();
2130 if (aBuildingRect) {
2131 aBuildingRect->UnionRect(*aBuildingRect, i->GetBuildingRect());
2133 bounds.UnionRect(bounds, r);
2135 return bounds;
2138 nsRect nsDisplayList::GetBuildingRect() const {
2139 nsRect result;
2140 for (nsDisplayItem* i : *this) {
2141 result.UnionRect(result, i->GetBuildingRect());
2143 return result;
2146 WindowRenderer* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView** aView) {
2147 if (aView) {
2148 *aView = RootReferenceFrame()->GetView();
2150 if (RootReferenceFrame() !=
2151 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2152 return nullptr;
2154 nsIWidget* window = RootReferenceFrame()->GetNearestWidget();
2155 if (window) {
2156 return window->GetWindowRenderer();
2158 return nullptr;
2161 WebRenderLayerManager* nsDisplayListBuilder::GetWidgetLayerManager(
2162 nsView** aView) {
2163 WindowRenderer* renderer = GetWidgetWindowRenderer();
2164 if (renderer) {
2165 return renderer->AsWebRender();
2167 return nullptr;
2170 void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2171 int32_t aAppUnitsPerDevPixel) {
2172 FlattenedDisplayListIterator iter(aBuilder, this);
2173 while (iter.HasNext()) {
2174 nsPaintedDisplayItem* item = iter.GetNextItem()->AsPaintedDisplayItem();
2175 if (!item) {
2176 continue;
2179 nsRect visible = item->GetClippedBounds(aBuilder);
2180 visible = visible.Intersect(item->GetPaintRect(aBuilder, aCtx));
2181 if (visible.IsEmpty()) {
2182 continue;
2185 DisplayItemClip currentClip = item->GetClip();
2186 if (currentClip.HasClip()) {
2187 aCtx->Save();
2188 if (currentClip.IsRectClippedByRoundedCorner(visible)) {
2189 currentClip.ApplyTo(aCtx, aAppUnitsPerDevPixel);
2190 } else {
2191 currentClip.ApplyRectTo(aCtx, aAppUnitsPerDevPixel);
2194 aCtx->NewPath();
2196 item->Paint(aBuilder, aCtx);
2198 if (currentClip.HasClip()) {
2199 aCtx->Restore();
2205 * We paint by executing a layer manager transaction, constructing a
2206 * single layer representing the display list, and then making it the
2207 * root of the layer manager, drawing into the PaintedLayers.
2209 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2210 uint32_t aFlags,
2211 Maybe<double> aDisplayListBuildTime) {
2212 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS);
2214 RefPtr<WebRenderLayerManager> layerManager;
2215 WindowRenderer* renderer = nullptr;
2216 bool widgetTransaction = false;
2217 bool doBeginTransaction = true;
2218 nsView* view = nullptr;
2219 if (aFlags & PAINT_USE_WIDGET_LAYERS) {
2220 renderer = aBuilder->GetWidgetWindowRenderer(&view);
2221 if (renderer) {
2222 // The fallback renderer doesn't retain any content, so it's
2223 // not meaningful to use it when drawing to an external context.
2224 if (aCtx && renderer->AsFallback()) {
2225 MOZ_ASSERT(!(aFlags & PAINT_EXISTING_TRANSACTION));
2226 renderer = nullptr;
2227 } else {
2228 layerManager = renderer->AsWebRender();
2229 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
2230 widgetTransaction = true;
2235 nsIFrame* frame = aBuilder->RootReferenceFrame();
2236 nsPresContext* presContext = frame->PresContext();
2237 PresShell* presShell = presContext->PresShell();
2238 Document* document = presShell->GetDocument();
2240 ScopeExit g([&]() {
2241 #ifdef DEBUG
2242 MOZ_ASSERT(!layerManager || !layerManager->GetTarget());
2243 #endif
2245 // For layers-free mode, we check the invalidation state bits in the
2246 // EndTransaction. So we clear the invalidation state bits after
2247 // EndTransaction.
2248 if (widgetTransaction ||
2249 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2250 // but they still need the invalidation state bits cleared in order for
2251 // invalidation for CSS/SMIL animation to work properly.
2252 (document && document->IsBeingUsedAsImage())) {
2253 DL_LOGD("Clearing invalidation state bits");
2254 frame->ClearInvalidationStateBits();
2258 if (!renderer) {
2259 if (!aCtx) {
2260 NS_WARNING("Nowhere to paint into");
2261 return;
2263 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(false);
2264 Paint(aBuilder, aCtx, presContext->AppUnitsPerDevPixel());
2266 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2267 return;
2270 if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) {
2271 MOZ_ASSERT(layerManager);
2272 if (doBeginTransaction) {
2273 if (aCtx) {
2274 if (!layerManager->BeginTransactionWithTarget(aCtx, nsCString())) {
2275 return;
2277 } else {
2278 if (!layerManager->BeginTransaction(nsCString())) {
2279 return;
2284 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(true);
2285 layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
2287 bool sent = false;
2288 if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
2289 MOZ_ASSERT(!aCtx);
2290 sent = layerManager->EndEmptyTransaction();
2293 if (!sent) {
2294 auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
2296 nsIDocShell* docShell = presContext->GetDocShell();
2297 WrFiltersHolder wrFilters;
2298 gfx::Matrix5x4* colorMatrix =
2299 nsDocShell::Cast(docShell)->GetColorMatrix();
2300 if (colorMatrix) {
2301 wrFilters.filters.AppendElement(
2302 wr::FilterOp::ColorMatrix(colorMatrix->components));
2305 wrManager->EndTransactionWithoutLayer(this, aBuilder,
2306 std::move(wrFilters), nullptr,
2307 aDisplayListBuildTime.valueOr(0.0));
2310 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2311 if (presContext->RefreshDriver()->HasScheduleFlush()) {
2312 presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2313 frame->GetRect());
2316 return;
2319 FallbackRenderer* fallback = renderer->AsFallback();
2320 MOZ_ASSERT(fallback);
2322 if (doBeginTransaction) {
2323 MOZ_ASSERT(!aCtx);
2324 if (!fallback->BeginTransaction()) {
2325 return;
2329 bool temp = aBuilder->SetIsCompositingCheap(renderer->IsCompositingCheap());
2330 fallback->EndTransactionWithList(aBuilder, this,
2331 presContext->AppUnitsPerDevPixel(),
2332 WindowRenderer::END_DEFAULT);
2334 aBuilder->SetIsCompositingCheap(temp);
2337 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
2338 for (auto* item : TakeItems()) {
2339 item->Destroy(aBuilder);
2343 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) {
2344 return aFrame->Style()->PointerEvents() != StylePointerEvents::None;
2347 // A list of frames, and their z depth. Used for sorting
2348 // the results of hit testing.
2349 struct FramesWithDepth {
2350 explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {}
2352 bool operator<(const FramesWithDepth& aOther) const {
2353 if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
2354 // We want to sort so that the shallowest item (highest depth value) is
2355 // first
2356 return mDepth > aOther.mDepth;
2358 return false;
2360 bool operator==(const FramesWithDepth& aOther) const {
2361 return this == &aOther;
2364 float mDepth;
2365 nsTArray<nsIFrame*> mFrames;
2368 // Sort the frames by depth and then moves all the contained frames to the
2369 // destination
2370 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource,
2371 nsTArray<nsIFrame*>* aDest) {
2372 if (aSource.IsEmpty()) {
2373 return;
2375 aSource.StableSort();
2376 uint32_t length = aSource.Length();
2377 for (uint32_t i = 0; i < length; i++) {
2378 aDest->AppendElements(std::move(aSource[i].mFrames));
2380 aSource.Clear();
2383 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2384 nsDisplayItem::HitTestState* aState,
2385 nsTArray<nsIFrame*>* aOutFrames) const {
2386 nsDisplayItem* item;
2388 if (aState->mInPreserves3D) {
2389 // Collect leaves of the current 3D rendering context.
2390 for (nsDisplayItem* item : *this) {
2391 auto itemType = item->GetType();
2392 if (itemType != DisplayItemType::TYPE_TRANSFORM ||
2393 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2394 item->HitTest(aBuilder, aRect, aState, aOutFrames);
2395 } else {
2396 // One of leaves in the current 3D rendering context.
2397 aState->mItemBuffer.AppendElement(item);
2400 return;
2403 int32_t itemBufferStart = aState->mItemBuffer.Length();
2404 for (nsDisplayItem* item : *this) {
2405 aState->mItemBuffer.AppendElement(item);
2408 AutoTArray<FramesWithDepth, 16> temp;
2409 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart;
2410 --i) {
2411 // Pop element off the end of the buffer. We want to shorten the buffer
2412 // so that recursive calls to HitTest have more buffer space.
2413 item = aState->mItemBuffer[i];
2414 aState->mItemBuffer.SetLength(i);
2416 bool snap;
2417 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
2418 auto itemType = item->GetType();
2419 bool same3DContext =
2420 (itemType == DisplayItemType::TYPE_TRANSFORM &&
2421 static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
2422 (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
2423 item->Frame()->Extend3DContext());
2424 if (same3DContext &&
2425 (itemType != DisplayItemType::TYPE_TRANSFORM ||
2426 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
2427 if (!item->GetClip().MayIntersect(aRect)) {
2428 continue;
2430 AutoTArray<nsIFrame*, 1> neverUsed;
2431 // Start gathering leaves of the 3D rendering context, and
2432 // append leaves at the end of mItemBuffer. Leaves are
2433 // processed at following iterations.
2434 aState->mInPreserves3D = true;
2435 item->HitTest(aBuilder, aRect, aState, &neverUsed);
2436 aState->mInPreserves3D = false;
2437 i = aState->mItemBuffer.Length();
2438 continue;
2440 if (same3DContext || item->GetClip().MayIntersect(r)) {
2441 AutoTArray<nsIFrame*, 16> outFrames;
2442 item->HitTest(aBuilder, aRect, aState, &outFrames);
2444 // For 3d transforms with preserve-3d we add hit frames into the temp list
2445 // so we can sort them later, otherwise we add them directly to the output
2446 // list.
2447 nsTArray<nsIFrame*>* writeFrames = aOutFrames;
2448 if (item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
2449 static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2450 if (outFrames.Length()) {
2451 nsDisplayTransform* transform =
2452 static_cast<nsDisplayTransform*>(item);
2453 nsPoint point = aRect.TopLeft();
2454 // A 1x1 rect means a point, otherwise use the center of the rect
2455 if (aRect.width != 1 || aRect.height != 1) {
2456 point = aRect.Center();
2458 temp.AppendElement(
2459 FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
2460 writeFrames = &temp[temp.Length() - 1].mFrames;
2462 } else {
2463 // We may have just finished a run of consecutive preserve-3d
2464 // transforms, so flush these into the destination array before
2465 // processing our frame list.
2466 FlushFramesArray(temp, aOutFrames);
2469 for (uint32_t j = 0; j < outFrames.Length(); j++) {
2470 nsIFrame* f = outFrames.ElementAt(j);
2471 // Filter out some frames depending on the type of hittest
2472 // we are doing. For visibility tests, pass through all frames.
2473 // For pointer tests, only pass through frames that are styled
2474 // to receive pointer events.
2475 if (aBuilder->HitTestIsForVisibility() ||
2476 IsFrameReceivingPointerEvents(f)) {
2477 writeFrames->AppendElement(f);
2481 if (aBuilder->HitTestIsForVisibility()) {
2482 aState->mHitOccludingItem = [&] {
2483 if (aState->mHitOccludingItem) {
2484 // We already hit something before.
2485 return true;
2487 if (aState->mCurrentOpacity == 1.0f &&
2488 item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) {
2489 // An opaque item always occludes everything. Note that we need to
2490 // check wrapping opacity and such as well.
2491 return true;
2493 float threshold = aBuilder->VisibilityThreshold();
2494 if (threshold == 1.0f) {
2495 return false;
2497 float itemOpacity = [&] {
2498 switch (item->GetType()) {
2499 case DisplayItemType::TYPE_OPACITY:
2500 return static_cast<nsDisplayOpacity*>(item)->GetOpacity();
2501 case DisplayItemType::TYPE_BACKGROUND_COLOR:
2502 return static_cast<nsDisplayBackgroundColor*>(item)
2503 ->GetOpacity();
2504 default:
2505 // Be conservative and assume it won't occlude other items.
2506 return 0.0f;
2508 }();
2509 return itemOpacity * aState->mCurrentOpacity >= threshold;
2510 }();
2512 if (aState->mHitOccludingItem) {
2513 // We're exiting early, so pop the remaining items off the buffer.
2514 aState->mItemBuffer.TruncateLength(itemBufferStart);
2515 break;
2520 // Clear any remaining preserve-3d transforms.
2521 FlushFramesArray(temp, aOutFrames);
2522 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
2523 "How did we forget to pop some elements?");
2526 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, Document* aDoc) {
2527 nsIFrame* f = aItem->Frame();
2528 while (f) {
2529 nsPresContext* pc = f->PresContext();
2530 if (pc->Document() == aDoc) {
2531 return f->GetContent();
2533 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(
2534 pc->PresShell()->GetRootFrame());
2536 return nullptr;
2539 struct ZSortItem {
2540 nsDisplayItem* item;
2541 int32_t zIndex;
2543 explicit ZSortItem(nsDisplayItem* aItem)
2544 : item(aItem), zIndex(aItem->ZIndex()) {}
2546 operator nsDisplayItem*() { return item; }
2549 struct ZOrderComparator {
2550 bool LessThan(const ZSortItem& aLeft, const ZSortItem& aRight) const {
2551 return aLeft.zIndex < aRight.zIndex;
2555 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); }
2557 struct ContentComparator {
2558 nsIContent* mCommonAncestor;
2560 explicit ContentComparator(nsIContent* aCommonAncestor)
2561 : mCommonAncestor(aCommonAncestor) {}
2563 bool LessThan(nsDisplayItem* aLeft, nsDisplayItem* aRight) const {
2564 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2565 // subdocument of commonAncestor, because display items for subdocuments
2566 // have been mixed into the same list. Ensure that we're looking at content
2567 // in commonAncestor's document.
2568 Document* commonAncestorDoc = mCommonAncestor->OwnerDoc();
2569 nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc);
2570 nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc);
2571 if (!content1 || !content2) {
2572 NS_ERROR("Document trees are mixed up!");
2573 // Something weird going on
2574 return true;
2576 return nsContentUtils::CompareTreePosition<TreeKind::Flat>(
2577 content1, content2, mCommonAncestor) < 0;
2581 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) {
2582 Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor));
2585 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2586 static_assert(sizeof(nsDisplayItem) <= 176, "nsDisplayItem has grown");
2587 #endif
2589 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2590 : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
2592 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2593 const ActiveScrolledRoot* aActiveScrolledRoot)
2594 : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) {
2595 MOZ_COUNT_CTOR(nsDisplayItem);
2596 MOZ_ASSERT(mFrame);
2597 if (aBuilder->IsRetainingDisplayList()) {
2598 mFrame->AddDisplayItem(this);
2601 aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
2602 NS_ASSERTION(
2603 aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
2604 "visible rect not set");
2606 mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2608 // The visible rect is for mCurrentFrame, so we have to use
2609 // mCurrentOffsetToReferenceFrame
2610 nsRect visible = aBuilder->GetVisibleRect() +
2611 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
2612 SetBuildingRect(visible);
2614 const nsStyleDisplay* disp = mFrame->StyleDisplay();
2615 if (mFrame->BackfaceIsHidden(disp)) {
2616 mItemFlags += ItemFlag::BackfaceHidden;
2618 if (mFrame->Combines3DTransformWithAncestors()) {
2619 mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
2623 void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
2625 bool nsDisplayItem::HasDeletedFrame() const {
2626 bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) ||
2627 (GetType() == DisplayItemType::TYPE_REMOTE &&
2628 !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader());
2629 MOZ_ASSERT(retval || mFrame);
2630 return retval;
2633 /* static */
2634 bool nsDisplayItem::ForceActiveLayers() {
2635 return StaticPrefs::layers_force_active();
2638 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); }
2640 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
2641 bool aStore) {
2642 mClipChain = aClipChain;
2645 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
2646 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2647 if (const DisplayItemClip* clip =
2648 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
2649 return Some(clip->GetClipRect());
2651 #ifdef DEBUG
2652 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
2653 #endif
2654 return Nothing();
2657 const DisplayItemClip& nsDisplayItem::GetClip() const {
2658 const DisplayItemClip* clip =
2659 DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot);
2660 return clip ? *clip : DisplayItemClip::NoClip();
2663 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
2664 const DisplayItemClipChain* aOther,
2665 bool aStore) {
2666 if (!aOther || mClipChain == aOther) {
2667 return;
2670 // aOther might be a reference to a clip on the stack. We need to make sure
2671 // that CreateClipChainIntersection will allocate the actual intersected
2672 // clip in the builder's arena, so for the mClipChain == nullptr case,
2673 // we supply nullptr as the common ancestor so that
2674 // CreateClipChainIntersection clones the whole chain.
2675 const DisplayItemClipChain* ancestorClip =
2676 mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther)
2677 : nullptr;
2679 SetClipChain(
2680 aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther),
2681 aStore);
2684 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2685 bool snap;
2686 nsRect r = GetBounds(aBuilder, &snap);
2687 return GetClip().ApplyNonRoundedIntersection(r);
2690 nsDisplayContainer::nsDisplayContainer(
2691 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2692 const ActiveScrolledRoot* aActiveScrolledRoot, nsDisplayList* aList)
2693 : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
2694 mChildren(aBuilder) {
2695 MOZ_COUNT_CTOR(nsDisplayContainer);
2696 mChildren.AppendToTop(aList);
2697 UpdateBounds(aBuilder);
2699 // Clear and store the clip chain set by nsDisplayItem constructor.
2700 nsDisplayItem::SetClipChain(nullptr, true);
2703 nsRect nsDisplayItem::GetPaintRect(nsDisplayListBuilder* aBuilder,
2704 gfxContext* aCtx) {
2705 bool dummy;
2706 nsRect result = GetBounds(aBuilder, &dummy);
2707 if (aCtx) {
2708 result.IntersectRect(result,
2709 nsLayoutUtils::RoundGfxRectToAppRect(
2710 aCtx->GetClipExtents(),
2711 mFrame->PresContext()->AppUnitsPerDevPixel()));
2713 return result;
2716 bool nsDisplayContainer::CreateWebRenderCommands(
2717 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2718 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2719 nsDisplayListBuilder* aDisplayListBuilder) {
2720 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2721 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
2722 false);
2723 return true;
2726 nsRect nsDisplayContainer::GetBounds(nsDisplayListBuilder* aBuilder,
2727 bool* aSnap) const {
2728 *aSnap = false;
2729 return mBounds;
2732 nsRect nsDisplayContainer::GetComponentAlphaBounds(
2733 nsDisplayListBuilder* aBuilder) const {
2734 return mChildren.GetComponentAlphaBounds(aBuilder);
2737 static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2738 nsDisplayList* aList,
2739 const nsRect& aListBounds) {
2740 return aList->GetOpaqueRegion(aBuilder);
2743 nsRegion nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2744 bool* aSnap) const {
2745 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
2746 GetBounds(aBuilder, aSnap));
2749 Maybe<nsRect> nsDisplayContainer::GetClipWithRespectToASR(
2750 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2751 // Our children should have finite bounds with respect to |aASR|.
2752 if (aASR == mActiveScrolledRoot) {
2753 return Some(mBounds);
2756 return Some(mChildren.GetClippedBoundsWithRespectToASR(aBuilder, aASR));
2759 void nsDisplayContainer::HitTest(nsDisplayListBuilder* aBuilder,
2760 const nsRect& aRect, HitTestState* aState,
2761 nsTArray<nsIFrame*>* aOutFrames) {
2762 mChildren.HitTest(aBuilder, aRect, aState, aOutFrames);
2765 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder* aBuilder) {
2766 // Container item bounds are expected to be clipped.
2767 mBounds =
2768 mChildren.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
2771 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder,
2772 bool* aSnap) const {
2773 *aSnap = true;
2774 return mBounds;
2777 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
2778 gfxContext* aCtx) {
2779 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2780 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2781 Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx),
2782 appUnitsPerDevPixel, *drawTarget);
2783 drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
2786 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) {
2787 aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
2788 << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
2789 << ")";
2792 bool nsDisplaySolidColor::CreateWebRenderCommands(
2793 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2794 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2795 nsDisplayListBuilder* aDisplayListBuilder) {
2796 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
2797 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
2798 wr::LayoutRect r = wr::ToLayoutRect(bounds);
2799 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, mIsCheckerboardBackground,
2800 wr::ToColorF(ToDeviceColor(mColor)));
2802 return true;
2805 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
2806 bool* aSnap) const {
2807 *aSnap = true;
2808 return mRegion.GetBounds();
2811 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
2812 gfxContext* aCtx) {
2813 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2814 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2815 ColorPattern color(ToDeviceColor(mColor));
2816 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2817 Rect rect =
2818 NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
2819 drawTarget->FillRect(rect, color);
2823 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) {
2824 aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255)
2825 << "," << int(mColor.b * 255) << "," << mColor.a << ")";
2828 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2829 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2830 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2831 nsDisplayListBuilder* aDisplayListBuilder) {
2832 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2833 nsRect rect = iter.Get();
2834 LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
2835 rect, mFrame->PresContext()->AppUnitsPerDevPixel());
2836 wr::LayoutRect r = wr::ToLayoutRect(layerRects);
2837 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
2838 wr::ToColorF(ToDeviceColor(mColor)));
2841 return true;
2844 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder,
2845 nsDisplayItem* aItem, nsIFrame* aFrame,
2846 nsITheme::ThemeGeometryType aType) {
2847 if (aBuilder->IsInChromeDocumentOrPopup()) {
2848 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2849 bool preservesAxisAlignedRectangles = false;
2850 nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(
2851 aFrame, aFrame->GetRectRelativeToSelf(), displayRoot,
2852 &preservesAxisAlignedRectangles);
2853 if (preservesAxisAlignedRectangles) {
2854 aBuilder->RegisterThemeGeometry(
2855 aType, aItem,
2856 LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels(
2857 aFrame->PresContext()->AppUnitsPerDevPixel())));
2862 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2863 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
2864 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
2865 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
2866 nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
2867 nsRect rootRect = rootFrame->GetRectRelativeToSelf();
2868 if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) ==
2869 nsLayoutUtils::TRANSFORM_SUCCEEDED) {
2870 return Some(rootRect + aBuilder->ToReferenceFrame(aFrame));
2872 return Nothing();
2875 /* static */ nsDisplayBackgroundImage::InitData
2876 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder,
2877 nsIFrame* aFrame, uint16_t aLayer,
2878 const nsRect& aBackgroundRect,
2879 const ComputedStyle* aBackgroundStyle) {
2880 nsPresContext* presContext = aFrame->PresContext();
2881 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2882 const nsStyleImageLayers::Layer& layer =
2883 aBackgroundStyle->StyleBackground()->mImage.mLayers[aLayer];
2885 bool isTransformedFixed;
2886 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
2887 presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer,
2888 &isTransformedFixed);
2890 // background-attachment:fixed is treated as background-attachment:scroll
2891 // if it's affected by a transform.
2892 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2893 bool shouldTreatAsFixed =
2894 layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2895 !isTransformedFixed;
2897 bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsNone();
2898 bool isRasterImage = state.mImageRenderer.IsRasterImage();
2899 nsCOMPtr<imgIContainer> image;
2900 if (isRasterImage) {
2901 image = state.mImageRenderer.GetImage();
2903 return InitData{aBuilder, aBackgroundStyle, image,
2904 aBackgroundRect, state.mFillArea, state.mDestArea,
2905 aLayer, isRasterImage, shouldFixToViewport};
2908 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2909 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aInitData,
2910 nsIFrame* aFrameForBounds)
2911 : nsPaintedDisplayItem(aBuilder, aFrame),
2912 mBackgroundStyle(aInitData.backgroundStyle),
2913 mImage(aInitData.image),
2914 mDependentFrame(nullptr),
2915 mBackgroundRect(aInitData.backgroundRect),
2916 mFillRect(aInitData.fillArea),
2917 mDestRect(aInitData.destArea),
2918 mLayer(aInitData.layer),
2919 mIsRasterImage(aInitData.isRasterImage),
2920 mShouldFixToViewport(aInitData.shouldFixToViewport) {
2921 MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
2922 #ifdef DEBUG
2923 if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) {
2924 // If this changes, then you also need to adjust css::ImageLoader to
2925 // invalidate mFrame as needed.
2926 MOZ_ASSERT(mFrame->IsCanvasFrame() || mFrame->IsTablePart());
2928 #endif
2930 mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
2931 if (mShouldFixToViewport) {
2932 // Expand the item's visible rect to cover the entire bounds, limited to the
2933 // viewport rect. This is necessary because the background's clip can move
2934 // asynchronously.
2935 if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
2936 aInitData.builder, mFrame)) {
2937 SetBuildingRect(mBounds.Intersect(*viewportRect));
2942 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2943 MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
2944 if (mDependentFrame) {
2945 mDependentFrame->RemoveDisplayItem(this);
2949 static void SetBackgroundClipRegion(
2950 DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame,
2951 const nsStyleImageLayers::Layer& aLayer, const nsRect& aBackgroundRect,
2952 bool aWillPaintBorder) {
2953 nsCSSRendering::ImageLayerClipState clip;
2954 nsCSSRendering::GetImageLayerClip(
2955 aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect,
2956 aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
2958 if (clip.mHasAdditionalBGClipArea) {
2959 aClipState.ClipContentDescendants(
2960 clip.mAdditionalBGClipArea, clip.mBGClipArea,
2961 clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2962 } else {
2963 aClipState.ClipContentDescendants(
2964 clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2969 * This is used for the find bar highlighter overlay. It's only accessible
2970 * through the AnonymousContent API, so it's not exposed to general web pages.
2972 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
2973 nsIFrame* aFrame,
2974 const nsRect& aBackgroundRect,
2975 nsDisplayList* aList, nscolor aColor) {
2976 nsIContent* content = aFrame->GetContent();
2977 if (!content) {
2978 return false;
2981 void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
2982 if (!cutoutRegion) {
2983 return false;
2986 if (NS_GET_A(aColor) == 0) {
2987 return true;
2990 nsRegion region;
2991 region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
2992 region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
2993 aList->AppendNewToTop<nsDisplaySolidColorRegion>(aBuilder, aFrame, region,
2994 aColor);
2996 return true;
2999 enum class TableType : uint8_t {
3000 Table,
3001 TableCol,
3002 TableColGroup,
3003 TableRow,
3004 TableRowGroup,
3005 TableCell,
3007 MAX,
3010 enum class TableTypeBits : uint8_t { Count = 3 };
3012 static_assert(static_cast<uint8_t>(TableType::MAX) <
3013 (1 << (static_cast<uint8_t>(TableTypeBits::Count) + 1)),
3014 "TableType cannot fit with TableTypeBits::Count");
3015 TableType GetTableTypeFromFrame(nsIFrame* aFrame);
3017 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex,
3018 const TableType aType) {
3019 const uint32_t key = (aIndex << static_cast<uint8_t>(TableTypeBits::Count)) |
3020 static_cast<uint8_t>(aType);
3022 return static_cast<uint16_t>(key);
3025 static nsDisplayBackgroundImage* CreateBackgroundImage(
3026 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3027 const nsDisplayBackgroundImage::InitData& aBgData) {
3028 const auto index = aBgData.layer;
3030 if (aSecondaryFrame) {
3031 const auto tableType = GetTableTypeFromFrame(aFrame);
3032 const uint16_t tableItemIndex = CalculateTablePerFrameKey(index, tableType);
3034 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundImage>(
3035 aBuilder, aSecondaryFrame, tableItemIndex, aBgData, aFrame);
3038 return MakeDisplayItemWithIndex<nsDisplayBackgroundImage>(aBuilder, aFrame,
3039 index, aBgData);
3042 static nsDisplayThemedBackground* CreateThemedBackground(
3043 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3044 const nsRect& aBgRect) {
3045 if (aSecondaryFrame) {
3046 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3047 return MakeDisplayItemWithIndex<nsDisplayTableThemedBackground>(
3048 aBuilder, aSecondaryFrame, index, aBgRect, aFrame);
3051 return MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, aBgRect);
3054 static nsDisplayBackgroundColor* CreateBackgroundColor(
3055 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3056 nsRect& aBgRect, const ComputedStyle* aBgSC, nscolor aColor) {
3057 if (aSecondaryFrame) {
3058 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3059 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>(
3060 aBuilder, aSecondaryFrame, index, aBgRect, aBgSC, aColor, aFrame);
3063 return MakeDisplayItem<nsDisplayBackgroundColor>(aBuilder, aFrame, aBgRect,
3064 aBgSC, aColor);
3067 static void DealWithWindowsAppearanceHacks(nsIFrame* aFrame,
3068 nsDisplayListBuilder* aBuilder) {
3069 const auto& disp = *aFrame->StyleDisplay();
3071 // We use default appearance rather than effective appearance because we want
3072 // to handle when titlebar buttons that have appearance: none.
3073 const auto defaultAppearance = disp.mDefaultAppearance;
3074 if (MOZ_LIKELY(defaultAppearance == StyleAppearance::None)) {
3075 return;
3078 if (auto type = disp.GetWindowButtonType()) {
3079 if (auto* widget = aFrame->GetNearestWidget()) {
3080 auto rect = LayoutDevicePixel::FromAppUnitsToNearest(
3081 nsRect(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize()),
3082 aFrame->PresContext()->AppUnitsPerDevPixel());
3083 widget->SetWindowButtonRect(*type, rect);
3088 /*static*/
3089 AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3090 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3091 const nsRect& aBackgroundRect, nsDisplayList* aList,
3092 bool aAllowWillPaintBorderOptimization, const nsRect& aBackgroundOriginRect,
3093 nsIFrame* aSecondaryReferenceFrame,
3094 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
3095 aAutoBuildingDisplayList) {
3096 MOZ_ASSERT(!aFrame->IsCanvasFrame(),
3097 "We don't expect propagated canvas backgrounds here");
3098 #ifdef DEBUG
3100 nsIFrame* bgFrame = nsCSSRendering::FindBackgroundFrame(aFrame);
3101 MOZ_ASSERT(
3102 !bgFrame || bgFrame == aFrame,
3103 "Should only suppress backgrounds, never propagate to another frame");
3105 #endif
3107 DealWithWindowsAppearanceHacks(aFrame, aBuilder);
3109 const bool isThemed = aFrame->IsThemed();
3111 const ComputedStyle* bgSC = aFrame->Style();
3112 const nsStyleBackground* bg = bgSC->StyleBackground();
3113 const bool needsBackgroundColor =
3114 aBuilder->IsForEventDelivery() ||
3115 (EffectCompositor::HasAnimationsForCompositor(
3116 aFrame, DisplayItemType::TYPE_BACKGROUND_COLOR) &&
3117 !isThemed);
3118 if (!needsBackgroundColor && !isThemed && bg->IsTransparent(bgSC)) {
3119 return AppendedBackgroundType::None;
3122 bool drawBackgroundColor = false;
3123 bool drawBackgroundImage = false;
3124 nscolor color = NS_RGBA(0, 0, 0, 0);
3125 // Don't get background color / images if we propagated our background to the
3126 // canvas (that is, if FindBackgroundFrame is null). But don't early return
3127 // yet, since we might still need a background-color item for hit-testing.
3128 if (!isThemed && nsCSSRendering::FindBackgroundFrame(aFrame)) {
3129 color = nsCSSRendering::DetermineBackgroundColor(
3130 aFrame->PresContext(), bgSC, aFrame, drawBackgroundImage,
3131 drawBackgroundColor);
3134 if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
3135 color)) {
3136 return AppendedBackgroundType::None;
3139 const nsStyleBorder& border = *aFrame->StyleBorder();
3140 const bool willPaintBorder =
3141 aAllowWillPaintBorderOptimization && !isThemed &&
3142 !aFrame->StyleEffects()->HasBoxShadowWithInset(true) &&
3143 border.HasBorder();
3145 auto EnsureBuildingDisplayList = [&] {
3146 if (!aAutoBuildingDisplayList || *aAutoBuildingDisplayList) {
3147 return;
3149 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3150 aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3151 aBuilder->GetVisibleRect() + offset,
3152 aBuilder->GetDirtyRect() + offset);
3155 // An auxiliary list is necessary in case we have background blending; if that
3156 // is the case, background items need to be wrapped by a blend container to
3157 // isolate blending to the background
3158 nsDisplayList bgItemList(aBuilder);
3159 // Even if we don't actually have a background color to paint, we may still
3160 // need to create an item for hit testing and we still need to create an item
3161 // for background-color animations.
3162 if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) ||
3163 needsBackgroundColor) {
3164 EnsureBuildingDisplayList();
3165 Maybe<DisplayListClipState::AutoSaveRestore> clipState;
3166 nsRect bgColorRect = aBackgroundRect;
3167 if (!isThemed && !aBuilder->IsForEventDelivery()) {
3168 // Disable the will-paint-border optimization for background
3169 // colors with no border-radius. Enabling it for background colors
3170 // doesn't help much (there are no tiling issues) and clipping the
3171 // background breaks detection of the element's border-box being
3172 // opaque. For nonzero border-radius we still need it because we
3173 // want to inset the background if possible to avoid antialiasing
3174 // artifacts along the rounded corners.
3175 const bool useWillPaintBorderOptimization =
3176 willPaintBorder &&
3177 nsLayoutUtils::HasNonZeroCorner(border.mBorderRadius);
3179 nsCSSRendering::ImageLayerClipState clip;
3180 nsCSSRendering::GetImageLayerClip(
3181 bg->BottomLayer(), aFrame, border, aBackgroundRect, aBackgroundRect,
3182 useWillPaintBorderOptimization,
3183 aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3185 bgColorRect = bgColorRect.Intersect(clip.mBGClipArea);
3186 if (clip.mHasAdditionalBGClipArea) {
3187 bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea);
3189 if (clip.mHasRoundedCorners) {
3190 clipState.emplace(aBuilder);
3191 clipState->ClipContentDescendants(clip.mBGClipArea, clip.mRadii);
3195 nsDisplayBackgroundColor* bgItem = CreateBackgroundColor(
3196 aBuilder, aFrame, aSecondaryReferenceFrame, bgColorRect, bgSC,
3197 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0));
3199 if (bgItem) {
3200 bgItemList.AppendToTop(bgItem);
3204 if (isThemed) {
3205 nsDisplayThemedBackground* bgItem = CreateThemedBackground(
3206 aBuilder, aFrame, aSecondaryReferenceFrame, aBackgroundRect);
3208 if (bgItem) {
3209 bgItem->Init(aBuilder);
3210 bgItemList.AppendToTop(bgItem);
3213 if (!bgItemList.IsEmpty()) {
3214 aList->AppendToTop(&bgItemList);
3215 return AppendedBackgroundType::ThemedBackground;
3218 return AppendedBackgroundType::None;
3221 if (!drawBackgroundImage) {
3222 if (!bgItemList.IsEmpty()) {
3223 aList->AppendToTop(&bgItemList);
3224 return AppendedBackgroundType::Background;
3227 return AppendedBackgroundType::None;
3230 const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
3232 bool needBlendContainer = false;
3233 const nsRect& bgOriginRect =
3234 aBackgroundOriginRect.IsEmpty() ? aBackgroundRect : aBackgroundOriginRect;
3236 // Passing bg == nullptr in this macro will result in one iteration with
3237 // i = 0.
3238 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
3239 if (bg->mImage.mLayers[i].mImage.IsNone()) {
3240 continue;
3243 EnsureBuildingDisplayList();
3245 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3246 needBlendContainer = true;
3249 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3250 if (!aBuilder->IsForEventDelivery()) {
3251 const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
3252 SetBackgroundClipRegion(clipState, aFrame, layer, aBackgroundRect,
3253 willPaintBorder);
3256 nsDisplayList thisItemList(aBuilder);
3257 nsDisplayBackgroundImage::InitData bgData =
3258 nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect,
3259 bgSC);
3261 if (bgData.shouldFixToViewport) {
3262 auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
3263 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3264 aBuilder, aFrame, aBuilder->GetVisibleRect(),
3265 aBuilder->GetDirtyRect());
3267 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
3268 aBuilder);
3269 if (displayData) {
3270 asrSetter.SetCurrentActiveScrolledRoot(
3271 displayData->mContainingBlockActiveScrolledRoot);
3272 asrSetter.SetCurrentScrollParentId(displayData->mScrollParentId);
3273 if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
3274 // Override the dirty rect on the builder to be the dirty rect of
3275 // the viewport.
3276 // displayData->mDirtyRect is relative to the presshell's viewport
3277 // frame (the root frame), and we need it to be relative to aFrame.
3278 nsIFrame* rootFrame =
3279 aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
3280 // There cannot be any transforms between aFrame and rootFrame
3281 // because then bgData.shouldFixToViewport would have been false.
3282 nsRect visibleRect =
3283 displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame);
3284 aBuilder->SetVisibleRect(visibleRect);
3285 nsRect dirtyRect =
3286 displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
3287 aBuilder->SetDirtyRect(dirtyRect);
3291 nsDisplayBackgroundImage* bgItem = nullptr;
3293 // The clip is captured by the nsDisplayFixedPosition, so clear the
3294 // clip for the nsDisplayBackgroundImage inside.
3295 DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
3296 bgImageClip.Clear();
3297 bgItem = CreateBackgroundImage(aBuilder, aFrame,
3298 aSecondaryReferenceFrame, bgData);
3300 if (bgItem) {
3301 thisItemList.AppendToTop(
3302 nsDisplayFixedPosition::CreateForFixedBackground(
3303 aBuilder, aFrame, aSecondaryReferenceFrame, bgItem, i, asr));
3305 } else { // bgData.shouldFixToViewport == false
3306 nsDisplayBackgroundImage* bgItem = CreateBackgroundImage(
3307 aBuilder, aFrame, aSecondaryReferenceFrame, bgData);
3308 if (bgItem) {
3309 thisItemList.AppendToTop(bgItem);
3313 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3314 // asr is scrolled. Even if we wrap a fixed background layer, that's
3315 // fine, because the item will have a scrolled clip that limits the
3316 // item with respect to asr.
3317 if (aSecondaryReferenceFrame) {
3318 const auto tableType = GetTableTypeFromFrame(aFrame);
3319 const uint16_t index = CalculateTablePerFrameKey(i + 1, tableType);
3321 thisItemList.AppendNewToTopWithIndex<nsDisplayTableBlendMode>(
3322 aBuilder, aSecondaryReferenceFrame, index, &thisItemList,
3323 bg->mImage.mLayers[i].mBlendMode, asr, aFrame, true);
3324 } else {
3325 thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
3326 aBuilder, aFrame, i + 1, &thisItemList,
3327 bg->mImage.mLayers[i].mBlendMode, asr, true);
3330 bgItemList.AppendToTop(&thisItemList);
3333 if (needBlendContainer) {
3334 bgItemList.AppendToTop(
3335 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3336 aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr));
3339 if (!bgItemList.IsEmpty()) {
3340 aList->AppendToTop(&bgItemList);
3341 return AppendedBackgroundType::Background;
3344 return AppendedBackgroundType::None;
3347 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3348 // intersects aRect. Assumes that the unrounded border has already
3349 // been checked for intersection.
3350 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame,
3351 const nsPoint& aFrameToReferenceFrame,
3352 const nsRect& aTestRect) {
3353 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize())
3354 .Intersects(aTestRect)) {
3355 return false;
3358 nscoord radii[8];
3359 return !aFrame->GetBorderRadii(radii) ||
3360 nsLayoutUtils::RoundedRectIntersectsRect(
3361 nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii,
3362 aTestRect);
3365 // Returns TRUE if aContainedRect is guaranteed to be contained in
3366 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3367 // handled conservatively by returning FALSE in some situations where
3368 // a more thorough analysis could return TRUE.
3370 // See also RoundedRectIntersectsRect.
3371 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
3372 const nscoord aRadii[8],
3373 const nsRect& aContainedRect) {
3374 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii,
3375 aContainedRect);
3376 return rgn.Contains(aContainedRect);
3379 bool nsDisplayBackgroundImage::CanApplyOpacity(
3380 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3381 return CanBuildWebRenderDisplayItems(aManager, aBuilder);
3384 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3385 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3386 return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip !=
3387 StyleGeometryBox::Text &&
3388 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3389 aManager, *StyleFrame()->PresContext(), StyleFrame(),
3390 mBackgroundStyle->StyleBackground(), mLayer,
3391 aBuilder->GetBackgroundPaintFlags());
3394 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3395 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3396 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3397 nsDisplayListBuilder* aDisplayListBuilder) {
3398 if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(),
3399 aDisplayListBuilder)) {
3400 return false;
3403 uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
3404 bool dummy;
3405 nsCSSRendering::PaintBGParams params =
3406 nsCSSRendering::PaintBGParams::ForSingleLayer(
3407 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy),
3408 mBackgroundRect, StyleFrame(), paintFlags, mLayer,
3409 CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity());
3410 params.bgClipRect = &mBounds;
3411 ImgDrawResult result =
3412 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3413 params, aBuilder, aResources, aSc, aManager, this);
3414 if (result == ImgDrawResult::NOT_SUPPORTED) {
3415 return false;
3418 if (nsIContent* content = StyleFrame()->GetContent()) {
3419 if (imgRequestProxy* requestProxy = mBackgroundStyle->StyleBackground()
3420 ->mImage.mLayers[mLayer]
3421 .mImage.GetImageRequest()) {
3422 // LCP don't consider gradient backgrounds.
3423 LCPHelpers::FinalizeLCPEntryForImage(content->AsElement(), requestProxy,
3424 mBounds - ToReferenceFrame());
3428 return true;
3431 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
3432 const nsRect& aRect,
3433 HitTestState* aState,
3434 nsTArray<nsIFrame*>* aOutFrames) {
3435 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3436 aOutFrames->AppendElement(mFrame);
3440 static nsRect GetInsideClipRect(const nsDisplayItem* aItem,
3441 StyleGeometryBox aClip, const nsRect& aRect,
3442 const nsRect& aBackgroundRect) {
3443 if (aRect.IsEmpty()) {
3444 return {};
3447 nsIFrame* frame = aItem->Frame();
3449 nsRect clipRect = aBackgroundRect;
3450 if (frame->IsCanvasFrame()) {
3451 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3452 clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
3453 } else if (aClip == StyleGeometryBox::PaddingBox ||
3454 aClip == StyleGeometryBox::ContentBox) {
3455 nsMargin border = frame->GetUsedBorder();
3456 if (aClip == StyleGeometryBox::ContentBox) {
3457 border += frame->GetUsedPadding();
3459 border.ApplySkipSides(frame->GetSkipSides());
3460 clipRect.Deflate(border);
3463 return clipRect.Intersect(aRect);
3466 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
3467 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3468 nsRegion result;
3469 *aSnap = false;
3471 if (!mBackgroundStyle) {
3472 return result;
3475 *aSnap = true;
3477 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3478 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3479 // which expects frames to be sent to it in content order, not reverse
3480 // content order which we'll produce here.
3481 // Of course, if there's only one frame in the flow, it doesn't matter.
3482 if (mFrame->StyleBorder()->mBoxDecorationBreak ==
3483 StyleBoxDecorationBreak::Clone ||
3484 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
3485 const nsStyleImageLayers::Layer& layer =
3486 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3487 if (layer.mImage.IsOpaque() && layer.mBlendMode == StyleBlend::Normal &&
3488 layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
3489 layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
3490 layer.mClip != StyleGeometryBox::Text) {
3491 result = GetInsideClipRect(this, layer.mClip, mBounds, mBackgroundRect);
3495 return result;
3498 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform(
3499 nsDisplayListBuilder* aBuilder) const {
3500 if (!mBackgroundStyle) {
3501 return Some(NS_RGBA(0, 0, 0, 0));
3503 return Nothing();
3506 nsRect nsDisplayBackgroundImage::GetPositioningArea() const {
3507 if (!mBackgroundStyle) {
3508 return nsRect();
3510 nsIFrame* attachedToFrame;
3511 bool transformedFixed;
3512 return nsCSSRendering::ComputeImageLayerPositioningArea(
3513 mFrame->PresContext(), mFrame, mBackgroundRect,
3514 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer],
3515 &attachedToFrame, &transformedFixed) +
3516 ToReferenceFrame();
3519 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3520 const {
3521 if (!mBackgroundStyle) {
3522 return false;
3525 nscoord radii[8];
3526 if (mFrame->GetBorderRadii(radii)) {
3527 // A change in the size of the positioning area might change the position
3528 // of the rounded corners.
3529 return true;
3532 const nsStyleImageLayers::Layer& layer =
3533 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3534 return layer.RenderingMightDependOnPositioningAreaSizeChange();
3537 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
3538 gfxContext* aCtx) {
3539 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &mBounds);
3542 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
3543 gfxContext* aCtx,
3544 const nsRect& aBounds,
3545 nsRect* aClipRect) {
3546 gfxContext* ctx = aCtx;
3547 StyleGeometryBox clip =
3548 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip;
3550 if (clip == StyleGeometryBox::Text) {
3551 if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect,
3552 aBuilder)) {
3553 return;
3557 nsCSSRendering::PaintBGParams params =
3558 nsCSSRendering::PaintBGParams::ForSingleLayer(
3559 *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
3560 aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER,
3561 1.0f);
3562 params.bgClipRect = aClipRect;
3563 Unused << nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
3565 if (clip == StyleGeometryBox::Text) {
3566 ctx->PopGroupAndBlend();
3570 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3571 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3572 nsRegion* aInvalidRegion) const {
3573 if (!mBackgroundStyle) {
3574 return;
3577 const auto* geometry =
3578 static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
3580 bool snap;
3581 nsRect bounds = GetBounds(aBuilder, &snap);
3582 nsRect positioningArea = GetPositioningArea();
3583 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
3584 (positioningArea.Size() != geometry->mPositioningArea.Size() &&
3585 RenderingMightDependOnPositioningAreaSizeChange())) {
3586 // Positioning area changed in a way that could cause everything to change,
3587 // so invalidate everything (both old and new painting areas).
3588 aInvalidRegion->Or(bounds, geometry->mBounds);
3589 return;
3591 if (!mDestRect.IsEqualInterior(geometry->mDestRect)) {
3592 // Dest area changed in a way that could cause everything to change,
3593 // so invalidate everything (both old and new painting areas).
3594 aInvalidRegion->Or(bounds, geometry->mBounds);
3595 return;
3597 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3598 // Positioning area is unchanged, so invalidate just the change in the
3599 // painting area.
3600 aInvalidRegion->Xor(bounds, geometry->mBounds);
3604 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder,
3605 bool* aSnap) const {
3606 *aSnap = true;
3607 return mBounds;
3610 nsRect nsDisplayBackgroundImage::GetBoundsInternal(
3611 nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) {
3612 // This allows nsDisplayTableBackgroundImage to change the frame used for
3613 // bounds calculation.
3614 nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame;
3616 nsPresContext* presContext = frame->PresContext();
3618 if (!mBackgroundStyle) {
3619 return nsRect();
3622 nsRect clipRect = mBackgroundRect;
3623 if (frame->IsCanvasFrame()) {
3624 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3625 clipRect = canvasFrame->CanvasArea() + ToReferenceFrame();
3627 const nsStyleImageLayers::Layer& layer =
3628 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3629 return nsCSSRendering::GetBackgroundLayerRect(
3630 presContext, frame, mBackgroundRect, clipRect, layer,
3631 aBuilder->GetBackgroundPaintFlags());
3634 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3635 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aData,
3636 nsIFrame* aCellFrame)
3637 : nsDisplayBackgroundImage(aBuilder, aFrame, aData, aCellFrame),
3638 mStyleFrame(aCellFrame) {
3639 if (aBuilder->IsRetainingDisplayList()) {
3640 mStyleFrame->AddDisplayItem(this);
3644 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3645 if (mStyleFrame) {
3646 mStyleFrame->RemoveDisplayItem(this);
3650 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const {
3651 bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
3652 aRect += ToReferenceFrame();
3653 return result;
3656 nsDisplayThemedBackground::nsDisplayThemedBackground(
3657 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3658 const nsRect& aBackgroundRect)
3659 : nsPaintedDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) {
3660 MOZ_COUNT_CTOR(nsDisplayThemedBackground);
3663 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) {
3664 const nsStyleDisplay* disp = StyleFrame()->StyleDisplay();
3665 mAppearance = disp->EffectiveAppearance();
3666 StyleFrame()->IsThemed(disp, &mThemeTransparency);
3668 // Perform necessary RegisterThemeGeometry
3669 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3670 nsITheme::ThemeGeometryType type =
3671 theme->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance);
3672 if (type != nsITheme::eThemeGeometryTypeUnknown) {
3673 RegisterThemeGeometry(aBuilder, this, StyleFrame(), type);
3676 mBounds = GetBoundsInternal();
3679 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) {
3680 aStream << " (themed, appearance:" << (int)mAppearance << ")";
3683 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
3684 const nsRect& aRect,
3685 HitTestState* aState,
3686 nsTArray<nsIFrame*>* aOutFrames) {
3687 // Assume that any point in our background rect is a hit.
3688 if (mBackgroundRect.Intersects(aRect)) {
3689 aOutFrames->AppendElement(mFrame);
3693 nsRegion nsDisplayThemedBackground::GetOpaqueRegion(
3694 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3695 nsRegion result;
3696 *aSnap = false;
3698 if (mThemeTransparency == nsITheme::eOpaque) {
3699 *aSnap = true;
3700 result = mBackgroundRect;
3702 return result;
3705 Maybe<nscolor> nsDisplayThemedBackground::IsUniform(
3706 nsDisplayListBuilder* aBuilder) const {
3707 return Nothing();
3710 nsRect nsDisplayThemedBackground::GetPositioningArea() const {
3711 return mBackgroundRect;
3714 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
3715 gfxContext* aCtx) {
3716 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), nullptr);
3719 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
3720 gfxContext* aCtx,
3721 const nsRect& aBounds,
3722 nsRect* aClipRect) {
3723 // XXXzw this ignores aClipRect.
3724 nsPresContext* presContext = StyleFrame()->PresContext();
3725 nsITheme* theme = presContext->Theme();
3726 nsRect drawing(mBackgroundRect);
3727 theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(),
3728 mAppearance, &drawing);
3729 drawing.IntersectRect(drawing, aBounds);
3730 theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect,
3731 drawing);
3734 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3735 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3736 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3737 nsDisplayListBuilder* aDisplayListBuilder) {
3738 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3739 return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc,
3740 aManager, StyleFrame(),
3741 mAppearance, mBackgroundRect);
3744 bool nsDisplayThemedBackground::IsWindowActive() const {
3745 return !mFrame->PresContext()->Document()->IsTopLevelWindowInactive();
3748 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3749 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3750 nsRegion* aInvalidRegion) const {
3751 const auto* geometry =
3752 static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
3754 bool snap;
3755 nsRect bounds = GetBounds(aBuilder, &snap);
3756 nsRect positioningArea = GetPositioningArea();
3757 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
3758 // Invalidate everything (both old and new painting areas).
3759 aInvalidRegion->Or(bounds, geometry->mBounds);
3760 return;
3762 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3763 // Positioning area is unchanged, so invalidate just the change in the
3764 // painting area.
3765 aInvalidRegion->Xor(bounds, geometry->mBounds);
3767 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3768 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
3769 IsWindowActive() != geometry->mWindowIsActive) {
3770 aInvalidRegion->Or(*aInvalidRegion, bounds);
3774 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder,
3775 bool* aSnap) const {
3776 *aSnap = true;
3777 return mBounds;
3780 nsRect nsDisplayThemedBackground::GetBoundsInternal() {
3781 nsPresContext* presContext = mFrame->PresContext();
3783 nsRect r = mBackgroundRect - ToReferenceFrame();
3784 presContext->Theme()->GetWidgetOverflow(
3785 presContext->DeviceContext(), mFrame,
3786 mFrame->StyleDisplay()->EffectiveAppearance(), &r);
3787 return r + ToReferenceFrame();
3790 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
3791 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder,
3792 gfxContext* aCtx) {
3793 mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(),
3794 mFrame, ToReferenceFrame(), mColor);
3796 #endif
3798 bool nsDisplayBackgroundColor::CanApplyOpacity(
3799 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3800 // Don't apply opacity if the background color is animated since the color is
3801 // going to be changed on the compositor.
3802 return !EffectCompositor::HasAnimationsForCompositor(
3803 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR);
3806 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3807 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3808 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3809 nsDisplayListBuilder* aDisplayListBuilder) {
3810 gfx::sRGBColor color = mColor;
3811 color.a *= aBuilder.GetInheritedOpacity();
3813 if (color == sRGBColor() &&
3814 !EffectCompositor::HasAnimationsForCompositor(
3815 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3816 return true;
3819 if (HasBackgroundClipText()) {
3820 return false;
3823 uint64_t animationsId = 0;
3824 // We don't support background-color animations on table elements yet.
3825 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
3826 animationsId =
3827 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
3830 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
3831 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3832 wr::LayoutRect r = wr::ToLayoutRect(bounds);
3834 if (animationsId) {
3835 wr::WrAnimationProperty prop{
3836 wr::WrAnimationType::BackgroundColor,
3837 animationsId,
3839 aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(),
3840 wr::ToColorF(ToDeviceColor(color)), &prop);
3841 } else {
3842 aBuilder.StartGroup(this);
3843 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
3844 wr::ToColorF(ToDeviceColor(color)));
3845 aBuilder.FinishGroup();
3848 return true;
3851 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder,
3852 gfxContext* aCtx,
3853 const DisplayItemClip& aClip) {
3854 MOZ_ASSERT(!HasBackgroundClipText());
3856 if (mColor == sRGBColor()) {
3857 return;
3860 nsRect fillRect = mBackgroundRect;
3861 if (aClip.HasClip()) {
3862 fillRect.IntersectRect(fillRect, aClip.GetClipRect());
3865 DrawTarget* dt = aCtx->GetDrawTarget();
3866 int32_t A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
3867 Rect bounds = ToRect(nsLayoutUtils::RectToGfxRect(fillRect, A2D));
3868 MaybeSnapToDevicePixels(bounds, *dt);
3869 ColorPattern fill(ToDeviceColor(mColor));
3871 if (aClip.GetRoundedRectCount()) {
3872 MOZ_ASSERT(aClip.GetRoundedRectCount() == 1);
3874 AutoTArray<DisplayItemClip::RoundedRect, 1> roundedRect;
3875 aClip.AppendRoundedRects(&roundedRect);
3877 bool pushedClip = false;
3878 if (!fillRect.Contains(roundedRect[0].mRect)) {
3879 dt->PushClipRect(bounds);
3880 pushedClip = true;
3883 RectCornerRadii pixelRadii;
3884 nsCSSRendering::ComputePixelRadii(roundedRect[0].mRadii, A2D, &pixelRadii);
3885 dt->FillRoundedRect(
3886 RoundedRect(NSRectToSnappedRect(roundedRect[0].mRect, A2D, *dt),
3887 pixelRadii),
3888 fill);
3889 if (pushedClip) {
3890 dt->PopClip();
3892 } else {
3893 dt->FillRect(bounds, fill);
3897 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
3898 gfxContext* aCtx) {
3899 if (mColor == sRGBColor()) {
3900 return;
3903 #if 0
3904 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3905 // results in a precision induced rounding issue that makes the rect one
3906 // pixel shorter in rare cases. Disabled in favor of the old code for now.
3907 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3908 // reproduce the bug.
3910 // TODO:
3911 // This new path does not include support for background-clip:text; need to
3912 // be fixed if/when we switch to this new code path.
3914 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
3916 Rect rect = NSRectToSnappedRect(mBackgroundRect,
3917 mFrame->PresContext()->AppUnitsPerDevPixel(),
3918 aDrawTarget);
3919 ColorPattern color(ToDeviceColor(mColor));
3920 aDrawTarget.FillRect(rect, color);
3921 #else
3922 gfxContext* ctx = aCtx;
3923 gfxRect bounds = nsLayoutUtils::RectToGfxRect(
3924 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3926 if (HasBackgroundClipText()) {
3927 if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
3928 return;
3931 ctx->SetColor(mColor);
3932 ctx->NewPath();
3933 ctx->SnappedRectangle(bounds);
3934 ctx->Fill();
3935 ctx->PopGroupAndBlend();
3936 return;
3939 ctx->SetColor(mColor);
3940 ctx->NewPath();
3941 ctx->SnappedRectangle(bounds);
3942 ctx->Fill();
3943 #endif
3946 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion(
3947 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3948 *aSnap = false;
3950 if (mColor.a != 1 ||
3951 // Even if the current alpha channel is 1, we treat this item as if it's
3952 // non-opaque if there is a background-color animation since the animation
3953 // might change the alpha channel.
3954 EffectCompositor::HasAnimationsForCompositor(
3955 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3956 return nsRegion();
3959 if (!mHasStyle || HasBackgroundClipText()) {
3960 return nsRegion();
3963 *aSnap = true;
3964 return GetInsideClipRect(this, mBottomLayerClip, mBackgroundRect,
3965 mBackgroundRect);
3968 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform(
3969 nsDisplayListBuilder* aBuilder) const {
3970 return Some(mColor.ToABGR());
3973 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
3974 const nsRect& aRect,
3975 HitTestState* aState,
3976 nsTArray<nsIFrame*>* aOutFrames) {
3977 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3978 // aRect doesn't intersect our border-radius curve.
3979 return;
3982 aOutFrames->AppendElement(mFrame);
3985 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) {
3986 aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << ","
3987 << mColor.a << ")";
3988 aStream << " backgroundRect" << mBackgroundRect;
3991 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
3992 bool* aSnap) const {
3993 *aSnap = false;
3994 return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
3997 nsRect nsDisplayOutline::GetInnerRect() const {
3998 if (nsRect* savedOutlineInnerRect =
3999 mFrame->GetProperty(nsIFrame::OutlineInnerRectProperty())) {
4000 return *savedOutlineInnerRect;
4002 return mFrame->GetRectRelativeToSelf();
4005 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4006 // TODO join outlines together
4007 MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(),
4008 "Should have not created a nsDisplayOutline!");
4010 nsRect rect = GetInnerRect() + ToReferenceFrame();
4011 nsPresContext* pc = mFrame->PresContext();
4012 if (IsThemedOutline()) {
4013 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
4014 pc->Theme()->DrawWidgetBackground(aCtx, mFrame,
4015 StyleAppearance::FocusOutline, rect,
4016 GetPaintRect(aBuilder, aCtx));
4017 return;
4020 nsCSSRendering::PaintNonThemedOutline(
4021 pc, *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), rect, mFrame->Style());
4024 bool nsDisplayOutline::IsThemedOutline() const {
4025 #ifdef DEBUG
4026 nsPresContext* pc = mFrame->PresContext();
4027 MOZ_ASSERT(
4028 pc->Theme()->ThemeSupportsWidget(pc, mFrame,
4029 StyleAppearance::FocusOutline),
4030 "All of our supported platforms have support for themed focus-outlines");
4031 #endif
4032 return mFrame->StyleOutline()->mOutlineStyle.IsAuto();
4035 bool nsDisplayOutline::CreateWebRenderCommands(
4036 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4037 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4038 nsDisplayListBuilder* aDisplayListBuilder) {
4039 nsPresContext* pc = mFrame->PresContext();
4040 nsRect rect = GetInnerRect() + ToReferenceFrame();
4041 if (IsThemedOutline()) {
4042 rect.Inflate(mFrame->StyleOutline()->EffectiveOffsetFor(rect));
4043 return pc->Theme()->CreateWebRenderCommandsForWidget(
4044 aBuilder, aResources, aSc, aManager, mFrame,
4045 StyleAppearance::FocusOutline, rect);
4048 bool dummy;
4049 Maybe<nsCSSBorderRenderer> borderRenderer =
4050 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4051 pc, /* aDrawTarget = */ nullptr, mFrame,
4052 GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style());
4054 if (!borderRenderer) {
4055 // No border renderer means "there is no outline".
4056 // Paint nothing and return success.
4057 return true;
4060 borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
4061 return true;
4064 bool nsDisplayOutline::HasRadius() const {
4065 const auto& radius = mFrame->StyleBorder()->mBorderRadius;
4066 return !nsLayoutUtils::HasNonZeroCorner(radius);
4069 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const {
4070 const nsStyleOutline* outline = mFrame->StyleOutline();
4071 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
4072 if (borderBox.Contains(aRect) && !HasRadius() &&
4073 outline->mOutlineOffset.ToCSSPixels() >= 0.0f) {
4074 // aRect is entirely inside the border-rect, and the outline isn't rendered
4075 // inside the border-rect, so the outline is not visible.
4076 return true;
4078 return false;
4081 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
4082 const nsRect& aRect, HitTestState* aState,
4083 nsTArray<nsIFrame*>* aOutFrames) {
4084 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4085 // aRect doesn't intersect our border-radius curve.
4086 return;
4089 aOutFrames->AppendElement(mFrame);
4092 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4093 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4094 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4095 nsDisplayListBuilder* aDisplayListBuilder) {
4096 return true;
4099 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4100 return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
4103 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) {
4104 mOverrideZIndex = Some(aZIndex);
4107 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
4108 nsIFrame* aCaretFrame)
4109 : nsPaintedDisplayItem(aBuilder, aCaretFrame),
4110 mCaret(aBuilder->GetCaret()),
4111 mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) {
4112 MOZ_COUNT_CTOR(nsDisplayCaret);
4113 // The presence of a caret doesn't change the overflow rect
4114 // of the owning frame, so the normal building rect might not
4115 // include the caret at all. We use MarkFrameForDisplay to ensure
4116 // we build this item, and here we override the building rect
4117 // to cover the pixels we're going to draw.
4118 SetBuildingRect(mBounds);
4121 #ifdef NS_BUILD_REFCNT_LOGGING
4122 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret); }
4123 #endif
4125 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder,
4126 bool* aSnap) const {
4127 *aSnap = true;
4128 // The caret returns a rect in the coordinates of mFrame.
4129 return mBounds;
4132 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4133 // Note: Because we exist, we know that the caret is visible, so we don't
4134 // need to check for the caret's visibility.
4135 mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
4138 bool nsDisplayCaret::CreateWebRenderCommands(
4139 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4140 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4141 nsDisplayListBuilder* aDisplayListBuilder) {
4142 using namespace layers;
4143 nsRect caretRect;
4144 nsRect hookRect;
4145 nscolor caretColor;
4146 nsIFrame* frame =
4147 mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor);
4148 if (NS_WARN_IF(!frame) || NS_WARN_IF(frame != mFrame)) {
4149 return true;
4152 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
4153 gfx::DeviceColor color = ToDeviceColor(caretColor);
4154 LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
4155 caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
4156 LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
4157 hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
4159 wr::LayoutRect caret = wr::ToLayoutRect(devCaretRect);
4160 wr::LayoutRect hook = wr::ToLayoutRect(devHookRect);
4162 // Note, WR will pixel snap anything that is layout aligned.
4163 aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), false, false,
4164 wr::ToColorF(color));
4166 if (!devHookRect.IsEmpty()) {
4167 aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), false, false,
4168 wr::ToColorF(color));
4170 return true;
4173 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder,
4174 nsIFrame* aFrame)
4175 : nsPaintedDisplayItem(aBuilder, aFrame) {
4176 MOZ_COUNT_CTOR(nsDisplayBorder);
4178 mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
4181 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const {
4182 nsRect paddingRect = GetPaddingRect();
4183 const nsStyleBorder* styleBorder;
4184 if (paddingRect.Contains(aRect) &&
4185 !(styleBorder = mFrame->StyleBorder())->IsBorderImageSizeAvailable() &&
4186 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
4187 // aRect is entirely inside the content rect, and no part
4188 // of the border is rendered inside the content rect, so we are not
4189 // visible
4190 // Skip this if there's a border-image (which draws a background
4191 // too) or if there is a border-radius (which makes the border draw
4192 // further in).
4193 return true;
4196 return false;
4199 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry(
4200 nsDisplayListBuilder* aBuilder) {
4201 return new nsDisplayBorderGeometry(this, aBuilder);
4204 void nsDisplayBorder::ComputeInvalidationRegion(
4205 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4206 nsRegion* aInvalidRegion) const {
4207 const auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
4208 bool snap;
4210 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
4211 // We can probably get away with only invalidating the difference
4212 // between the border and padding rects, but the XUL ui at least
4213 // is apparently painting a background with this?
4214 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4218 bool nsDisplayBorder::CreateWebRenderCommands(
4219 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4220 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4221 nsDisplayListBuilder* aDisplayListBuilder) {
4222 nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
4224 ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder(
4225 this, mFrame, rect, aBuilder, aResources, aSc, aManager,
4226 aDisplayListBuilder);
4228 if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
4229 return false;
4231 return true;
4234 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4235 nsPoint offset = ToReferenceFrame();
4237 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
4238 ? PaintBorderFlags::SyncDecodeImages
4239 : PaintBorderFlags();
4241 Unused << nsCSSRendering::PaintBorder(
4242 mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
4243 nsRect(offset, mFrame->GetSize()), mFrame->Style(), flags,
4244 mFrame->GetSkipSides());
4247 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder,
4248 bool* aSnap) const {
4249 *aSnap = true;
4250 return mBounds;
4253 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
4254 gfxContext* aCtx) {
4255 nsPoint offset = ToReferenceFrame();
4256 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4257 nsPresContext* presContext = mFrame->PresContext();
4259 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
4261 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
4262 GetPaintRect(aBuilder, aCtx), 1.0f);
4265 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
4266 bool* aSnap) const {
4267 *aSnap = false;
4268 return mBounds;
4271 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() {
4272 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
4273 ToReferenceFrame();
4276 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const {
4277 nsPoint origin = ToReferenceFrame();
4278 nsRect frameRect(origin, mFrame->GetSize());
4279 if (!frameRect.Contains(aRect)) {
4280 return false;
4283 // the visible region is entirely inside the border-rect, and box shadows
4284 // never render within the border-rect (unless there's a border radius).
4285 nscoord twipsRadii[8];
4286 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
4287 if (!hasBorderRadii) {
4288 return true;
4291 return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
4294 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4295 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4296 if (shadows.IsEmpty()) {
4297 return false;
4300 bool hasBorderRadius;
4301 // We don't support native themed things yet like box shadows around
4302 // input buttons.
4304 // TODO(emilio): The non-native theme could provide the right rect+radius
4305 // instead relatively painlessly, if we find this causes performance issues or
4306 // what not.
4307 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4310 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4311 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4312 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4313 nsDisplayListBuilder* aDisplayListBuilder) {
4314 if (!CanBuildWebRenderDisplayItems()) {
4315 return false;
4318 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4319 nsPoint offset = ToReferenceFrame();
4320 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4321 bool snap;
4322 nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
4324 bool hasBorderRadius;
4325 bool nativeTheme =
4326 nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4328 // Don't need the full size of the shadow rect like we do in
4329 // nsCSSRendering since WR takes care of calculations for blur
4330 // and spread radius.
4331 nsRect frameRect =
4332 nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
4334 RectCornerRadii borderRadii;
4335 if (hasBorderRadius) {
4336 hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
4337 mFrame, borderRadii);
4340 // Everything here is in app units, change to device units.
4341 LayoutDeviceRect clipRect =
4342 LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel);
4343 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4344 MOZ_ASSERT(!shadows.IsEmpty());
4346 for (const auto& shadow : Reversed(shadows)) {
4347 if (shadow.inset) {
4348 continue;
4351 float blurRadius =
4352 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4353 gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor(
4354 shadow.base, mFrame, aBuilder.GetInheritedOpacity());
4356 // We don't move the shadow rect here since WR does it for us
4357 // Now translate everything to device pixels.
4358 const nsRect& shadowRect = frameRect;
4359 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4360 nsPoint(shadow.base.horizontal.ToAppUnits(),
4361 shadow.base.vertical.ToAppUnits()),
4362 appUnitsPerDevPixel);
4364 LayoutDeviceRect deviceBox =
4365 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4366 wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
4367 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4369 LayoutDeviceSize zeroSize;
4370 wr::BorderRadius borderRadius =
4371 wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
4372 if (hasBorderRadius) {
4373 borderRadius = wr::ToBorderRadius(
4374 LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
4375 LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
4376 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
4377 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
4380 float spreadRadius =
4381 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4383 aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
4384 deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
4385 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius,
4386 spreadRadius, borderRadius,
4387 wr::BoxShadowClipMode::Outset);
4390 return true;
4393 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4394 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4395 nsRegion* aInvalidRegion) const {
4396 const auto* geometry =
4397 static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
4398 bool snap;
4399 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
4400 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
4401 nsRegion oldShadow, newShadow;
4402 nscoord dontCare[8];
4403 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
4404 if (hasBorderRadius) {
4405 // If we have rounded corners then we need to invalidate the frame area
4406 // too since we paint into it.
4407 oldShadow = geometry->mBounds;
4408 newShadow = GetBounds(aBuilder, &snap);
4409 } else {
4410 oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
4411 newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
4413 aInvalidRegion->Or(oldShadow, newShadow);
4417 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
4418 gfxContext* aCtx) {
4419 nsPoint offset = ToReferenceFrame();
4420 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4421 nsPresContext* presContext = mFrame->PresContext();
4423 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS);
4425 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
4428 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4429 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4430 const nsPoint& aReferenceOffset) {
4431 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4432 if (shadows.IsEmpty()) {
4433 // Means we don't have to paint anything
4434 return true;
4437 bool hasBorderRadius;
4438 bool nativeTheme =
4439 nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
4441 // We don't support native themed things yet like box shadows around
4442 // input buttons.
4443 return !nativeTheme;
4446 /* static */
4447 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4448 wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
4449 nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect) {
4450 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) {
4451 return;
4454 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
4456 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4458 LayoutDeviceRect clipRect =
4459 LayoutDeviceRect::FromAppUnits(aVisibleRect, appUnitsPerDevPixel);
4461 for (const auto& shadow : Reversed(shadows)) {
4462 if (!shadow.inset) {
4463 continue;
4466 nsRect shadowRect =
4467 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
4468 RectCornerRadii innerRadii;
4469 nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
4471 // Now translate everything to device pixels.
4472 LayoutDeviceRect deviceBoxRect =
4473 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4474 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4475 sRGBColor shadowColor =
4476 nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0);
4478 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4479 nsPoint(shadow.base.horizontal.ToAppUnits(),
4480 shadow.base.vertical.ToAppUnits()),
4481 appUnitsPerDevPixel);
4483 float blurRadius =
4484 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4486 wr::BorderRadius borderRadius = wr::ToBorderRadius(
4487 LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
4488 LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
4489 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
4490 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
4491 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4492 float spreadRadius =
4493 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4495 aBuilder.PushBoxShadow(
4496 wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
4497 !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
4498 wr::ToLayoutVector2D(shadowOffset),
4499 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, spreadRadius,
4500 borderRadius, wr::BoxShadowClipMode::Inset);
4504 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4505 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4506 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4507 nsDisplayListBuilder* aDisplayListBuilder) {
4508 if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame,
4509 ToReferenceFrame())) {
4510 return false;
4513 bool snap;
4514 nsRect visible = GetBounds(aDisplayListBuilder, &snap);
4515 nsPoint offset = ToReferenceFrame();
4516 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4517 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4518 aBuilder, aSc, visible, mFrame, borderRect);
4520 return true;
4523 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4524 nsIFrame* aFrame, nsDisplayList* aList)
4525 : nsDisplayWrapList(aBuilder, aFrame, aList,
4526 aBuilder->CurrentActiveScrolledRoot(), false) {}
4528 nsDisplayWrapList::nsDisplayWrapList(
4529 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4530 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
4531 : nsPaintedDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
4532 mList(aBuilder),
4533 mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
4534 mOverrideZIndex(0),
4535 mHasZIndexOverride(false),
4536 mClearingClipChain(aClearClipChain) {
4537 MOZ_COUNT_CTOR(nsDisplayWrapList);
4539 mBaseBuildingRect = GetBuildingRect();
4541 mListPtr = &mList;
4542 mListPtr->AppendToTop(aList);
4543 mOriginalClipChain = mClipChain;
4544 nsDisplayWrapList::UpdateBounds(aBuilder);
4547 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4548 nsIFrame* aFrame, nsDisplayItem* aItem)
4549 : nsPaintedDisplayItem(aBuilder, aFrame,
4550 aBuilder->CurrentActiveScrolledRoot()),
4551 mList(aBuilder),
4552 mOverrideZIndex(0),
4553 mHasZIndexOverride(false) {
4554 MOZ_COUNT_CTOR(nsDisplayWrapList);
4556 mBaseBuildingRect = GetBuildingRect();
4558 mListPtr = &mList;
4559 mListPtr->AppendToTop(aItem);
4560 mOriginalClipChain = mClipChain;
4561 nsDisplayWrapList::UpdateBounds(aBuilder);
4563 if (!aFrame || !aFrame->IsTransformed()) {
4564 return;
4567 // See the previous nsDisplayWrapList constructor
4568 if (aItem->Frame() == aFrame) {
4569 mToReferenceFrame = aItem->ToReferenceFrame();
4572 nsRect visible = aBuilder->GetVisibleRect() +
4573 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
4575 SetBuildingRect(visible);
4578 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList); }
4580 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder,
4581 const nsRect& aRect, HitTestState* aState,
4582 nsTArray<nsIFrame*>* aOutFrames) {
4583 mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames);
4586 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder,
4587 bool* aSnap) const {
4588 *aSnap = false;
4589 return mBounds;
4592 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4593 bool* aSnap) const {
4594 *aSnap = false;
4595 bool snap;
4596 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
4597 GetBounds(aBuilder, &snap));
4600 Maybe<nscolor> nsDisplayWrapList::IsUniform(
4601 nsDisplayListBuilder* aBuilder) const {
4602 // We could try to do something but let's conservatively just return Nothing.
4603 return Nothing();
4606 void nsDisplayWrapper::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4607 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4610 nsRect nsDisplayWrapList::GetComponentAlphaBounds(
4611 nsDisplayListBuilder* aBuilder) const {
4612 return mListPtr->GetComponentAlphaBounds(aBuilder);
4615 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4616 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4617 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4618 nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList) {
4619 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4620 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
4621 aNewClipList);
4622 return true;
4625 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder,
4626 nsIFrame* aFrame, nsDisplayList* aList,
4627 nsDisplayItemWrapper* aWrapper) {
4628 if (!aList->GetTop()) {
4629 return NS_OK;
4631 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
4632 if (!item) {
4633 return NS_ERROR_OUT_OF_MEMORY;
4635 // aList was emptied
4636 aList->AppendToTop(item);
4637 return NS_OK;
4640 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
4641 nsDisplayList* aList,
4642 nsDisplayItemWrapper* aWrapper) {
4643 for (nsDisplayItem* item : aList->TakeItems()) {
4644 item = aWrapper->WrapItem(aBuilder, item);
4645 if (!item) {
4646 return NS_ERROR_OUT_OF_MEMORY;
4648 aList->AppendToTop(item);
4650 // aList was emptied
4651 return NS_OK;
4654 nsresult nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
4655 nsIFrame* aFrame,
4656 const nsDisplayListSet& aIn,
4657 const nsDisplayListSet& aOut) {
4658 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
4659 NS_ENSURE_SUCCESS(rv, rv);
4661 if (&aOut == &aIn) {
4662 return NS_OK;
4664 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
4665 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
4666 aOut.Floats()->AppendToTop(aIn.Floats());
4667 aOut.Content()->AppendToTop(aIn.Content());
4668 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
4669 aOut.Outlines()->AppendToTop(aIn.Outlines());
4670 return NS_OK;
4673 nsresult nsDisplayItemWrapper::WrapListsInPlace(
4674 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4675 const nsDisplayListSet& aLists) {
4676 nsresult rv;
4677 if (WrapBorderBackground()) {
4678 // Our border-backgrounds are in-flow
4679 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
4680 NS_ENSURE_SUCCESS(rv, rv);
4682 // Our block border-backgrounds are in-flow
4683 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
4684 NS_ENSURE_SUCCESS(rv, rv);
4685 // The floats are not in flow
4686 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
4687 NS_ENSURE_SUCCESS(rv, rv);
4688 // Our child content is in flow
4689 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
4690 NS_ENSURE_SUCCESS(rv, rv);
4691 // The positioned descendants may not be in-flow
4692 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
4693 NS_ENSURE_SUCCESS(rv, rv);
4694 // The outlines may not be in-flow
4695 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
4698 nsDisplayOpacity::nsDisplayOpacity(
4699 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4700 const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsOnly,
4701 bool aNeedsActiveLayer, bool aWrapsBackdropFilter)
4702 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4703 mOpacity(aFrame->StyleEffects()->mOpacity),
4704 mForEventsOnly(aForEventsOnly),
4705 mNeedsActiveLayer(aNeedsActiveLayer),
4706 mChildOpacityState(ChildOpacityState::Unknown),
4707 mWrapsBackdropFilter(aWrapsBackdropFilter) {
4708 MOZ_COUNT_CTOR(nsDisplayOpacity);
4711 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder,
4712 const nsRect& aRect,
4713 nsDisplayItem::HitTestState* aState,
4714 nsTArray<nsIFrame*>* aOutFrames) {
4715 AutoRestore<float> opacity(aState->mCurrentOpacity);
4716 aState->mCurrentOpacity *= mOpacity;
4718 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4719 // only consider fully opaque items? Or make this configurable somehow?
4720 if (aBuilder->HitTestIsForVisibility() && mOpacity == 0.0f) {
4721 return;
4723 nsDisplayWrapList::HitTest(aBuilder, aRect, aState, aOutFrames);
4726 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4727 bool* aSnap) const {
4728 *aSnap = false;
4729 // The only time where mOpacity == 1.0 should be when we have will-change.
4730 // We could report this as opaque then but when the will-change value starts
4731 // animating the element would become non opaque and could cause repaints.
4732 return nsRegion();
4735 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4736 if (GetOpacity() == 0.0f) {
4737 return;
4740 if (GetOpacity() == 1.0f) {
4741 GetChildren()->Paint(aBuilder, aCtx,
4742 mFrame->PresContext()->AppUnitsPerDevPixel());
4743 return;
4746 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4747 // allocation.
4748 aCtx->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4749 GetChildren()->Paint(aBuilder, aCtx,
4750 mFrame->PresContext()->AppUnitsPerDevPixel());
4751 aCtx->GetDrawTarget()->PopLayer();
4754 /* static */
4755 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
4756 nsIFrame* aFrame) {
4757 return EffectCompositor::HasAnimationsForCompositor(
4758 aFrame, DisplayItemType::TYPE_OPACITY) ||
4759 (ActiveLayerTracker::IsStyleAnimated(
4760 aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()));
4763 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager,
4764 nsDisplayListBuilder* aBuilder) const {
4765 return !EffectCompositor::HasAnimationsForCompositor(
4766 mFrame, DisplayItemType::TYPE_OPACITY);
4769 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4770 // children that don't overlap and can all apply the opacity to themselves.
4771 static const size_t kOpacityMaxChildCount = 3;
4773 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4774 // are likely have more child items than |kOpacityMaxChildCount|.
4775 static const size_t kOpacityMaxListSize = kOpacityMaxChildCount * 2;
4778 * Recursively iterates through |aList| and collects at most
4779 * |kOpacityMaxChildCount| display item pointers to items that return true for
4780 * CanApplyOpacity(). The item pointers are added to |aArray|.
4782 * LayerEventRegions and WrapList items are ignored.
4784 * We need to do this recursively, because the child display items might contain
4785 * nested nsDisplayWrapLists.
4787 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4788 * item that returns false for CanApplyOpacity() is encountered.
4789 * Otherwise returns true.
4791 static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager,
4792 nsDisplayListBuilder* aBuilder,
4793 nsDisplayList* aList,
4794 nsTArray<nsPaintedDisplayItem*>& aArray) {
4795 if (aList->Length() > kOpacityMaxListSize) {
4796 // Exit early, since |aList| will likely contain more than
4797 // |kOpacityMaxChildCount| items.
4798 return false;
4801 for (nsDisplayItem* i : *aList) {
4802 const DisplayItemType type = i->GetType();
4804 if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
4805 continue;
4808 // Descend only into wraplists.
4809 if (type == DisplayItemType::TYPE_WRAP_LIST ||
4810 type == DisplayItemType::TYPE_CONTAINER) {
4811 // The current display item has children, process them first.
4812 if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(),
4813 aArray)) {
4814 return false;
4817 continue;
4820 if (aArray.Length() == kOpacityMaxChildCount) {
4821 return false;
4824 auto* item = i->AsPaintedDisplayItem();
4825 if (!item || !item->CanApplyOpacity(aManager, aBuilder)) {
4826 return false;
4829 aArray.AppendElement(item);
4832 return true;
4835 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager,
4836 nsDisplayListBuilder* aBuilder) {
4837 if (mChildOpacityState == ChildOpacityState::Deferred) {
4838 return false;
4841 // Iterate through the child display list and copy at most
4842 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4843 AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items;
4844 if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) {
4845 mChildOpacityState = ChildOpacityState::Deferred;
4846 return false;
4849 struct {
4850 nsPaintedDisplayItem* item{};
4851 nsRect bounds;
4852 } children[kOpacityMaxChildCount];
4854 bool snap;
4855 size_t childCount = 0;
4856 for (nsPaintedDisplayItem* item : items) {
4857 children[childCount].item = item;
4858 children[childCount].bounds = item->GetBounds(aBuilder, &snap);
4859 childCount++;
4862 for (size_t i = 0; i < childCount; i++) {
4863 for (size_t j = i + 1; j < childCount; j++) {
4864 if (children[i].bounds.Intersects(children[j].bounds)) {
4865 mChildOpacityState = ChildOpacityState::Deferred;
4866 return false;
4871 mChildOpacityState = ChildOpacityState::Applied;
4872 return true;
4876 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4877 * that has the same frame as the opacity item, and that supports painting with
4878 * opacity. In this case the opacity item can be optimized away.
4880 bool nsDisplayOpacity::ApplyToMask() {
4881 if (mList.Length() != 1) {
4882 return false;
4885 nsDisplayItem* item = mList.GetBottom();
4886 if (item->Frame() != mFrame) {
4887 // The effect item needs to have the same frame as the opacity item.
4888 return false;
4891 const DisplayItemType type = item->GetType();
4892 if (type == DisplayItemType::TYPE_MASK) {
4893 return true;
4896 return false;
4899 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4900 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder,
4901 float aInheritedOpacity) {
4902 if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
4903 mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
4904 // If we've been split, then we might need to merge, so
4905 // don't flatten us away.
4906 return false;
4909 if (mNeedsActiveLayer || mOpacity == 0.0) {
4910 // If our opacity is zero then we'll discard all descendant display items
4911 // except for layer event regions, so there's no point in doing this
4912 // optimization (and if we do do it, then invalidations of those descendants
4913 // might trigger repainting).
4914 return false;
4917 if (mList.IsEmpty()) {
4918 return false;
4921 // We can only flatten opacity items into a mask if we haven't
4922 // already flattened an earlier ancestor, since the SVG code pulls the opacity
4923 // from style directly, and won't know about the outer opacity value.
4924 if (aInheritedOpacity == 1.0f && ApplyToMask()) {
4925 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame));
4926 mChildOpacityState = ChildOpacityState::Applied;
4927 return true;
4930 // Return true if we successfully applied opacity to child items.
4931 return CanApplyToChildren(aManager, aBuilder);
4934 void nsDisplayOpacity::ComputeInvalidationRegion(
4935 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4936 nsRegion* aInvalidRegion) const {
4937 const auto* geometry =
4938 static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
4940 bool snap;
4941 if (mOpacity != geometry->mOpacity) {
4942 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4946 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) {
4947 aStream << " (opacity " << mOpacity << ", mChildOpacityState: ";
4948 switch (mChildOpacityState) {
4949 case ChildOpacityState::Unknown:
4950 aStream << "Unknown";
4951 break;
4952 case ChildOpacityState::Applied:
4953 aStream << "Applied";
4954 break;
4955 case ChildOpacityState::Deferred:
4956 aStream << "Deferred";
4957 break;
4958 default:
4959 break;
4962 aStream << ")";
4965 bool nsDisplayOpacity::CreateWebRenderCommands(
4966 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4967 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4968 nsDisplayListBuilder* aDisplayListBuilder) {
4969 MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied);
4970 float oldOpacity = aBuilder.GetInheritedOpacity();
4971 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
4972 aBuilder.SetInheritedOpacity(1.0f);
4973 aBuilder.SetInheritedClipChain(nullptr);
4974 float opacity = mOpacity * oldOpacity;
4975 float* opacityForSC = &opacity;
4977 uint64_t animationsId =
4978 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
4979 wr::WrAnimationProperty prop{
4980 wr::WrAnimationType::Opacity,
4981 animationsId,
4984 wr::StackingContextParams params;
4985 params.animation = animationsId ? &prop : nullptr;
4986 params.opacity = opacityForSC;
4987 params.clip =
4988 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
4989 if (mWrapsBackdropFilter) {
4990 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
4992 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
4993 params);
4995 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4996 &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
4997 aBuilder.SetInheritedOpacity(oldOpacity);
4998 aBuilder.SetInheritedClipChain(oldClipChain);
4999 return true;
5002 nsDisplayBlendMode::nsDisplayBlendMode(
5003 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5004 StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
5005 const bool aIsForBackground)
5006 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5007 mBlendMode(aBlendMode),
5008 mIsForBackground(aIsForBackground) {
5009 MOZ_COUNT_CTOR(nsDisplayBlendMode);
5012 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5013 bool* aSnap) const {
5014 *aSnap = false;
5015 // We are never considered opaque
5016 return nsRegion();
5019 bool nsDisplayBlendMode::CreateWebRenderCommands(
5020 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5021 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5022 nsDisplayListBuilder* aDisplayListBuilder) {
5023 wr::StackingContextParams params;
5024 params.mix_blend_mode =
5025 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
5026 params.clip =
5027 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5028 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5029 params);
5031 return nsDisplayWrapList::CreateWebRenderCommands(
5032 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5035 void nsDisplayBlendMode::Paint(nsDisplayListBuilder* aBuilder,
5036 gfxContext* aCtx) {
5037 // This should be switched to use PushLayerWithBlend, once it's
5038 // been implemented for all DrawTarget backends.
5039 DrawTarget* dt = aCtx->GetDrawTarget();
5040 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5041 Rect rect = NSRectToRect(GetPaintRect(aBuilder, aCtx), appUnitsPerDevPixel);
5042 rect.RoundOut();
5044 // Create a temporary DrawTarget that is clipped to the area that
5045 // we're going to draw to. This will include the same transform as
5046 // is currently on |dt|.
5047 RefPtr<DrawTarget> temp =
5048 dt->CreateClippedDrawTarget(rect, SurfaceFormat::B8G8R8A8);
5049 if (!temp) {
5050 return;
5053 gfxContext ctx(temp, /* aPreserveTransform */ true);
5055 GetChildren()->Paint(aBuilder, &ctx,
5056 mFrame->PresContext()->AppUnitsPerDevPixel());
5058 // Draw the temporary DT to the real destination, applying the blend mode, but
5059 // no transform.
5060 temp->Flush();
5061 RefPtr<SourceSurface> surface = temp->Snapshot();
5062 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
5063 dt->SetTransform(Matrix());
5064 dt->DrawSurface(
5065 surface, Rect(surface->GetRect()), Rect(surface->GetRect()),
5066 DrawSurfaceOptions(),
5067 DrawOptions(1.0f, nsCSSRendering::GetGFXBlendMode(mBlendMode)));
5070 gfx::CompositionOp nsDisplayBlendMode::BlendMode() {
5071 return nsCSSRendering::GetGFXBlendMode(mBlendMode);
5074 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const {
5075 // Items for the same content element should be merged into a single
5076 // compositing group.
5077 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
5078 !HasSameContent(aItem)) {
5079 return false;
5082 const auto* item = static_cast<const nsDisplayBlendMode*>(aItem);
5083 if (mIsForBackground || item->mIsForBackground) {
5084 // Don't merge background-blend-mode items
5085 return false;
5088 return true;
5091 /* static */
5092 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForMixBlendMode(
5093 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5094 const ActiveScrolledRoot* aActiveScrolledRoot) {
5095 return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
5096 aActiveScrolledRoot, false);
5099 /* static */
5100 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5101 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5102 nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot) {
5103 if (aSecondaryFrame) {
5104 auto type = GetTableTypeFromFrame(aFrame);
5105 auto index = static_cast<uint16_t>(type);
5107 return MakeDisplayItemWithIndex<nsDisplayTableBlendContainer>(
5108 aBuilder, aSecondaryFrame, index, aList, aActiveScrolledRoot, true,
5109 aFrame);
5112 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>(
5113 aBuilder, aFrame, 1, aList, aActiveScrolledRoot, true);
5116 nsDisplayBlendContainer::nsDisplayBlendContainer(
5117 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5118 const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground)
5119 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5120 mIsForBackground(aIsForBackground) {
5121 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
5124 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder* aBuilder,
5125 gfxContext* aCtx) {
5126 aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5127 GetChildren()->Paint(aBuilder, aCtx,
5128 mFrame->PresContext()->AppUnitsPerDevPixel());
5129 aCtx->GetDrawTarget()->PopLayer();
5132 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5133 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5134 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5135 nsDisplayListBuilder* aDisplayListBuilder) {
5136 wr::StackingContextParams params;
5137 params.flags |= wr::StackingContextFlags::IS_BLEND_CONTAINER;
5138 params.clip =
5139 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5140 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5141 params);
5143 return nsDisplayWrapList::CreateWebRenderCommands(
5144 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5147 nsDisplayOwnLayer::nsDisplayOwnLayer(
5148 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5149 const ActiveScrolledRoot* aActiveScrolledRoot,
5150 nsDisplayOwnLayerFlags aFlags, const ScrollbarData& aScrollbarData,
5151 bool aForceActive, bool aClearClipChain)
5152 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
5153 aClearClipChain),
5154 mFlags(aFlags),
5155 mScrollbarData(aScrollbarData),
5156 mForceActive(aForceActive),
5157 mWrAnimationId(0) {
5158 MOZ_COUNT_CTOR(nsDisplayOwnLayer);
5161 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5162 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb;
5165 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5166 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container;
5169 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5170 return IsScrollbarContainer() && IsScrollbarLayerForRoot();
5173 bool nsDisplayOwnLayer::IsScrollbarLayerForRoot() const {
5174 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5175 mScrollbarData.mTargetViewId ==
5176 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5179 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5180 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM;
5183 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5184 return GetType() == DisplayItemType::TYPE_FIXED_POSITION ||
5185 GetType() == DisplayItemType::TYPE_TABLE_FIXED_POSITION;
5188 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5189 return GetType() == DisplayItemType::TYPE_STICKY_POSITION;
5192 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5193 if (!mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
5194 return false;
5196 return mFrame->PresContext()->HasDynamicToolbar() ||
5197 // For tests on Android, this pref is set to simulate the dynamic
5198 // toolbar
5199 StaticPrefs::apz_fixed_margin_override_enabled();
5202 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5203 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5204 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5205 nsDisplayListBuilder* aDisplayListBuilder) {
5206 Maybe<wr::WrAnimationProperty> prop;
5207 bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() &&
5208 (IsScrollThumbLayer() || IsZoomingLayer() ||
5209 ShouldGetFixedOrStickyAnimationId() ||
5210 (IsRootScrollbarContainer() && HasDynamicToolbar()));
5212 if (needsProp) {
5213 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5214 // to create and set an animation id. That way APZ can adjust the position/
5215 // zoom of this content asynchronously as needed.
5216 RefPtr<WebRenderAPZAnimationData> animationData =
5217 aManager->CommandBuilder()
5218 .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(this);
5219 mWrAnimationId = animationData->GetAnimationId();
5221 prop.emplace();
5222 prop->id = mWrAnimationId;
5223 prop->key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5224 wr::SpatialKeyKind::APZ);
5225 prop->effect_type = wr::WrAnimationType::Transform;
5228 wr::StackingContextParams params;
5229 params.animation = prop.ptrOr(nullptr);
5230 params.clip =
5231 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5232 if (IsScrollbarContainer() && IsRootScrollbarContainer()) {
5233 params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER;
5235 if (IsZoomingLayer() ||
5236 (ShouldGetFixedOrStickyAnimationId() ||
5237 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5238 params.is_2d_scale_translation = true;
5239 params.should_snap = true;
5242 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5243 params);
5245 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
5246 aDisplayListBuilder);
5247 return true;
5250 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData,
5251 WebRenderLayerScrollData* aLayerData) {
5252 bool isRelevantToApz =
5253 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5254 ShouldGetFixedOrStickyAnimationId());
5256 if (!isRelevantToApz) {
5257 return false;
5260 if (!aLayerData) {
5261 return true;
5264 if (IsZoomingLayer()) {
5265 aLayerData->SetZoomAnimationId(mWrAnimationId);
5266 return true;
5269 if (IsFixedPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5270 aLayerData->SetFixedPositionAnimationId(mWrAnimationId);
5271 return true;
5274 if (IsStickyPositionLayer() && ShouldGetFixedOrStickyAnimationId()) {
5275 aLayerData->SetStickyPositionAnimationId(mWrAnimationId);
5276 return true;
5279 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5281 aLayerData->SetScrollbarData(mScrollbarData);
5283 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5284 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5285 return true;
5288 if (IsScrollThumbLayer()) {
5289 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5290 LayoutDeviceRect bounds = LayoutDeviceIntRect::FromAppUnits(
5291 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
5292 // Subframe scrollbars are subject to the pinch-zoom scale,
5293 // but root scrollbars are not because they are outside of the
5294 // region that is zoomed.
5295 const float resolution =
5296 IsScrollbarLayerForRoot()
5297 ? 1.0f
5298 : mFrame->PresShell()->GetCumulativeResolution();
5299 LayerIntRect layerBounds =
5300 RoundedOut(bounds * LayoutDeviceToLayerScale(resolution));
5301 aLayerData->SetVisibleRect(layerBounds);
5303 return true;
5306 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) {
5307 aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
5308 (int)mFlags, mScrollbarData.mTargetViewId)
5309 .get();
5312 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
5313 nsIFrame* aFrame,
5314 nsSubDocumentFrame* aSubDocFrame,
5315 nsDisplayList* aList,
5316 nsDisplayOwnLayerFlags aFlags)
5317 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5318 aBuilder->CurrentActiveScrolledRoot(), aFlags),
5319 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5320 mShouldFlatten(false),
5321 mSubDocFrame(aSubDocFrame) {
5322 MOZ_COUNT_CTOR(nsDisplaySubDocument);
5324 if (mSubDocFrame && mSubDocFrame != mFrame) {
5325 mSubDocFrame->AddDisplayItem(this);
5329 nsDisplaySubDocument::~nsDisplaySubDocument() {
5330 MOZ_COUNT_DTOR(nsDisplaySubDocument);
5331 if (mSubDocFrame) {
5332 mSubDocFrame->RemoveDisplayItem(this);
5336 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const {
5337 return mSubDocFrame ? mSubDocFrame : mFrame;
5340 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) {
5341 if (aFrame == mSubDocFrame) {
5342 mSubDocFrame = nullptr;
5343 SetDeletedFrame();
5345 nsDisplayOwnLayer::RemoveFrame(aFrame);
5348 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder,
5349 nsIFrame* aFrame) {
5350 return aBuilder->IsPaintingToWindow() &&
5351 DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext());
5354 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder,
5355 bool* aSnap) const {
5356 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5358 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5359 usingDisplayPort) {
5360 *aSnap = false;
5361 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
5364 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
5367 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5368 bool* aSnap) const {
5369 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5371 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5372 usingDisplayPort) {
5373 *aSnap = false;
5374 return nsRegion();
5377 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
5380 /* static */
5381 nsDisplayFixedPosition* nsDisplayFixedPosition::CreateForFixedBackground(
5382 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5383 nsDisplayBackgroundImage* aImage, const uint16_t aIndex,
5384 const ActiveScrolledRoot* aScrollTargetASR) {
5385 nsDisplayList temp(aBuilder);
5386 temp.AppendToTop(aImage);
5388 if (aSecondaryFrame) {
5389 auto tableType = GetTableTypeFromFrame(aFrame);
5390 const uint16_t index = CalculateTablePerFrameKey(aIndex + 1, tableType);
5391 return MakeDisplayItemWithIndex<nsDisplayTableFixedPosition>(
5392 aBuilder, aSecondaryFrame, index, &temp, aFrame, aScrollTargetASR);
5395 return MakeDisplayItemWithIndex<nsDisplayFixedPosition>(
5396 aBuilder, aFrame, aIndex + 1, &temp, aScrollTargetASR);
5399 nsDisplayFixedPosition::nsDisplayFixedPosition(
5400 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5401 const ActiveScrolledRoot* aActiveScrolledRoot,
5402 const ActiveScrolledRoot* aScrollTargetASR)
5403 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5404 mScrollTargetASR(aScrollTargetASR),
5405 mIsFixedBackground(false) {
5406 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5409 nsDisplayFixedPosition::nsDisplayFixedPosition(
5410 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5411 const ActiveScrolledRoot* aScrollTargetASR)
5412 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5413 aBuilder->CurrentActiveScrolledRoot()),
5414 // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5415 mScrollTargetASR(aScrollTargetASR),
5416 mIsFixedBackground(true) {
5417 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5420 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() const {
5421 if (mScrollTargetASR &&
5422 (mIsFixedBackground || !nsLayoutUtils::IsReallyFixedPos(mFrame))) {
5423 return mScrollTargetASR->GetViewId();
5425 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5428 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5429 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5430 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5431 nsDisplayListBuilder* aDisplayListBuilder) {
5432 SideBits sides = SideBits::eNone;
5433 if (!mIsFixedBackground) {
5434 sides = nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5437 // We install this RAII scrolltarget tracker so that any
5438 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5439 // share the same ASR as this item) use the correct scroll target. That way
5440 // attempts to scroll on those items will scroll the root scroll frame.
5441 wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker(
5442 aBuilder, GetActiveScrolledRoot(), GetScrollTargetId(), sides);
5443 return nsDisplayOwnLayer::CreateWebRenderCommands(
5444 aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
5447 bool nsDisplayFixedPosition::UpdateScrollData(
5448 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5449 if (aLayerData) {
5450 if (!mIsFixedBackground) {
5451 aLayerData->SetFixedPositionSides(
5452 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame));
5454 aLayerData->SetFixedPositionScrollContainerId(GetScrollTargetId());
5456 nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5457 return true;
5460 bool nsDisplayFixedPosition::ShouldGetFixedOrStickyAnimationId() {
5461 #if defined(MOZ_WIDGET_ANDROID)
5462 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5463 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) ==
5464 GetScrollTargetId();
5465 #else
5466 return false;
5467 #endif
5470 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream& aStream) {
5471 aStream << nsPrintfCString(
5472 " (containerASR %s) (scrolltarget %" PRIu64 ")",
5473 ActiveScrolledRoot::ToString(mScrollTargetASR).get(),
5474 GetScrollTargetId())
5475 .get();
5478 TableType GetTableTypeFromFrame(nsIFrame* aFrame) {
5479 if (aFrame->IsTableFrame()) {
5480 return TableType::Table;
5483 if (aFrame->IsTableColFrame()) {
5484 return TableType::TableCol;
5487 if (aFrame->IsTableColGroupFrame()) {
5488 return TableType::TableColGroup;
5491 if (aFrame->IsTableRowFrame()) {
5492 return TableType::TableRow;
5495 if (aFrame->IsTableRowGroupFrame()) {
5496 return TableType::TableRowGroup;
5499 if (aFrame->IsTableCellFrame()) {
5500 return TableType::TableCell;
5503 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5504 return TableType::Table;
5507 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5508 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5509 nsIFrame* aAncestorFrame, const ActiveScrolledRoot* aScrollTargetASR)
5510 : nsDisplayFixedPosition(aBuilder, aFrame, aList, aScrollTargetASR),
5511 mAncestorFrame(aAncestorFrame) {
5512 if (aBuilder->IsRetainingDisplayList()) {
5513 mAncestorFrame->AddDisplayItem(this);
5517 nsDisplayStickyPosition::nsDisplayStickyPosition(
5518 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5519 const ActiveScrolledRoot* aActiveScrolledRoot,
5520 const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
5521 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5522 mContainerASR(aContainerASR),
5523 mClippedToDisplayPort(aClippedToDisplayPort),
5524 mShouldFlatten(false) {
5525 MOZ_COUNT_CTOR(nsDisplayStickyPosition);
5528 // Returns the smallest distance from "0" to the range [min, max] where
5529 // min <= max. Despite the name, the return value is actually a 1-D vector,
5530 // and so may be negative if max < 0.
5531 static nscoord DistanceToRange(nscoord min, nscoord max) {
5532 MOZ_ASSERT(min <= max);
5533 if (max < 0) {
5534 return max;
5536 if (min > 0) {
5537 return min;
5539 MOZ_ASSERT(min <= 0 && max >= 0);
5540 return 0;
5543 // Returns the magnitude of the part of the range [min, max] that is greater
5544 // than zero. The return value is always non-negative.
5545 static nscoord PositivePart(nscoord min, nscoord max) {
5546 MOZ_ASSERT(min <= max);
5547 if (min >= 0) {
5548 return max - min;
5550 if (max > 0) {
5551 return max;
5553 return 0;
5556 // Returns the magnitude of the part of the range [min, max] that is less
5557 // than zero. The return value is always non-negative.
5558 static nscoord NegativePart(nscoord min, nscoord max) {
5559 MOZ_ASSERT(min <= max);
5560 if (max <= 0) {
5561 return max - min;
5563 if (min < 0) {
5564 return 0 - min;
5566 return 0;
5569 StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() {
5570 StickyScrollContainer* stickyScrollContainer =
5571 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
5572 if (stickyScrollContainer) {
5573 // If there's no ASR for the scrollframe that this sticky item is attached
5574 // to, then don't create a WR sticky item for it either. Trying to do so
5575 // will end in sadness because WR will interpret some coordinates as
5576 // relative to the nearest enclosing scrollframe, which will correspond
5577 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5578 // same as the scrollframe this sticky item is actually supposed to be
5579 // attached to, thus the sadness.
5580 // Not sending WR the sticky item is ok, because the enclosing scrollframe
5581 // will never be asynchronously scrolled. Instead we will always position
5582 // the sticky items correctly on the gecko side and WR will never need to
5583 // adjust their position itself.
5584 MOZ_ASSERT(
5585 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5586 if (!stickyScrollContainer->ScrollFrame()
5587 ->IsMaybeAsynchronouslyScrolled()) {
5588 stickyScrollContainer = nullptr;
5591 return stickyScrollContainer;
5594 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5595 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5596 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5597 nsDisplayListBuilder* aDisplayListBuilder) {
5598 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5600 Maybe<wr::SpaceAndClipChainHelper> saccHelper;
5602 if (stickyScrollContainer) {
5603 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5605 bool snap;
5606 nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
5608 Maybe<float> topMargin;
5609 Maybe<float> rightMargin;
5610 Maybe<float> bottomMargin;
5611 Maybe<float> leftMargin;
5612 wr::StickyOffsetBounds vBounds = {0.0, 0.0};
5613 wr::StickyOffsetBounds hBounds = {0.0, 0.0};
5614 nsPoint appliedOffset;
5616 nsRectAbsolute outer;
5617 nsRectAbsolute inner;
5618 stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5620 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
5621 nsPoint offset =
5622 scrollFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5624 // Adjust the scrollPort coordinates to be relative to the reference frame,
5625 // so that it is in the same space as everything else.
5626 nsRect scrollPort =
5627 stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
5628 scrollPort += offset;
5630 // The following computations make more sense upon understanding the
5631 // semantics of "inner" and "outer", which is explained in the comment on
5632 // SetStickyPositionData in Layers.h.
5634 if (outer.YMost() != inner.YMost()) {
5635 // Question: How far will itemBounds.y be from the top of the scrollport
5636 // when we have scrolled from the current scroll position of "0" to
5637 // reach the range [inner.YMost(), outer.YMost()] where the item gets
5638 // stuck?
5639 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5640 // needs to be adjusted by the distance to the range, less any other
5641 // sticky ranges that fall between 0 and the range. If the distance is
5642 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5643 // scrolling upwards (decreasing scroll offset) to reach that range,
5644 // which would increase itemBounds.y and make it farther away from the
5645 // top of the scrollport. So in that case the adjustment is -distance.
5646 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5647 // we would be scrolling downwards, itemBounds.y would decrease, and we
5648 // again need to adjust by -distance. If we are already in the range
5649 // then no adjustment is needed and distance is 0 so again using
5650 // -distance works. If the distance is positive, and the item has both
5651 // top and bottom sticky ranges, then the bottom sticky range may fall
5652 // (entirely[1] or partly[2]) between the current scroll position.
5653 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5654 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5655 // In these cases, the item doesn't actually move for that part of the
5656 // distance, so we need to subtract out that bit, which can be computed
5657 // as the positive portion of the range [outer.Y(), inner.Y()].
5658 nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
5659 if (distance > 0) {
5660 distance -= PositivePart(outer.Y(), inner.Y());
5662 topMargin = Some(NSAppUnitsToFloatPixels(
5663 itemBounds.y - scrollPort.y - distance, auPerDevPixel));
5664 // Question: What is the maximum positive ("downward") offset that WR
5665 // will have to apply to this item in order to prevent the item from
5666 // visually moving?
5667 // Answer: Since the item is "sticky" in the range [inner.YMost(),
5668 // outer.YMost()], the maximum offset will be the size of the range, which
5669 // is outer.YMost() - inner.YMost().
5670 vBounds.max =
5671 NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
5672 // Question: how much of an offset has layout already applied to the item?
5673 // Answer: if we are
5674 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5675 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5676 // then layout has already applied some offset to the position of the
5677 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5678 // and |outer.YMost() - inner.YMost()| in case (b).
5679 if (inner.YMost() < 0) {
5680 appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
5681 MOZ_ASSERT(appliedOffset.y > 0);
5684 if (outer.Y() != inner.Y()) {
5685 // Similar logic as in the previous section, but this time we care about
5686 // the distance from itemBounds.YMost() to scrollPort.YMost().
5687 nscoord distance = DistanceToRange(outer.Y(), inner.Y());
5688 if (distance < 0) {
5689 distance += NegativePart(inner.YMost(), outer.YMost());
5691 bottomMargin = Some(NSAppUnitsToFloatPixels(
5692 scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
5693 // And here WR will be moving the item upwards rather than downwards so
5694 // again things are inverted from the previous block.
5695 vBounds.min =
5696 NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
5697 // We can't have appliedOffset be both positive and negative, and the top
5698 // adjustment takes priority. So here we only update appliedOffset.y if
5699 // it wasn't set by the top-sticky case above.
5700 if (appliedOffset.y == 0 && inner.Y() > 0) {
5701 appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
5702 MOZ_ASSERT(appliedOffset.y < 0);
5705 // Same as above, but for the x-axis
5706 if (outer.XMost() != inner.XMost()) {
5707 nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
5708 if (distance > 0) {
5709 distance -= PositivePart(outer.X(), inner.X());
5711 leftMargin = Some(NSAppUnitsToFloatPixels(
5712 itemBounds.x - scrollPort.x - distance, auPerDevPixel));
5713 hBounds.max =
5714 NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
5715 if (inner.XMost() < 0) {
5716 appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
5717 MOZ_ASSERT(appliedOffset.x > 0);
5720 if (outer.X() != inner.X()) {
5721 nscoord distance = DistanceToRange(outer.X(), inner.X());
5722 if (distance < 0) {
5723 distance += NegativePart(inner.XMost(), outer.XMost());
5725 rightMargin = Some(NSAppUnitsToFloatPixels(
5726 scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
5727 hBounds.min =
5728 NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
5729 if (appliedOffset.x == 0 && inner.X() > 0) {
5730 appliedOffset.x = std::max(0, outer.X()) - inner.X();
5731 MOZ_ASSERT(appliedOffset.x < 0);
5735 LayoutDeviceRect bounds =
5736 LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
5737 wr::LayoutVector2D applied = {
5738 NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
5739 NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
5740 wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame(
5741 wr::ToLayoutRect(bounds), topMargin.ptrOr(nullptr),
5742 rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
5743 leftMargin.ptrOr(nullptr), vBounds, hBounds, applied,
5744 wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5745 wr::SpatialKeyKind::Sticky));
5747 saccHelper.emplace(aBuilder, spatialId);
5748 aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId);
5752 wr::StackingContextParams params;
5753 params.clip =
5754 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5755 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
5756 aBuilder, params);
5757 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc,
5758 aManager, aDisplayListBuilder);
5761 if (stickyScrollContainer) {
5762 aManager->CommandBuilder().PopOverrideForASR(mContainerASR);
5765 return true;
5768 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5769 StickyScrollContainer* aStickyScrollContainer, float aAppUnitsPerDevPixel,
5770 float aScaleX, float aScaleY, LayerRectAbsolute& aStickyOuter,
5771 LayerRectAbsolute& aStickyInner) {
5772 nsRectAbsolute outer;
5773 nsRectAbsolute inner;
5774 aStickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5775 aStickyOuter.SetBox(
5776 NSAppUnitsToFloatPixels(outer.X(), aAppUnitsPerDevPixel) * aScaleX,
5777 NSAppUnitsToFloatPixels(outer.Y(), aAppUnitsPerDevPixel) * aScaleY,
5778 NSAppUnitsToFloatPixels(outer.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5779 NSAppUnitsToFloatPixels(outer.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5780 aStickyInner.SetBox(
5781 NSAppUnitsToFloatPixels(inner.X(), aAppUnitsPerDevPixel) * aScaleX,
5782 NSAppUnitsToFloatPixels(inner.Y(), aAppUnitsPerDevPixel) * aScaleY,
5783 NSAppUnitsToFloatPixels(inner.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5784 NSAppUnitsToFloatPixels(inner.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5787 bool nsDisplayStickyPosition::UpdateScrollData(
5788 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5789 bool hasDynamicToolbar = HasDynamicToolbar();
5790 if (aLayerData && hasDynamicToolbar) {
5791 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5792 if (stickyScrollContainer) {
5793 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5794 float cumulativeResolution =
5795 mFrame->PresShell()->GetCumulativeResolution();
5796 LayerRectAbsolute stickyOuter;
5797 LayerRectAbsolute stickyInner;
5798 CalculateLayerScrollRanges(stickyScrollContainer, auPerDevPixel,
5799 cumulativeResolution, cumulativeResolution,
5800 stickyOuter, stickyInner);
5801 aLayerData->SetStickyScrollRangeOuter(stickyOuter);
5802 aLayerData->SetStickyScrollRangeInner(stickyInner);
5804 SideBits sides =
5805 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5806 aLayerData->SetFixedPositionSides(sides);
5808 ScrollableLayerGuid::ViewID scrollId =
5809 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5810 ->GetScrolledFrame()
5811 ->GetContent());
5812 aLayerData->SetStickyPositionScrollContainerId(scrollId);
5815 // Return true if either there is a dynamic toolbar affecting this sticky
5816 // item or the OwnLayer base implementation returns true for some other
5817 // reason.
5818 bool ret = hasDynamicToolbar;
5819 ret |= nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5820 return ret;
5823 bool nsDisplayStickyPosition::ShouldGetFixedOrStickyAnimationId() {
5824 #if defined(MOZ_WIDGET_ANDROID)
5825 if (HasDynamicToolbar()) { // also implies being in the cross-process RCD
5826 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5827 if (stickyScrollContainer) {
5828 ScrollableLayerGuid::ViewID scrollId =
5829 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5830 ->GetScrolledFrame()
5831 ->GetContent());
5832 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) ==
5833 scrollId;
5836 #endif
5837 return false;
5840 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5841 nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
5842 nsIFrame* aScrollFrame, const CompositorHitTestInfo& aHitInfo,
5843 const nsRect& aHitArea)
5844 : nsDisplayWrapList(aBuilder, aScrollFrame),
5845 mScrollFrame(aScrollFrame),
5846 mScrolledFrame(aScrolledFrame),
5847 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5848 mHitInfo(aHitInfo),
5849 mHitArea(aHitArea) {
5850 #ifdef NS_BUILD_REFCNT_LOGGING
5851 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
5852 #endif
5855 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5856 nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) {
5857 ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
5858 mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(),
5859 ToReferenceFrame(), aLayerManager, mScrollParentId,
5860 mScrollFrame->GetSize(), false);
5861 metadata.GetMetrics().SetIsScrollInfoLayer(true);
5862 nsIScrollableFrame* scrollableFrame = mScrollFrame->GetScrollTargetFrame();
5863 if (scrollableFrame) {
5864 aBuilder->AddScrollFrameToNotify(scrollableFrame);
5867 return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
5870 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5871 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5872 if (aLayerData) {
5873 UniquePtr<ScrollMetadata> metadata =
5874 ComputeScrollMetadata(aData->GetBuilder(), aData->GetManager());
5875 MOZ_ASSERT(aData);
5876 MOZ_ASSERT(metadata);
5877 aLayerData->AppendScrollMetadata(*aData, *metadata);
5879 return true;
5882 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5883 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5884 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5885 nsDisplayListBuilder* aDisplayListBuilder) {
5886 ScrollableLayerGuid::ViewID scrollId =
5887 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame->GetContent());
5889 const LayoutDeviceRect devRect = LayoutDeviceRect::FromAppUnits(
5890 mHitArea, mScrollFrame->PresContext()->AppUnitsPerDevPixel());
5892 const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
5894 aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, mHitInfo,
5895 SideBits::eNone);
5897 return true;
5900 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) {
5901 aStream << " (scrollframe " << mScrollFrame << " scrolledFrame "
5902 << mScrolledFrame << ")";
5905 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5906 nsSubDocumentFrame* aSubDocFrame,
5907 nsDisplayList* aList, int32_t aAPD,
5908 int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags)
5909 : nsDisplaySubDocument(aBuilder, aFrame, aSubDocFrame, aList, aFlags),
5910 mAPD(aAPD),
5911 mParentAPD(aParentAPD) {
5912 MOZ_COUNT_CTOR(nsDisplayZoom);
5915 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder,
5916 bool* aSnap) const {
5917 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
5918 *aSnap = false;
5919 return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD);
5922 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5923 HitTestState* aState,
5924 nsTArray<nsIFrame*>* aOutFrames) {
5925 nsRect rect;
5926 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5927 // rect as well instead of possibly rounding the width or height to zero.
5928 if (aRect.width == 1 && aRect.height == 1) {
5929 rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD));
5930 rect.width = rect.height = 1;
5931 } else {
5932 rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
5934 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5937 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5938 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5939 const ActiveScrolledRoot* aActiveScrolledRoot, FrameMetrics::ViewID aViewID)
5940 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5941 mViewID(aViewID) {
5942 MOZ_COUNT_CTOR(nsDisplayAsyncZoom);
5945 #ifdef NS_BUILD_REFCNT_LOGGING
5946 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5947 MOZ_COUNT_DTOR(nsDisplayAsyncZoom);
5949 #endif
5951 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder* aBuilder,
5952 const nsRect& aRect, HitTestState* aState,
5953 nsTArray<nsIFrame*>* aOutFrames) {
5954 #ifdef DEBUG
5955 nsIScrollableFrame* scrollFrame = do_QueryFrame(mFrame);
5956 MOZ_ASSERT(scrollFrame && ViewportUtils::IsZoomedContentRoot(
5957 scrollFrame->GetScrolledFrame()));
5958 #endif
5959 nsRect rect = ViewportUtils::VisualToLayout(aRect, mFrame->PresShell());
5960 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5963 bool nsDisplayAsyncZoom::UpdateScrollData(
5964 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5965 bool ret = nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5966 MOZ_ASSERT(ret);
5967 if (aLayerData) {
5968 aLayerData->SetAsyncZoomContainerId(mViewID);
5970 return ret;
5973 ///////////////////////////////////////////////////
5974 // nsDisplayTransform Implementation
5977 #ifndef DEBUG
5978 static_assert(sizeof(nsDisplayTransform) <= 512,
5979 "nsDisplayTransform has grown");
5980 #endif
5982 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5983 nsIFrame* aFrame, nsDisplayList* aList,
5984 const nsRect& aChildrenBuildingRect)
5985 : nsPaintedDisplayItem(aBuilder, aFrame),
5986 mChildren(aBuilder),
5987 mTransform(Some(Matrix4x4())),
5988 mChildrenBuildingRect(aChildrenBuildingRect),
5989 mPrerenderDecision(PrerenderDecision::No),
5990 mIsTransformSeparator(true),
5991 mHasTransformGetter(false),
5992 mHasAssociatedPerspective(false),
5993 mContainsASRs(false) {
5994 MOZ_COUNT_CTOR(nsDisplayTransform);
5995 MOZ_ASSERT(aFrame, "Must have a frame!");
5996 Init(aBuilder, aList);
5999 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
6000 nsIFrame* aFrame, nsDisplayList* aList,
6001 const nsRect& aChildrenBuildingRect,
6002 PrerenderDecision aPrerenderDecision)
6003 : nsPaintedDisplayItem(aBuilder, aFrame),
6004 mChildren(aBuilder),
6005 mChildrenBuildingRect(aChildrenBuildingRect),
6006 mPrerenderDecision(aPrerenderDecision),
6007 mIsTransformSeparator(false),
6008 mHasTransformGetter(false),
6009 mHasAssociatedPerspective(false),
6010 mContainsASRs(false) {
6011 MOZ_COUNT_CTOR(nsDisplayTransform);
6012 MOZ_ASSERT(aFrame, "Must have a frame!");
6013 SetReferenceFrameToAncestor(aBuilder);
6014 Init(aBuilder, aList);
6017 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
6018 nsIFrame* aFrame, nsDisplayList* aList,
6019 const nsRect& aChildrenBuildingRect,
6020 decltype(WithTransformGetter))
6021 : nsPaintedDisplayItem(aBuilder, aFrame),
6022 mChildren(aBuilder),
6023 mChildrenBuildingRect(aChildrenBuildingRect),
6024 mPrerenderDecision(PrerenderDecision::No),
6025 mIsTransformSeparator(false),
6026 mHasTransformGetter(true),
6027 mHasAssociatedPerspective(false),
6028 mContainsASRs(false) {
6029 MOZ_COUNT_CTOR(nsDisplayTransform);
6030 MOZ_ASSERT(aFrame, "Must have a frame!");
6031 MOZ_ASSERT(aFrame->GetTransformGetter());
6032 Init(aBuilder, aList);
6035 void nsDisplayTransform::SetReferenceFrameToAncestor(
6036 nsDisplayListBuilder* aBuilder) {
6037 if (mFrame == aBuilder->RootReferenceFrame()) {
6038 return;
6040 // We manually recompute mToReferenceFrame without going through the
6041 // builder, since this won't apply the 'additional offset'. Our
6042 // children will already be painting with that applied, and we don't
6043 // want to include it a second time in our transform. We don't recompute
6044 // our visible/building rects, since those should still include the additional
6045 // offset.
6046 // TODO: Are there are things computed using our ToReferenceFrame that should
6047 // have the additional offset applied? Should we instead just manually remove
6048 // the offset from our transform instead of this more general value?
6049 // Can we instead apply the additional offset to us and not our children, like
6050 // we do for all other offsets (and how reference frames are supposed to
6051 // work)?
6052 nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame);
6053 const nsIFrame* referenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
6054 mToReferenceFrame = mFrame->GetOffsetToCrossDoc(referenceFrame);
6057 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
6058 nsDisplayList* aChildren) {
6059 mChildren.AppendToTop(aChildren);
6060 UpdateBounds(aBuilder);
6063 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
6064 return false;
6067 /* Returns the delta specified by the transform-origin property.
6068 * This is a positive delta, meaning that it indicates the direction to move
6069 * to get from (0, 0) of the frame to the transform origin. This function is
6070 * called off the main thread.
6072 /* static */
6073 Point3D nsDisplayTransform::GetDeltaToTransformOrigin(
6074 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6075 float aAppUnitsPerPixel) {
6076 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6077 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6078 aFrame->Combines3DTransformWithAncestors(),
6079 "Shouldn't get a delta for an untransformed frame!");
6081 if (!aFrame->IsTransformed()) {
6082 return Point3D();
6085 /* For both of the coordinates, if the value of transform is a
6086 * percentage, it's relative to the size of the frame. Otherwise, if it's
6087 * a distance, it's already computed for us!
6089 const nsStyleDisplay* display = aFrame->StyleDisplay();
6091 const StyleTransformOrigin& transformOrigin = display->mTransformOrigin;
6092 CSSPoint origin = nsStyleTransformMatrix::Convert2DPosition(
6093 transformOrigin.horizontal, transformOrigin.vertical, aRefBox);
6095 // Note:
6096 // 1. SVG frames have a reference box that can be (and typically is) offset
6097 // from the TopLeft() of the frame. We need to account for that here.
6098 // 2. If we are using transform-box:content-box in CSS layout, we have the
6099 // offset from TopLeft() of the frame as well.
6100 origin.x += CSSPixel::FromAppUnits(aRefBox.X());
6101 origin.y += CSSPixel::FromAppUnits(aRefBox.Y());
6103 float scale = AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel);
6104 float z = transformOrigin.depth._0;
6105 return Point3D(origin.x * scale, origin.y * scale, z * scale);
6108 /* static */
6109 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
6110 float aAppUnitsPerPixel,
6111 Matrix4x4& aOutMatrix) {
6112 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6113 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6114 aFrame->Combines3DTransformWithAncestors(),
6115 "Shouldn't get a delta for an untransformed frame!");
6116 MOZ_ASSERT(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
6118 if (!aFrame->IsTransformed()) {
6119 return false;
6122 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6123 // correctly yet (similar to the aBoundsOverride case for
6124 // GetResultingTransformMatrix)?
6125 nsIFrame* perspectiveFrame =
6126 aFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6127 if (!perspectiveFrame) {
6128 return false;
6131 /* Grab the values for perspective and perspective-origin (if present) */
6132 const nsStyleDisplay* perspectiveDisplay = perspectiveFrame->StyleDisplay();
6133 if (perspectiveDisplay->mChildPerspective.IsNone()) {
6134 return false;
6137 MOZ_ASSERT(perspectiveDisplay->mChildPerspective.IsLength());
6138 float perspective =
6139 perspectiveDisplay->mChildPerspective.AsLength().ToCSSPixels();
6140 perspective = std::max(1.0f, perspective);
6141 if (perspective < std::numeric_limits<Float>::epsilon()) {
6142 return true;
6145 TransformReferenceBox refBox(perspectiveFrame);
6147 Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition(
6148 perspectiveDisplay->mPerspectiveOrigin.horizontal,
6149 perspectiveDisplay->mPerspectiveOrigin.vertical, refBox,
6150 aAppUnitsPerPixel);
6152 /* GetOffsetTo computes the offset required to move from 0,0 in
6153 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6154 * this, it's faster to compute this way.
6156 nsPoint frameToPerspectiveOffset = -aFrame->GetOffsetTo(perspectiveFrame);
6157 Point frameToPerspectiveGfxOffset(
6158 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.x, aAppUnitsPerPixel),
6159 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.y, aAppUnitsPerPixel));
6161 /* Move the perspective origin to be relative to aFrame, instead of relative
6162 * to the containing block which is how it was specified in the style system.
6164 perspectiveOrigin += frameToPerspectiveGfxOffset;
6166 aOutMatrix._34 =
6167 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective),
6168 aAppUnitsPerPixel);
6170 aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
6171 return true;
6174 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6175 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6176 float aAppUnitsPerPixel)
6177 : mFrame(aFrame),
6178 mTranslate(aFrame->StyleDisplay()->mTranslate),
6179 mRotate(aFrame->StyleDisplay()->mRotate),
6180 mScale(aFrame->StyleDisplay()->mScale),
6181 mTransform(aFrame->StyleDisplay()->mTransform),
6182 mMotion(aFrame->StyleDisplay()->mOffsetPath.IsNone()
6183 ? Nothing()
6184 : MotionPathUtils::ResolveMotionPath(aFrame, aRefBox)),
6185 mToTransformOrigin(
6186 GetDeltaToTransformOrigin(aFrame, aRefBox, aAppUnitsPerPixel)) {}
6188 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6189 * translates from local coordinate space to transform coordinate space, then
6190 * hands it back.
6192 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6193 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6194 float aAppUnitsPerPixel) {
6195 return GetResultingTransformMatrixInternal(aProperties, aRefBox, nsPoint(),
6196 aAppUnitsPerPixel, 0);
6199 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6200 const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel,
6201 uint32_t aFlags) {
6202 TransformReferenceBox refBox(aFrame);
6203 FrameTransformProperties props(aFrame, refBox, aAppUnitsPerPixel);
6204 return GetResultingTransformMatrixInternal(props, refBox, aOrigin,
6205 aAppUnitsPerPixel, aFlags);
6208 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal(
6209 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6210 const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags) {
6211 const nsIFrame* frame = aProperties.mFrame;
6212 NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE),
6213 "Must have a frame to compute perspective!");
6215 // IncrementScaleRestyleCountIfNeeded in ActiveLayerTracker.cpp is a
6216 // simplified copy of this function.
6218 // Get the underlying transform matrix:
6220 /* Get the matrix, then change its basis to factor in the origin. */
6221 Matrix4x4 result;
6222 // Call IsSVGTransformed() regardless of the value of
6223 // aProperties.HasTransform(), since we still need any
6224 // potential parentsChildrenOnlyTransform.
6225 Matrix svgTransform, parentsChildrenOnlyTransform;
6226 const bool hasSVGTransforms =
6227 frame && frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
6228 frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform);
6229 bool shouldRound = nsLayoutUtils::ShouldSnapToGrid(frame);
6231 /* Transformed frames always have a transform, or are preserving 3d (and might
6232 * still have perspective!) */
6233 if (aProperties.HasTransform()) {
6234 result = nsStyleTransformMatrix::ReadTransforms(
6235 aProperties.mTranslate, aProperties.mRotate, aProperties.mScale,
6236 aProperties.mMotion.ptrOr(nullptr), aProperties.mTransform, aRefBox,
6237 aAppUnitsPerPixel);
6238 } else if (hasSVGTransforms) {
6239 // Correct the translation components for zoom:
6240 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6241 svgTransform._31 *= pixelsPerCSSPx;
6242 svgTransform._32 *= pixelsPerCSSPx;
6243 result = Matrix4x4::From2D(svgTransform);
6246 // Apply any translation due to 'transform-origin' and/or 'transform-box':
6247 result.ChangeBasis(aProperties.mToTransformOrigin);
6249 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
6250 // an explanation of what children-only transforms are.
6251 const bool parentHasChildrenOnlyTransform =
6252 hasSVGTransforms && !parentsChildrenOnlyTransform.IsIdentity();
6254 if (parentHasChildrenOnlyTransform) {
6255 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6256 parentsChildrenOnlyTransform._31 *= pixelsPerCSSPx;
6257 parentsChildrenOnlyTransform._32 *= pixelsPerCSSPx;
6259 Point3D frameOffset(
6260 NSAppUnitsToFloatPixels(-frame->GetPosition().x, aAppUnitsPerPixel),
6261 NSAppUnitsToFloatPixels(-frame->GetPosition().y, aAppUnitsPerPixel), 0);
6262 Matrix4x4 parentsChildrenOnlyTransform3D =
6263 Matrix4x4::From2D(parentsChildrenOnlyTransform)
6264 .ChangeBasis(frameOffset);
6266 result *= parentsChildrenOnlyTransform3D;
6269 Matrix4x4 perspectiveMatrix;
6270 bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE;
6271 if (hasPerspective) {
6272 if (ComputePerspectiveMatrix(frame, aAppUnitsPerPixel, perspectiveMatrix)) {
6273 result *= perspectiveMatrix;
6277 if ((aFlags & INCLUDE_PRESERVE3D_ANCESTORS) && frame &&
6278 frame->Combines3DTransformWithAncestors()) {
6279 // Include the transform set on our parent
6280 nsIFrame* parentFrame =
6281 frame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6282 NS_ASSERTION(parentFrame && parentFrame->IsTransformed() &&
6283 parentFrame->Extend3DContext(),
6284 "Preserve3D mismatch!");
6285 TransformReferenceBox refBox(parentFrame);
6286 FrameTransformProperties props(parentFrame, refBox, aAppUnitsPerPixel);
6288 uint32_t flags =
6289 aFlags & (INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE);
6291 // If this frame isn't transformed (but we exist for backface-visibility),
6292 // then we're not a reference frame so no offset to origin will be added.
6293 // Otherwise we need to manually translate into our parent's coordinate
6294 // space.
6295 if (frame->IsTransformed()) {
6296 nsLayoutUtils::PostTranslate(result, frame->GetPosition(),
6297 aAppUnitsPerPixel, shouldRound);
6299 Matrix4x4 parent = GetResultingTransformMatrixInternal(
6300 props, refBox, nsPoint(0, 0), aAppUnitsPerPixel, flags);
6301 result = result * parent;
6304 if (aFlags & OFFSET_BY_ORIGIN) {
6305 nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel,
6306 shouldRound);
6309 return result;
6312 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6313 static constexpr nsCSSPropertyIDSet opacitySet =
6314 nsCSSPropertyIDSet::OpacityProperties();
6315 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, opacitySet)) {
6316 return true;
6319 EffectCompositor::SetPerformanceWarning(
6320 mFrame, opacitySet,
6321 AnimationPerformanceWarning(
6322 AnimationPerformanceWarning::Type::OpacityFrameInactive));
6324 return false;
6327 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6328 return mPrerenderDecision != PrerenderDecision::No;
6331 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
6332 nsDisplayListBuilder* aBuilder) {
6333 return StaticPrefs::gfx_omta_background_color();
6336 static bool IsInStickyPositionedSubtree(const nsIFrame* aFrame) {
6337 for (const nsIFrame* frame = aFrame; frame;
6338 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame)) {
6339 if (frame->IsStickyPositioned()) {
6340 return true;
6343 return false;
6346 static bool ShouldUsePartialPrerender(const nsIFrame* aFrame) {
6347 return StaticPrefs::layout_animation_prerender_partial() &&
6348 // Bug 1642547: Support partial prerender for position:sticky elements.
6349 !IsInStickyPositionedSubtree(aFrame);
6352 /* static */
6353 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
6354 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect)
6355 -> PrerenderInfo {
6356 PrerenderInfo result;
6357 // If we are in a preserve-3d tree, and we've disallowed async animations, we
6358 // return No prerender decision directly.
6359 if ((aFrame->Extend3DContext() ||
6360 aFrame->Combines3DTransformWithAncestors()) &&
6361 !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
6362 return result;
6365 // Elements whose transform has been modified recently, or which
6366 // have a compositor-animated transform, can be prerendered. An element
6367 // might have only just had its transform animated in which case
6368 // the ActiveLayerManager may not have been notified yet.
6369 static constexpr nsCSSPropertyIDSet transformSet =
6370 nsCSSPropertyIDSet::TransformLikeProperties();
6371 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame) &&
6372 !EffectCompositor::HasAnimationsForCompositor(
6373 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
6374 EffectCompositor::SetPerformanceWarning(
6375 aFrame, transformSet,
6376 AnimationPerformanceWarning(
6377 AnimationPerformanceWarning::Type::TransformFrameInactive));
6379 // This case happens when we're sure that the frame is not animated and its
6380 // preserve-3d ancestors are not, either. So we don't need to pre-render.
6381 // However, this decision shouldn't affect the decisions for other frames in
6382 // the preserve-3d context. We need this flag to determine whether we should
6383 // block async animations on other frames in the current preserve-3d tree.
6384 result.mHasAnimations = false;
6385 return result;
6388 // We should not allow prerender if any ancestor container element has
6389 // mask/clip-path effects.
6391 // With prerender and async transform animation, we do not need to restyle an
6392 // animated element to respect position changes, since that transform is done
6393 // by layer animation. As a result, the container element is not aware of
6394 // position change of that containing element and loses the chance to update
6395 // the content of mask/clip-path.
6397 // Why do we need to update a mask? This is relative to how we generate a
6398 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
6399 // mask layer, to reduce memory usage, we did not choose the size of the
6400 // masked element as mask size. Instead, we read the union of bounds of all
6401 // children display items by nsDisplayWrapList::GetBounds, which is smaller
6402 // than or equal to the masked element's boundary, and use it as the position
6403 // size of the mask layer. That union bounds is actually affected by the
6404 // geometry of the animated element. To keep the content of mask up to date,
6405 // forbidding of prerender is required.
6406 for (nsIFrame* container =
6407 nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
6408 container;
6409 container = nsLayoutUtils::GetCrossDocParentFrameInProcess(container)) {
6410 const nsStyleSVGReset* svgReset = container->StyleSVGReset();
6411 if (svgReset->HasMask() || svgReset->HasClipPath()) {
6412 return result;
6416 // If the incoming dirty rect already contains the entire overflow area,
6417 // we are already rendering the entire content.
6418 nsRect overflow = aFrame->InkOverflowRectRelativeToSelf();
6419 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
6420 // in cases of non-invertible transforms, so we set `untransformedRect` to
6421 // `aDirtyRect` as an initial value for such cases.
6422 nsRect untransformedDirtyRect = *aDirtyRect;
6423 UntransformRect(*aDirtyRect, overflow, aFrame, &untransformedDirtyRect);
6424 if (untransformedDirtyRect.Contains(overflow)) {
6425 *aDirtyRect = untransformedDirtyRect;
6426 result.mDecision = PrerenderDecision::Full;
6427 return result;
6430 float viewportRatio =
6431 StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
6432 uint32_t absoluteLimitX =
6433 StaticPrefs::layout_animation_prerender_absolute_limit_x();
6434 uint32_t absoluteLimitY =
6435 StaticPrefs::layout_animation_prerender_absolute_limit_y();
6436 nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
6438 float resolution = aFrame->PresShell()->GetCumulativeResolution();
6439 if (resolution < 1.0f) {
6440 refSize.SizeTo(
6441 NSCoordSaturatingNonnegativeMultiply(refSize.width, 1.0f / resolution),
6442 NSCoordSaturatingNonnegativeMultiply(refSize.height,
6443 1.0f / resolution));
6446 // Only prerender if the transformed frame's size is <= a multiple of the
6447 // reference frame size (~viewport), and less than an absolute limit.
6448 // Both the ratio and the absolute limit are configurable.
6449 nscoord maxLength = std::max(nscoord(refSize.width * viewportRatio),
6450 nscoord(refSize.height * viewportRatio));
6451 nsSize relativeLimit(maxLength, maxLength);
6452 nsSize absoluteLimit(
6453 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
6454 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
6455 nsSize maxSize = Min(relativeLimit, absoluteLimit);
6457 const auto transform = nsLayoutUtils::GetTransformToAncestor(
6458 RelativeTo{aFrame},
6459 RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});
6460 const gfxRect transformedBounds = transform.TransformAndClipBounds(
6461 gfxRect(overflow.x, overflow.y, overflow.width, overflow.height),
6462 gfxRect::MaxIntRect());
6463 const nsSize frameSize =
6464 nsSize(transformedBounds.width, transformedBounds.height);
6466 uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
6467 uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
6468 if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
6469 *aDirtyRect = overflow;
6470 result.mDecision = PrerenderDecision::Full;
6471 return result;
6474 if (ShouldUsePartialPrerender(aFrame)) {
6475 *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(
6476 aFrame, untransformedDirtyRect, overflow, maxSize);
6477 result.mDecision = PrerenderDecision::Partial;
6478 return result;
6481 if (frameArea > maxLimitArea) {
6482 uint64_t appUnitsPerPixel = AppUnitsPerCSSPixel();
6483 EffectCompositor::SetPerformanceWarning(
6484 aFrame, transformSet,
6485 AnimationPerformanceWarning(
6486 AnimationPerformanceWarning::Type::ContentTooLargeArea,
6488 int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
6489 int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
6490 }));
6491 } else {
6492 EffectCompositor::SetPerformanceWarning(
6493 aFrame, transformSet,
6494 AnimationPerformanceWarning(
6495 AnimationPerformanceWarning::Type::ContentTooLarge,
6497 nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
6498 nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
6499 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
6500 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
6501 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
6502 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
6503 }));
6506 return result;
6509 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
6510 * visible or hit. */
6511 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix) {
6512 if (aMatrix.IsSingular()) {
6513 return false;
6515 if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) {
6516 return false;
6518 return true;
6521 const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const {
6522 if (mTransform) {
6523 return *mTransform;
6526 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6528 if (mHasTransformGetter) {
6529 mTransform.emplace((mFrame->GetTransformGetter())(mFrame, scale));
6530 Point3D newOrigin =
6531 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
6532 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f);
6533 mTransform->ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
6534 } else if (!mIsTransformSeparator) {
6535 DebugOnly<bool> isReference = mFrame->IsTransformed() ||
6536 mFrame->Combines3DTransformWithAncestors() ||
6537 mFrame->Extend3DContext();
6538 MOZ_ASSERT(isReference);
6539 mTransform.emplace(
6540 GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale,
6541 INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN));
6542 } else {
6543 // Use identity matrix
6544 mTransform.emplace();
6547 return *mTransform;
6550 const Matrix4x4Flagged& nsDisplayTransform::GetInverseTransform() const {
6551 if (mInverseTransform) {
6552 return *mInverseTransform;
6555 MOZ_ASSERT(!GetTransform().IsSingular());
6557 mInverseTransform.emplace(GetTransform().Inverse());
6559 return *mInverseTransform;
6562 Matrix4x4 nsDisplayTransform::GetTransformForRendering(
6563 LayoutDevicePoint* aOutOrigin) const {
6564 if (!mFrame->HasPerspective() || mHasTransformGetter ||
6565 mIsTransformSeparator) {
6566 if (!mHasTransformGetter && !mIsTransformSeparator && aOutOrigin) {
6567 // If aOutOrigin is provided, put the offset to origin into it, because
6568 // we need to keep it separate for webrender. The combination of
6569 // *aOutOrigin and the returned matrix here should always be equivalent
6570 // to what GetTransform() would have returned.
6571 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6572 *aOutOrigin = LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale);
6574 // The rounding behavior should also be the same as GetTransform().
6575 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6576 aOutOrigin->Round();
6578 return GetResultingTransformMatrix(mFrame, nsPoint(0, 0), scale,
6579 INCLUDE_PERSPECTIVE);
6581 return GetTransform().GetMatrix();
6583 MOZ_ASSERT(!mHasTransformGetter);
6585 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6586 // Don't include perspective transform, or the offset to origin, since
6587 // nsDisplayPerspective will handle both of those.
6588 return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
6591 const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
6592 nsDisplayListBuilder* aBuilder) {
6593 MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
6595 if (!IsLeafOf3DContext()) {
6596 return GetTransform().GetMatrix();
6599 if (!mTransformPreserves3D) {
6600 const nsIFrame* establisher; // Establisher of the 3D rendering context.
6601 for (establisher = mFrame;
6602 establisher && establisher->Combines3DTransformWithAncestors();
6603 establisher =
6604 establisher->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
6606 const nsIFrame* establisherReference = aBuilder->FindReferenceFrameFor(
6607 nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher));
6609 nsPoint offset = establisher->GetOffsetToCrossDoc(establisherReference);
6610 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6611 uint32_t flags =
6612 INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN;
6613 mTransformPreserves3D = MakeUnique<Matrix4x4>(
6614 GetResultingTransformMatrix(mFrame, offset, scale, flags));
6617 return *mTransformPreserves3D;
6620 bool nsDisplayTransform::CreateWebRenderCommands(
6621 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
6622 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
6623 nsDisplayListBuilder* aDisplayListBuilder) {
6624 // We want to make sure we don't pollute the transform property in the WR
6625 // stacking context by including the position of this frame (relative to the
6626 // parent reference frame). We need to keep those separate; the position of
6627 // this frame goes into the stacking context bounds while the transform goes
6628 // into the transform.
6629 LayoutDevicePoint position;
6630 Matrix4x4 newTransformMatrix = GetTransformForRendering(&position);
6632 gfx::Matrix4x4* transformForSC = &newTransformMatrix;
6633 if (newTransformMatrix.IsIdentity()) {
6634 // If the transform is an identity transform, strip it out so that WR
6635 // doesn't turn this stacking context into a reference frame, as it
6636 // affects positioning. Bug 1345577 tracks a better fix.
6637 transformForSC = nullptr;
6639 // In ChooseScaleAndSetTransform, we round the offset from the reference
6640 // frame used to adjust the transform, if there is no transform, or it
6641 // is just a translation. We need to do the same here.
6642 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6643 position.Round();
6647 auto key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
6648 wr::SpatialKeyKind::Transform);
6650 // We don't send animations for transform separator display items.
6651 uint64_t animationsId =
6652 mIsTransformSeparator
6654 : AddAnimationsForWebRender(
6655 this, aManager, aDisplayListBuilder,
6656 IsPartialPrerender() ? Some(position) : Nothing());
6657 wr::WrAnimationProperty prop{wr::WrAnimationType::Transform, animationsId,
6658 key};
6660 nsDisplayTransform* deferredTransformItem = nullptr;
6661 if (ShouldDeferTransform()) {
6662 // If it has perspective, we create a new scroll data via the
6663 // UpdateScrollData call because that scenario is more complex. Otherwise,
6664 // if we don't contain any ASRs then just stash the transform on the
6665 // StackingContextHelper and apply it to any scroll data that are created
6666 // inside this nsDisplayTransform.
6667 deferredTransformItem = this;
6670 // Determine if we're possibly animated (= would need an active layer in FLB).
6671 bool animated = !mIsTransformSeparator &&
6672 ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
6674 wr::StackingContextParams params;
6675 params.mBoundTransform = &newTransformMatrix;
6676 params.animation = animationsId ? &prop : nullptr;
6678 wr::WrTransformInfo transform_info;
6679 if (transformForSC) {
6680 transform_info.transform = wr::ToLayoutTransform(newTransformMatrix);
6681 transform_info.key = key;
6682 params.mTransformPtr = &transform_info;
6683 } else {
6684 params.mTransformPtr = nullptr;
6687 params.prim_flags = !BackfaceIsHidden()
6688 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
6689 : wr::PrimitiveFlags{0};
6690 params.paired_with_perspective = mHasAssociatedPerspective;
6691 params.mDeferredTransformItem = deferredTransformItem;
6692 params.mAnimated = animated;
6693 // Determine if we would have to rasterize any items in local raster space
6694 // (i.e. disable subpixel AA). We don't always need to rasterize locally even
6695 // if the stacking context is possibly animated (at the cost of potentially
6696 // some false negatives with respect to will-change handling), so we pass in
6697 // this determination separately to accurately match with when FLB would
6698 // normally disable subpixel AA.
6699 params.mRasterizeLocally = animated && Frame()->HasAnimationOfTransform();
6700 params.SetPreserve3D(mFrame->Extend3DContext() && !mIsTransformSeparator);
6701 params.clip =
6702 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
6704 LayoutDeviceSize boundsSize = LayoutDeviceSize::FromAppUnits(
6705 mChildBounds.Size(), mFrame->PresContext()->AppUnitsPerDevPixel());
6707 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
6708 params, LayoutDeviceRect(position, boundsSize));
6710 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6711 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
6712 return true;
6715 bool nsDisplayTransform::UpdateScrollData(
6716 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
6717 if (ShouldDeferTransform()) {
6718 // This case is handled in CreateWebRenderCommands by stashing the transform
6719 // on the stacking context.
6720 return false;
6722 if (aLayerData) {
6723 aLayerData->SetTransform(GetTransform().GetMatrix());
6724 aLayerData->SetTransformIsPerspective(mFrame->ChildrenHavePerspective());
6726 return true;
6729 bool nsDisplayTransform::ShouldSkipTransform(
6730 nsDisplayListBuilder* aBuilder) const {
6731 return (aBuilder->RootReferenceFrame() == mFrame) &&
6732 aBuilder->IsForGenerateGlyphMask();
6735 void nsDisplayTransform::Collect3DTransformLeaves(
6736 nsDisplayListBuilder* aBuilder, nsTArray<nsDisplayTransform*>& aLeaves) {
6737 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6738 aLeaves.AppendElement(this);
6739 return;
6742 FlattenedDisplayListIterator iter(aBuilder, &mChildren);
6743 while (iter.HasNext()) {
6744 nsDisplayItem* item = iter.GetNextItem();
6745 if (item->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
6746 auto* perspective = static_cast<nsDisplayPerspective*>(item);
6747 if (!perspective->GetChildren()->GetTop()) {
6748 continue;
6750 item = perspective->GetChildren()->GetTop();
6752 if (item->GetType() != DisplayItemType::TYPE_TRANSFORM) {
6753 gfxCriticalError() << "Invalid child item within 3D transform of type: "
6754 << item->Name();
6755 continue;
6757 static_cast<nsDisplayTransform*>(item)->Collect3DTransformLeaves(aBuilder,
6758 aLeaves);
6762 static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
6763 const gfx::Polygon& aPolygon) {
6764 MOZ_ASSERT(!aPolygon.IsEmpty());
6766 RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
6767 const nsTArray<Point4D>& points = aPolygon.GetPoints();
6769 pathBuilder->MoveTo(points[0].As2DPoint());
6771 for (size_t i = 1; i < points.Length(); ++i) {
6772 pathBuilder->LineTo(points[i].As2DPoint());
6775 pathBuilder->Close();
6776 return pathBuilder->Finish();
6779 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6780 nsDisplayListBuilder* aBuilder, nsTArray<TransformPolygon>& aLeaves) {
6781 std::list<TransformPolygon> inputLayers;
6783 nsTArray<nsDisplayTransform*> leaves;
6784 Collect3DTransformLeaves(aBuilder, leaves);
6785 for (nsDisplayTransform* item : leaves) {
6786 auto bounds = LayoutDeviceRect::FromAppUnits(
6787 item->mChildBounds, item->mFrame->PresContext()->AppUnitsPerDevPixel());
6788 Matrix4x4 transform = item->GetAccumulatedPreserved3DTransform(aBuilder);
6790 if (!IsFrameVisible(item->mFrame, transform)) {
6791 continue;
6793 gfx::Polygon polygon =
6794 gfx::Polygon::FromRect(gfx::Rect(bounds.ToUnknownRect()));
6796 polygon.TransformToScreenSpace(transform);
6798 if (polygon.GetPoints().Length() >= 3) {
6799 inputLayers.push_back(TransformPolygon(item, std::move(polygon)));
6803 if (inputLayers.empty()) {
6804 return;
6807 BSPTree<nsDisplayTransform> tree(inputLayers);
6808 nsTArray<TransformPolygon> orderedLayers(tree.GetDrawOrder());
6810 for (TransformPolygon& polygon : orderedLayers) {
6811 Matrix4x4 inverse =
6812 polygon.data->GetAccumulatedPreserved3DTransform(aBuilder).Inverse();
6814 MOZ_ASSERT(polygon.geometry);
6815 polygon.geometry->TransformToLayerSpace(inverse);
6818 aLeaves = std::move(orderedLayers);
6821 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder,
6822 gfxContext* aCtx) {
6823 Paint(aBuilder, aCtx, Nothing());
6826 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
6827 const Maybe<gfx::Polygon>& aPolygon) {
6828 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6829 MOZ_ASSERT(!aPolygon);
6830 nsTArray<TransformPolygon> leaves;
6831 CollectSorted3DTransformLeaves(aBuilder, leaves);
6832 for (TransformPolygon& item : leaves) {
6833 item.data->Paint(aBuilder, aCtx, item.geometry);
6835 return;
6838 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
6839 Matrix4x4 trans = ShouldSkipTransform(aBuilder)
6840 ? Matrix4x4()
6841 : GetAccumulatedPreserved3DTransform(aBuilder);
6842 if (!IsFrameVisible(mFrame, trans)) {
6843 return;
6846 Matrix trans2d;
6847 if (trans.CanDraw2D(&trans2d)) {
6848 aCtx->Multiply(ThebesMatrix(trans2d));
6850 if (aPolygon) {
6851 RefPtr<gfx::Path> path =
6852 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6853 aCtx->GetDrawTarget()->PushClip(path);
6856 GetChildren()->Paint(aBuilder, aCtx,
6857 mFrame->PresContext()->AppUnitsPerDevPixel());
6859 if (aPolygon) {
6860 aCtx->GetDrawTarget()->PopClip();
6862 return;
6865 // TODO: Implement 3d transform handling, including plane splitting and
6866 // sorting. See BasicCompositor.
6867 auto pixelBounds = LayoutDeviceRect::FromAppUnitsToOutside(
6868 mChildBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
6869 RefPtr<DrawTarget> untransformedDT =
6870 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6871 IntSize(pixelBounds.Width(), pixelBounds.Height()),
6872 SurfaceFormat::B8G8R8A8, true);
6873 if (!untransformedDT || !untransformedDT->IsValid()) {
6874 return;
6876 untransformedDT->SetTransform(
6877 Matrix::Translation(-Point(pixelBounds.X(), pixelBounds.Y())));
6879 gfxContext groupTarget(untransformedDT, /* aPreserveTransform */ true);
6881 if (aPolygon) {
6882 RefPtr<gfx::Path> path =
6883 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6884 aCtx->GetDrawTarget()->PushClip(path);
6887 GetChildren()->Paint(aBuilder, &groupTarget,
6888 mFrame->PresContext()->AppUnitsPerDevPixel());
6890 if (aPolygon) {
6891 aCtx->GetDrawTarget()->PopClip();
6894 RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
6896 trans.PreTranslate(pixelBounds.X(), pixelBounds.Y(), 0);
6897 aCtx->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf, trans);
6900 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
6901 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6902 // completely bypass the main thread for this animation, so it is always
6903 // worthwhile.
6904 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6905 // already involved so there is less to be gained.
6906 // Therefore we check that the *post-transform* bounds of this item are
6907 // big enough to justify an active layer.
6908 return EffectCompositor::HasAnimationsForCompositor(
6909 mFrame, DisplayItemType::TYPE_TRANSFORM) ||
6910 (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame));
6913 nsRect nsDisplayTransform::TransformUntransformedBounds(
6914 nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
6915 bool snap;
6916 const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
6917 // GetTransform always operates in dev pixels.
6918 const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
6919 return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
6920 factor);
6924 * Returns the bounds for this transform. The bounds are calculated during
6925 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6927 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
6928 bool* aSnap) const {
6929 *aSnap = false;
6930 return mBounds;
6933 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
6934 MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
6936 /* Some transforms can get empty bounds in 2D, but might get transformed again
6937 * and get non-empty bounds. A simple example of this would be a 180 degree
6938 * rotation getting applied twice.
6939 * We should not depend on transforming bounds level by level.
6941 * This function collects the bounds of this transform and stores it in
6942 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6943 * down and include the bounds of the child transforms.
6944 * The bounds are transformed with the accumulated transformation matrix up to
6945 * the 3D context root coordinate space.
6947 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6948 accTransform.Accumulate(GetTransform().GetMatrix());
6950 // Do not dive into another 3D context.
6951 if (!IsLeafOf3DContext()) {
6952 for (nsDisplayItem* i : *GetChildren()) {
6953 i->DoUpdateBoundsPreserves3D(aBuilder);
6957 /* The child transforms that extend 3D context further will have empty bounds,
6958 * so the untransformed bounds here is the bounds of all the non-preserve-3d
6959 * content under this transform.
6961 const nsRect rect = TransformUntransformedBounds(
6962 aBuilder, accTransform.GetCurrentTransform());
6963 aBuilder->AccumulateRect(rect);
6966 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6967 nsDisplayListBuilder* aBuilder) {
6968 MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
6969 IsTransformSeparator());
6970 // Updating is not going through to child 3D context.
6971 ComputeBounds(aBuilder);
6974 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
6975 UpdateUntransformedBounds(aBuilder);
6977 if (IsTransformSeparator()) {
6978 MOZ_ASSERT(GetTransform().IsIdentity());
6979 mBounds = mChildBounds;
6980 return;
6983 if (mFrame->Extend3DContext()) {
6984 if (!Combines3DTransformWithAncestors()) {
6985 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6986 // collect the bounds from the child transforms.
6987 UpdateBoundsFor3D(aBuilder);
6988 } else {
6989 // With nested 3D transforms, the 2D bounds might not be useful.
6990 mBounds = nsRect();
6993 return;
6996 MOZ_ASSERT(!mFrame->Extend3DContext());
6998 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
6999 // but mix-blend-mode relies on having bounds set. See bug 1556956.
7001 // A stand-alone transform.
7002 mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
7005 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
7006 MOZ_ASSERT(mFrame->Extend3DContext() &&
7007 !mFrame->Combines3DTransformWithAncestors() &&
7008 !IsTransformSeparator());
7010 // Always start updating from an establisher of a 3D rendering context.
7011 nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
7012 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
7013 accTransform.StartRoot();
7014 ComputeBounds(aBuilder);
7015 mBounds = aBuilder->GetAccumulatedRect();
7018 void nsDisplayTransform::UpdateUntransformedBounds(
7019 nsDisplayListBuilder* aBuilder) {
7020 mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
7021 aBuilder, mActiveScrolledRoot);
7024 #ifdef DEBUG_HIT
7025 # include <time.h>
7026 #endif
7028 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
7029 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
7030 const nsRect& aRect, HitTestState* aState,
7031 nsTArray<nsIFrame*>* aOutFrames) {
7032 if (aState->mInPreserves3D) {
7033 GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
7034 return;
7037 /* Here's how this works:
7038 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
7039 * anything).
7040 * 2. Invert the matrix.
7041 * 3. Use it to transform the rect into the correct space.
7042 * 4. Pass that rect down through to the list's version of HitTest.
7044 // GetTransform always operates in dev pixels.
7045 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7046 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7048 if (!IsFrameVisible(mFrame, matrix)) {
7049 return;
7052 const bool oldHitOccludingItem = aState->mHitOccludingItem;
7054 /* We want to go from transformed-space to regular space.
7055 * Thus we have to invert the matrix, which normally does
7056 * the reverse operation (e.g. regular->transformed)
7059 /* Now, apply the transform and pass it down the channel. */
7060 matrix.Invert();
7061 nsRect resultingRect;
7062 // Magic width/height indicating we're hit testing a point, not a rect
7063 const bool testingPoint = aRect.width == 1 && aRect.height == 1;
7064 if (testingPoint) {
7065 Point4D point =
7066 matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
7067 NSAppUnitsToFloatPixels(aRect.y, factor)));
7068 if (!point.HasPositiveWCoord()) {
7069 return;
7072 Point point2d = point.As2DPoint();
7074 resultingRect =
7075 nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
7076 NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1);
7078 } else {
7079 Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
7080 NSAppUnitsToFloatPixels(aRect.y, factor),
7081 NSAppUnitsToFloatPixels(aRect.width, factor),
7082 NSAppUnitsToFloatPixels(aRect.height, factor));
7084 Rect childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds.x, factor),
7085 NSAppUnitsToFloatPixels(mChildBounds.y, factor),
7086 NSAppUnitsToFloatPixels(mChildBounds.width, factor),
7087 NSAppUnitsToFloatPixels(mChildBounds.height, factor));
7089 Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
7091 resultingRect =
7092 nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
7093 NSFloatPixelsToAppUnits(float(rect.Y()), factor),
7094 NSFloatPixelsToAppUnits(float(rect.Width()), factor),
7095 NSFloatPixelsToAppUnits(float(rect.Height()), factor));
7098 if (resultingRect.IsEmpty()) {
7099 return;
7102 #ifdef DEBUG_HIT
7103 printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
7104 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(),
7105 resultingRect.Y());
7106 uint32_t originalFrameCount = aOutFrames.Length();
7107 #endif
7109 GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
7111 if (aState->mHitOccludingItem && !testingPoint &&
7112 !mChildBounds.Contains(aRect)) {
7113 MOZ_ASSERT(aBuilder->HitTestIsForVisibility());
7114 // We're hit-testing a rect that's bigger than our child bounds, but
7115 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so
7116 // we can't stop hit-testing altogether.
7118 // FIXME(emilio): I think this means that theoretically we might include
7119 // some frames fully behind other transformed-but-opaque frames? Then again
7120 // that's our pre-existing behavior for other untransformed content that
7121 // doesn't fill the whole rect. To be fully correct I think we'd need proper
7122 // "known occluded region" tracking, but that might be overkill for our
7123 // purposes here.
7124 aState->mHitOccludingItem = oldHitOccludingItem;
7127 #ifdef DEBUG_HIT
7128 if (originalFrameCount != aOutFrames.Length())
7129 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7130 dynamic_cast<void*>(aOutFrames.ElementAt(0)));
7131 printf("=== end of hit test ===\n");
7132 #endif
7135 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
7136 const nsPoint& aPoint) {
7137 // GetTransform always operates in dev pixels.
7138 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7139 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7141 NS_ASSERTION(IsFrameVisible(mFrame, matrix),
7142 "We can't have hit a frame that isn't visible!");
7144 Matrix4x4 inverse = matrix;
7145 inverse.Invert();
7146 Point4D point =
7147 inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
7148 NSAppUnitsToFloatPixels(aPoint.y, factor)));
7150 Point point2d = point.As2DPoint();
7152 Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
7153 return transformed.z;
7156 /* The transform is opaque iff the transform consists solely of scales and
7157 * translations and if the underlying content is opaque. Thus if the transform
7158 * is of the form
7160 * |a c e|
7161 * |b d f|
7162 * |0 0 1|
7164 * We need b and c to be zero.
7166 * We also need to check whether the underlying opaque content completely fills
7167 * our visible rect. We use UntransformRect which expands to the axis-aligned
7168 * bounding rect, but that's OK since if
7169 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7170 * certainly contains the actual (non-axis-aligned) untransformed rect.
7172 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7173 bool* aSnap) const {
7174 *aSnap = false;
7176 nsRect untransformedVisible;
7177 if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
7178 return nsRegion();
7181 const Matrix4x4Flagged& matrix = GetTransform();
7182 Matrix matrix2d;
7183 if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
7184 return nsRegion();
7187 nsRegion result;
7189 bool tmpSnap;
7190 const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
7191 const nsRegion opaque =
7192 ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
7194 if (opaque.Contains(untransformedVisible)) {
7195 result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
7197 return result;
7200 nsRect nsDisplayTransform::GetComponentAlphaBounds(
7201 nsDisplayListBuilder* aBuilder) const {
7202 if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
7203 return nsRect();
7206 bool snap;
7207 return GetBounds(aBuilder, &snap);
7210 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7211 * the smallest rectangle (in app space) containing the transformed image of
7212 * that rectangle. That is, it takes the four corners of the rectangle,
7213 * transforms them according to the matrix associated with the specified frame,
7214 * then returns the smallest rectangle containing the four transformed points.
7216 * @param aUntransformedBounds The rectangle (in app units) to transform.
7217 * @param aFrame The frame whose transformation should be applied.
7218 * @param aOrigin The delta from the frame origin to the coordinate space origin
7219 * @return The smallest rectangle containing the image of the transformed
7220 * rectangle.
7222 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds,
7223 const nsIFrame* aFrame,
7224 TransformReferenceBox& aRefBox) {
7225 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7227 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7229 FrameTransformProperties props(aFrame, aRefBox, factor);
7230 return nsLayoutUtils::MatrixTransformRect(
7231 aUntransformedBounds,
7232 GetResultingTransformMatrixInternal(props, aRefBox, nsPoint(), factor,
7233 kTransformRectFlags),
7234 factor);
7237 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7238 const nsRect& aChildBounds,
7239 const nsIFrame* aFrame,
7240 nsRect* aOutRect) {
7241 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7243 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7244 Matrix4x4 transform = GetResultingTransformMatrix(aFrame, nsPoint(), factor,
7245 kTransformRectFlags);
7246 return UntransformRect(aTransformedBounds, aChildBounds, transform, factor,
7247 aOutRect);
7250 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7251 const nsRect& aChildBounds,
7252 const Matrix4x4& aMatrix,
7253 float aAppUnitsPerPixel,
7254 nsRect* aOutRect) {
7255 if (aMatrix.IsSingular()) {
7256 return false;
7259 RectDouble result(
7260 NSAppUnitsToFloatPixels(aTransformedBounds.x, aAppUnitsPerPixel),
7261 NSAppUnitsToFloatPixels(aTransformedBounds.y, aAppUnitsPerPixel),
7262 NSAppUnitsToFloatPixels(aTransformedBounds.width, aAppUnitsPerPixel),
7263 NSAppUnitsToFloatPixels(aTransformedBounds.height, aAppUnitsPerPixel));
7265 RectDouble childGfxBounds(
7266 NSAppUnitsToFloatPixels(aChildBounds.x, aAppUnitsPerPixel),
7267 NSAppUnitsToFloatPixels(aChildBounds.y, aAppUnitsPerPixel),
7268 NSAppUnitsToFloatPixels(aChildBounds.width, aAppUnitsPerPixel),
7269 NSAppUnitsToFloatPixels(aChildBounds.height, aAppUnitsPerPixel));
7271 result = aMatrix.Inverse().ProjectRectBounds(result, childGfxBounds);
7272 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result),
7273 aAppUnitsPerPixel);
7274 return true;
7277 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder,
7278 const nsRect& aRect,
7279 nsRect* aOutRect) const {
7280 if (GetTransform().IsSingular()) {
7281 return false;
7284 // GetTransform always operates in dev pixels.
7285 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7286 RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor),
7287 NSAppUnitsToFloatPixels(aRect.y, factor),
7288 NSAppUnitsToFloatPixels(aRect.width, factor),
7289 NSAppUnitsToFloatPixels(aRect.height, factor));
7291 bool snap;
7292 nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
7293 RectDouble childGfxBounds(
7294 NSAppUnitsToFloatPixels(childBounds.x, factor),
7295 NSAppUnitsToFloatPixels(childBounds.y, factor),
7296 NSAppUnitsToFloatPixels(childBounds.width, factor),
7297 NSAppUnitsToFloatPixels(childBounds.height, factor));
7299 /* We want to untransform the matrix, so invert the transformation first! */
7300 result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds);
7302 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
7304 return true;
7307 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) {
7308 aStream << GetTransform().GetMatrix();
7309 if (IsTransformSeparator()) {
7310 aStream << " transform-separator";
7312 if (IsLeafOf3DContext()) {
7313 aStream << " 3d-context-leaf";
7315 if (mFrame->Extend3DContext()) {
7316 aStream << " extends-3d-context";
7318 if (mFrame->Combines3DTransformWithAncestors()) {
7319 aStream << " combines-3d-with-ancestors";
7322 aStream << " prerender(";
7323 switch (mPrerenderDecision) {
7324 case PrerenderDecision::No:
7325 aStream << "no";
7326 break;
7327 case PrerenderDecision::Partial:
7328 aStream << "partial";
7329 break;
7330 case PrerenderDecision::Full:
7331 aStream << "full";
7332 break;
7334 aStream << ")";
7335 aStream << " childrenBuildingRect" << mChildrenBuildingRect;
7338 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
7339 nsIFrame* aFrame,
7340 nsDisplayList* aList)
7341 : nsPaintedDisplayItem(aBuilder, aFrame), mList(aBuilder) {
7342 mList.AppendToTop(aList);
7343 MOZ_ASSERT(mList.Length() == 1);
7344 MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
7347 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder,
7348 gfxContext* aCtx) {
7349 // Just directly recurse into children, since we'll include the persepctive
7350 // value in any nsDisplayTransform children.
7351 GetChildren()->Paint(aBuilder, aCtx,
7352 mFrame->PresContext()->AppUnitsPerDevPixel());
7355 nsRegion nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7356 bool* aSnap) const {
7357 if (!GetChildren()->GetTop()) {
7358 *aSnap = false;
7359 return nsRegion();
7362 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder, aSnap);
7365 bool nsDisplayPerspective::CreateWebRenderCommands(
7366 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7367 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7368 nsDisplayListBuilder* aDisplayListBuilder) {
7369 float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
7370 Matrix4x4 perspectiveMatrix;
7371 DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
7372 mFrame, appUnitsPerPixel, perspectiveMatrix);
7373 MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
7376 * ClipListToRange can remove our child after we were created.
7378 if (!GetChildren()->GetTop()) {
7379 return false;
7383 * The resulting matrix is still in the coordinate space of the transformed
7384 * frame. Append a translation to the reference frame coordinates.
7386 nsDisplayTransform* transform =
7387 static_cast<nsDisplayTransform*>(GetChildren()->GetTop());
7389 Point3D newOrigin =
7390 Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
7391 appUnitsPerPixel),
7392 NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
7393 appUnitsPerPixel),
7394 0.0f);
7395 Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
7397 perspectiveMatrix.PostTranslate(roundedOrigin);
7399 nsIFrame* perspectiveFrame =
7400 mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
7402 // Passing true here is always correct, since perspective always combines
7403 // transforms with the descendants. However that'd make WR do a lot of work
7404 // that it doesn't really need to do if there aren't other transforms forming
7405 // part of the 3D context.
7407 // WR knows how to treat perspective in that case, so the only thing we need
7408 // to do is to ensure we pass true when we're involved in a 3d context in any
7409 // other way via the transform-style property on either the transformed frame
7410 // or the perspective frame in order to not confuse WR's preserve-3d code in
7411 // very awful ways.
7412 bool preserve3D =
7413 mFrame->Extend3DContext() || perspectiveFrame->Extend3DContext();
7415 wr::StackingContextParams params;
7417 wr::WrTransformInfo transform_info;
7418 transform_info.transform = wr::ToLayoutTransform(perspectiveMatrix);
7419 transform_info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
7420 wr::SpatialKeyKind::Perspective);
7421 params.mTransformPtr = &transform_info;
7423 params.reference_frame_kind = wr::WrReferenceFrameKind::Perspective;
7424 params.prim_flags = !BackfaceIsHidden()
7425 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7426 : wr::PrimitiveFlags{0};
7427 params.SetPreserve3D(preserve3D);
7428 params.clip =
7429 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
7431 Maybe<uint64_t> scrollingRelativeTo;
7432 for (const auto* asr = GetActiveScrolledRoot(); asr; asr = asr->mParent) {
7433 // In OOP documents, the root scrollable frame of the in-process root
7434 // document is always active, so using IsAncestorFrameCrossDocInProcess
7435 // should be fine here.
7436 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7437 asr->mScrollableFrame->GetScrolledFrame(), perspectiveFrame)) {
7438 scrollingRelativeTo.emplace(asr->GetViewId());
7439 break;
7443 // We put the perspective reference frame wrapping the transformed frame,
7444 // even though there may be arbitrarily nested scroll frames in between.
7446 // We need to know how many ancestor scroll-frames are we nested in, in order
7447 // for the async scrolling code in WebRender to calculate the right
7448 // transformation for the perspective contents.
7449 params.scrolling_relative_to = scrollingRelativeTo.ptrOr(nullptr);
7451 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
7452 params);
7454 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7455 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
7457 return true;
7460 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
7461 nsTextFrame* aFrame)
7462 : nsPaintedDisplayItem(aBuilder, aFrame),
7463 mVisIStartEdge(0),
7464 mVisIEndEdge(0) {
7465 MOZ_COUNT_CTOR(nsDisplayText);
7466 mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7467 // Bug 748228
7468 mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
7469 mVisibleRect = aBuilder->GetVisibleRect() +
7470 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
7473 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager,
7474 nsDisplayListBuilder* aBuilder) const {
7475 auto* f = static_cast<nsTextFrame*>(mFrame);
7477 if (f->IsSelected()) {
7478 return false;
7481 const nsStyleText* textStyle = f->StyleText();
7482 if (textStyle->HasTextShadow()) {
7483 return false;
7486 nsTextFrame::TextDecorations decorations;
7487 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7488 decorations);
7489 return !decorations.HasDecorationLines();
7492 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
7493 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
7494 // We don't pass mVisibleRect here, since this can be called from within
7495 // the WebRender fallback painting path, and we don't want to issue
7496 // recorded commands that are dependent on the visible/building rect.
7497 RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
7499 auto* textFrame = static_cast<nsTextFrame*>(mFrame);
7500 LCPTextFrameHelper::MaybeUnionTextFrame(textFrame,
7501 mBounds - ToReferenceFrame());
7504 bool nsDisplayText::CreateWebRenderCommands(
7505 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7506 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7507 nsDisplayListBuilder* aDisplayListBuilder) {
7508 auto* f = static_cast<nsTextFrame*>(mFrame);
7509 auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
7511 nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
7512 // Bug 748228
7513 bounds.Inflate(appUnitsPerDevPixel);
7515 if (bounds.IsEmpty()) {
7516 return true;
7519 // For large font sizes, punt to a blob image, to avoid the blurry rendering
7520 // that results from WR clamping the glyph size used for rasterization.
7522 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7524 // This is not strictly accurate, as final used font sizes might not be the
7525 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7526 // altering the used size of the font actually used).
7527 // It also fails to consider how transforms might affect the device-font-size
7528 // that webrender uses (and clamps).
7529 // But it should be near enough for practical purposes; the limitations just
7530 // mean we might sometimes end up with webrender still applying some bitmap
7531 // scaling, or bail out when we didn't really need to.
7532 constexpr float kWebRenderFontSizeLimit = 320.0;
7533 f->EnsureTextRun(nsTextFrame::eInflated);
7534 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7535 if (textRun &&
7536 textRun->GetFontGroup()->GetStyle()->size > kWebRenderFontSizeLimit) {
7537 return false;
7540 gfx::Point deviceOffset =
7541 LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
7542 .ToUnknownPoint();
7544 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7545 // frames) lets us early reject a bunch of things.
7546 nsRect visible = mVisibleRect;
7548 // Add the "source rect" area from which the given shadows could intersect
7549 // with mVisibleRect, and which therefore needs to included in the paint
7550 // operation, to the `visible` rect that we will use to limit the bounds of
7551 // what we send to the renderer.
7552 auto addShadowSourceToVisible = [&](Span<const StyleSimpleShadow> aShadows) {
7553 for (const auto& shadow : aShadows) {
7554 nsRect sourceRect = mVisibleRect;
7555 // Negate the offsets, because we're looking for the "source" rect that
7556 // could cast a shadow into the visible rect, rather than a "target" area
7557 // onto which the visible rect would cast a shadow.
7558 sourceRect.MoveBy(-shadow.horizontal.ToAppUnits(),
7559 -shadow.vertical.ToAppUnits());
7560 // Inflate to account for the shadow blur.
7561 sourceRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
7562 shadow.blur.ToAppUnits(), appUnitsPerDevPixel));
7563 visible.OrWith(sourceRect);
7567 // Shadows can translate things back into view, so we enlarge the notional
7568 // "visible" rect to ensure we don't skip painting relevant parts that might
7569 // cast a shadow within the visible area.
7570 addShadowSourceToVisible(f->StyleText()->mTextShadow.AsSpan());
7572 // Similarly for shadows that may be cast by ::selection.
7573 if (f->IsSelected()) {
7574 nsTextPaintStyle textPaint(f);
7575 Span<const StyleSimpleShadow> shadows;
7576 f->GetSelectionTextShadow(SelectionType::eNormal, textPaint, &shadows);
7577 addShadowSourceToVisible(shadows);
7580 // Inflate a little extra to allow for potential antialiasing "blur".
7581 visible.Inflate(3 * appUnitsPerDevPixel);
7582 bounds = bounds.Intersect(visible);
7584 gfxContext* textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager,
7585 this, bounds, deviceOffset);
7587 LCPTextFrameHelper::MaybeUnionTextFrame(f, bounds - ToReferenceFrame());
7589 aBuilder.StartGroup(this);
7591 RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
7592 aBuilder.GetInheritedOpacity(), true);
7593 const bool result = textDrawer->GetTextDrawer()->Finish();
7595 if (result) {
7596 aBuilder.FinishGroup();
7597 } else {
7598 aBuilder.CancelGroup(true);
7601 return result;
7604 void nsDisplayText::RenderToContext(gfxContext* aCtx,
7605 nsDisplayListBuilder* aBuilder,
7606 const nsRect& aVisibleRect, float aOpacity,
7607 bool aIsRecording) {
7608 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7610 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7611 // antialiased pixels beyond the measured text extents.
7612 // This is temporary until we do this in the actual calculation of text
7613 // extents.
7614 auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
7615 LayoutDeviceRect extraVisible =
7616 LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D);
7617 extraVisible.Inflate(1);
7619 gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width,
7620 extraVisible.height);
7621 pixelVisible.Inflate(2);
7622 pixelVisible.RoundOut();
7624 gfxClipAutoSaveRestore autoSaveClip(aCtx);
7625 if (!aBuilder->IsForGenerateGlyphMask() && !aIsRecording) {
7626 autoSaveClip.Clip(pixelVisible);
7629 NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
7630 NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
7632 gfxContextMatrixAutoSaveRestore matrixSR;
7634 nsPoint framePt = ToReferenceFrame();
7635 if (f->Style()->IsTextCombined()) {
7636 float scaleFactor = nsTextFrame::GetTextCombineScaleFactor(f);
7637 if (scaleFactor != 1.0f) {
7638 if (auto* textDrawer = aCtx->GetTextDrawer()) {
7639 // WebRender doesn't support scaling text like this yet
7640 textDrawer->FoundUnsupportedFeature();
7641 return;
7643 matrixSR.SetContext(aCtx);
7644 // Setup matrix to compress text for text-combine-upright if
7645 // necessary. This is done here because we want selection be
7646 // compressed at the same time as text.
7647 gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
7648 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7649 if (textRun && textRun->IsRightToLeft()) {
7650 pt.x += gfxFloat(f->GetSize().width) / A2D;
7652 gfxMatrix mat = aCtx->CurrentMatrixDouble()
7653 .PreTranslate(pt)
7654 .PreScale(scaleFactor, 1.0)
7655 .PreTranslate(-pt);
7656 aCtx->SetMatrixDouble(mat);
7659 nsTextFrame::PaintTextParams params(aCtx);
7660 params.framePt = gfx::Point(framePt.x, framePt.y);
7661 params.dirtyRect = extraVisible;
7663 if (aBuilder->IsForGenerateGlyphMask()) {
7664 params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
7665 } else {
7666 params.state = nsTextFrame::PaintTextParams::PaintText;
7669 f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
7670 f->IsSelected(), aOpacity);
7673 // This could go to nsDisplayListInvalidation.h, but
7674 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7675 // would produce circular dependencies.
7676 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
7677 public:
7678 nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
7679 : nsDisplayItemGenericGeometry(aItem, aBuilder),
7680 mVisIStartEdge(aItem->VisIStartEdge()),
7681 mVisIEndEdge(aItem->VisIEndEdge()) {
7682 nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
7683 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7684 mDecorations);
7688 * We store the computed text decorations here since they are
7689 * computed using style data from parent frames. Any changes to these
7690 * styles will only invalidate the parent frame and not this frame.
7692 nsTextFrame::TextDecorations mDecorations;
7693 nscoord mVisIStartEdge;
7694 nscoord mVisIEndEdge;
7697 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry(
7698 nsDisplayListBuilder* aBuilder) {
7699 return new nsDisplayTextGeometry(this, aBuilder);
7702 void nsDisplayText::ComputeInvalidationRegion(
7703 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7704 nsRegion* aInvalidRegion) const {
7705 const nsDisplayTextGeometry* geometry =
7706 static_cast<const nsDisplayTextGeometry*>(aGeometry);
7707 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7709 nsTextFrame::TextDecorations decorations;
7710 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7711 decorations);
7713 bool snap;
7714 const nsRect& newRect = geometry->mBounds;
7715 nsRect oldRect = GetBounds(aBuilder, &snap);
7716 if (decorations != geometry->mDecorations ||
7717 mVisIStartEdge != geometry->mVisIStartEdge ||
7718 mVisIEndEdge != geometry->mVisIEndEdge ||
7719 !oldRect.IsEqualInterior(newRect) ||
7720 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
7721 aInvalidRegion->Or(oldRect, newRect);
7725 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) {
7726 #ifdef DEBUG
7727 aStream << " (\"";
7729 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7730 nsCString buf;
7731 f->ToCString(buf);
7733 aStream << buf.get() << "\")";
7734 #endif
7737 nsDisplayEffectsBase::nsDisplayEffectsBase(
7738 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7739 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
7740 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
7741 aClearClipChain) {
7742 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7745 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
7746 nsIFrame* aFrame,
7747 nsDisplayList* aList)
7748 : nsDisplayWrapList(aBuilder, aFrame, aList) {
7749 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7752 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7753 bool* aSnap) const {
7754 *aSnap = false;
7755 return nsRegion();
7758 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder* aBuilder,
7759 const nsRect& aRect, HitTestState* aState,
7760 nsTArray<nsIFrame*>* aOutFrames) {
7761 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
7762 if (SVGIntegrationUtils::HitTestFrameForEffects(
7763 mFrame, rectCenter - ToReferenceFrame())) {
7764 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
7768 gfxRect nsDisplayEffectsBase::BBoxInUserSpace() const {
7769 return SVGUtils::GetBBox(mFrame);
7772 gfxPoint nsDisplayEffectsBase::UserSpaceOffset() const {
7773 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
7776 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7777 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7778 nsRegion* aInvalidRegion) const {
7779 const auto* geometry =
7780 static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
7781 bool snap;
7782 nsRect bounds = GetBounds(aBuilder, &snap);
7783 if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
7784 geometry->mUserSpaceOffset != UserSpaceOffset() ||
7785 !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
7786 // Filter and mask output can depend on the location of the frame's user
7787 // space and on the frame's BBox. We need to invalidate if either of these
7788 // change relative to the reference frame.
7789 // Invalidations from our inactive layer manager are not enough to catch
7790 // some of these cases because filters can produce output even if there's
7791 // nothing in the filter input.
7792 aInvalidRegion->Or(bounds, geometry->mBounds);
7796 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7797 if (mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
7798 ISVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame);
7799 if (!svgFrame) {
7800 return false;
7802 if (auto* svgElement = SVGElement::FromNode(mFrame->GetContent())) {
7803 // The SVG spec says only to draw filters if the element
7804 // has valid dimensions.
7805 return svgElement->HasValidDimensions();
7807 return false;
7810 return true;
7813 using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams;
7815 static void ComputeMaskGeometry(PaintFramesParams& aParams) {
7816 // Properties are added lazily and may have been removed by a restyle, so
7817 // make sure all applicable ones are set again.
7818 nsIFrame* firstFrame =
7819 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
7821 const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
7823 nsTArray<SVGMaskFrame*> maskFrames;
7824 // XXX check return value?
7825 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
7827 if (maskFrames.Length() == 0) {
7828 return;
7831 gfxContext& ctx = aParams.ctx;
7832 nsIFrame* frame = aParams.frame;
7834 nsPoint offsetToUserSpace =
7835 nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame);
7837 auto cssToDevScale = frame->PresContext()->CSSToDevPixelScale();
7838 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
7840 gfxPoint devPixelOffsetToUserSpace =
7841 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, appUnitsPerDevPixel);
7843 gfxContextMatrixAutoSaveRestore matSR(&ctx);
7844 ctx.SetMatrixDouble(
7845 ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace));
7847 // Convert boaderArea and dirtyRect to user space.
7848 nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
7849 nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
7851 // Union all mask layer rectangles in user space.
7852 LayoutDeviceRect maskInUserSpace;
7853 for (size_t i = 0; i < maskFrames.Length(); i++) {
7854 SVGMaskFrame* maskFrame = maskFrames[i];
7855 LayoutDeviceRect currentMaskSurfaceRect;
7857 if (maskFrame) {
7858 auto rect = maskFrame->GetMaskArea(aParams.frame);
7859 currentMaskSurfaceRect =
7860 CSSRect::FromUnknownRect(ToRect(rect)) * cssToDevScale;
7861 } else {
7862 nsCSSRendering::ImageLayerClipState clipState;
7863 nsCSSRendering::GetImageLayerClip(
7864 svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(),
7865 userSpaceBorderArea, userSpaceDirtyRect,
7866 /* aWillPaintBorder = */ false, appUnitsPerDevPixel, &clipState);
7867 currentMaskSurfaceRect = LayoutDeviceRect::FromUnknownRect(
7868 ToRect(clipState.mDirtyRectInDevPx));
7871 maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
7874 if (!maskInUserSpace.IsEmpty()) {
7875 aParams.maskRect = Some(maskInUserSpace);
7876 } else {
7877 aParams.maskRect = Nothing();
7881 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7882 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7883 const ActiveScrolledRoot* aActiveScrolledRoot, bool aWrapsBackdropFilter)
7884 : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
7885 mWrapsBackdropFilter(aWrapsBackdropFilter) {
7886 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
7888 nsPresContext* presContext = mFrame->PresContext();
7889 uint32_t flags =
7890 aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
7891 const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
7892 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7893 const auto& layer = svgReset->mMask.mLayers[i];
7894 if (!layer.mImage.IsResolved()) {
7895 continue;
7897 const nsRect& borderArea = mFrame->GetRectRelativeToSelf();
7898 // NOTE(emilio): We only care about the dest rect so we don't bother
7899 // computing a clip.
7900 bool isTransformedFixed = false;
7901 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
7902 presContext, aFrame, flags, borderArea, borderArea, layer,
7903 &isTransformedFixed);
7904 mDestRects.AppendElement(state.mDestArea);
7908 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) {
7909 // Do not merge items for box-decoration-break:clone elements,
7910 // since each box should have its own mask in that case.
7911 if (aFrame->StyleBorder()->mBoxDecorationBreak ==
7912 StyleBoxDecorationBreak::Clone) {
7913 return false;
7916 // Do not merge if either frame has a mask. Continuation frames should apply
7917 // the mask independently (just like nsDisplayBackgroundImage).
7918 if (aFrame->StyleSVGReset()->HasMask()) {
7919 return false;
7922 return true;
7925 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem* aItem) const {
7926 // Items for the same content element should be merged into a single
7927 // compositing group.
7928 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
7929 !HasSameContent(aItem)) {
7930 return false;
7933 return CanMergeDisplayMaskFrame(mFrame) &&
7934 CanMergeDisplayMaskFrame(aItem->Frame());
7937 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7938 if (!ValidateSVGFrame()) {
7939 return false;
7942 return SVGUtils::DetermineMaskUsage(mFrame, false).UsingMaskOrClipPath();
7945 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder,
7946 gfxContext* aMaskContext,
7947 bool aHandleOpacity,
7948 bool* aMaskPainted) {
7949 MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
7951 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7952 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7953 PaintFramesParams params(*aMaskContext, mFrame, mBounds, borderArea, aBuilder,
7954 aHandleOpacity, imgParams);
7955 ComputeMaskGeometry(params);
7956 bool maskIsComplete = false;
7957 bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete);
7958 if (aMaskPainted) {
7959 *aMaskPainted = painted;
7962 return maskIsComplete &&
7963 (imgParams.result == ImgDrawResult::SUCCESS ||
7964 imgParams.result == ImgDrawResult::SUCCESS_NOT_COMPLETE ||
7965 imgParams.result == ImgDrawResult::WRONG_SIZE);
7968 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7969 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7970 nsRegion* aInvalidRegion) const {
7971 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
7972 aInvalidRegion);
7974 const auto* geometry =
7975 static_cast<const nsDisplayMasksAndClipPathsGeometry*>(aGeometry);
7976 bool snap;
7977 nsRect bounds = GetBounds(aBuilder, &snap);
7979 if (mDestRects.Length() != geometry->mDestRects.Length()) {
7980 aInvalidRegion->Or(bounds, geometry->mBounds);
7981 } else {
7982 for (size_t i = 0; i < mDestRects.Length(); i++) {
7983 if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) {
7984 aInvalidRegion->Or(bounds, geometry->mBounds);
7985 break;
7991 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7992 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
7993 const std::function<void()>& aPaintChildren) {
7994 // Clip the drawing target by mVisibleRect, which contains the visible
7995 // region of the target frame and its out-of-flow and inflow descendants.
7996 Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx),
7997 mFrame->PresContext()->AppUnitsPerDevPixel());
7998 bounds.RoundOut();
7999 gfxClipAutoSaveRestore autoSaveClip(aCtx);
8000 autoSaveClip.Clip(bounds);
8002 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
8003 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8004 PaintFramesParams params(*aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
8005 borderArea, aBuilder, false, imgParams);
8007 ComputeMaskGeometry(params);
8009 SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren);
8012 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder* aBuilder,
8013 gfxContext* aCtx) {
8014 if (!IsValidMask()) {
8015 return;
8017 PaintWithContentsPaintCallback(aBuilder, aCtx, [&] {
8018 GetChildren()->Paint(aBuilder, aCtx,
8019 mFrame->PresContext()->AppUnitsPerDevPixel());
8023 static Maybe<wr::WrClipChainId> CreateSimpleClipRegion(
8024 const nsDisplayMasksAndClipPaths& aDisplayItem,
8025 wr::DisplayListBuilder& aBuilder) {
8026 nsIFrame* frame = aDisplayItem.Frame();
8027 const auto* style = frame->StyleSVGReset();
8028 MOZ_ASSERT(style->HasClipPath() || style->HasMask());
8029 if (!SVGUtils::DetermineMaskUsage(frame, false).IsSimpleClipShape()) {
8030 return Nothing();
8033 const auto& clipPath = style->mClipPath;
8034 const auto& shape = *clipPath.AsShape()._0;
8036 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8037 const nsRect refBox =
8038 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8040 wr::WrClipId clipId{};
8042 switch (shape.tag) {
8043 case StyleBasicShape::Tag::Rect: {
8044 const nsRect rect =
8045 ShapeUtils::ComputeInsetRect(shape.AsRect().rect, refBox) +
8046 aDisplayItem.ToReferenceFrame();
8048 nscoord radii[8] = {0};
8049 if (ShapeUtils::ComputeRectRadii(shape.AsRect().round, refBox, rect,
8050 radii)) {
8051 clipId = aBuilder.DefineRoundedRectClip(
8052 Nothing(),
8053 wr::ToComplexClipRegion(rect, radii, appUnitsPerDevPixel));
8054 } else {
8055 clipId = aBuilder.DefineRectClip(
8056 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8057 rect, appUnitsPerDevPixel)));
8060 break;
8062 case StyleBasicShape::Tag::Ellipse:
8063 case StyleBasicShape::Tag::Circle: {
8064 nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox);
8066 nsSize radii;
8067 if (shape.IsEllipse()) {
8068 radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox);
8069 } else {
8070 nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox);
8071 radii = {radius, radius};
8074 nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center -
8075 nsPoint(radii.width, radii.height),
8076 radii * 2);
8078 nscoord ellipseRadii[8];
8079 for (const auto corner : AllPhysicalHalfCorners()) {
8080 ellipseRadii[corner] =
8081 HalfCornerIsX(corner) ? radii.width : radii.height;
8084 clipId = aBuilder.DefineRoundedRectClip(
8085 Nothing(), wr::ToComplexClipRegion(ellipseRect, ellipseRadii,
8086 appUnitsPerDevPixel));
8088 break;
8090 default:
8091 // Please don't add more exceptions, try to find a way to define the clip
8092 // without using a mask image.
8094 // And if you _really really_ need to add an exception, add it to
8095 // SVGUtils::DetermineMaskUsage
8096 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8097 return Nothing();
8100 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8102 return Some(clipChainId);
8105 static void FillPolygonDataForDisplayItem(
8106 const nsDisplayMasksAndClipPaths& aDisplayItem,
8107 nsTArray<wr::LayoutPoint>& aPoints, wr::FillRule& aFillRule) {
8108 nsIFrame* frame = aDisplayItem.Frame();
8109 const auto* style = frame->StyleSVGReset();
8110 bool isPolygon = style->HasClipPath() && style->mClipPath.IsShape() &&
8111 style->mClipPath.AsShape()._0->IsPolygon();
8112 if (!isPolygon) {
8113 return;
8116 const auto& clipPath = style->mClipPath;
8117 const auto& shape = *clipPath.AsShape()._0;
8118 const nsRect refBox =
8119 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1);
8121 // We only fill polygon data for polygons that are below a complexity
8122 // limit.
8123 nsTArray<nsPoint> vertices =
8124 ShapeUtils::ComputePolygonVertices(shape, refBox);
8125 if (vertices.Length() > wr::POLYGON_CLIP_VERTEX_MAX) {
8126 return;
8129 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8131 for (size_t i = 0; i < vertices.Length(); ++i) {
8132 wr::LayoutPoint point = wr::ToLayoutPoint(
8133 LayoutDevicePoint::FromAppUnits(vertices[i], appUnitsPerDevPixel));
8134 aPoints.AppendElement(point);
8137 aFillRule = (shape.AsPolygon().fill == StyleFillRule::Nonzero)
8138 ? wr::FillRule::Nonzero
8139 : wr::FillRule::Evenodd;
8142 static Maybe<wr::WrClipChainId> CreateWRClipPathAndMasks(
8143 nsDisplayMasksAndClipPaths* aDisplayItem, const LayoutDeviceRect& aBounds,
8144 wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder,
8145 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8146 nsDisplayListBuilder* aDisplayListBuilder) {
8147 if (auto clip = CreateSimpleClipRegion(*aDisplayItem, aBuilder)) {
8148 return clip;
8151 Maybe<wr::ImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(
8152 aDisplayItem, aBuilder, aResources, aSc, aDisplayListBuilder, aBounds);
8153 if (!mask) {
8154 return Nothing();
8157 // We couldn't create a simple clip region, but before we create an image
8158 // mask clip, see if we can get a polygon clip to add to it.
8159 nsTArray<wr::LayoutPoint> points;
8160 wr::FillRule fillRule = wr::FillRule::Nonzero;
8161 FillPolygonDataForDisplayItem(*aDisplayItem, points, fillRule);
8163 wr::WrClipId clipId =
8164 aBuilder.DefineImageMaskClip(mask.ref(), points, fillRule);
8166 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain({clipId}, true);
8168 return Some(clipChainId);
8171 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8172 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8173 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8174 nsDisplayListBuilder* aDisplayListBuilder) {
8175 bool snap;
8176 auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8177 nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
8178 LayoutDeviceRect bounds =
8179 LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel);
8181 Maybe<wr::WrClipChainId> clip = CreateWRClipPathAndMasks(
8182 this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder);
8184 float oldOpacity = aBuilder.GetInheritedOpacity();
8186 Maybe<StackingContextHelper> layer;
8187 const StackingContextHelper* sc = &aSc;
8188 if (clip) {
8189 // Create a new stacking context to attach the mask to, ensuring the mask is
8190 // applied to the aggregate, and not the individual elements.
8192 // The stacking context shouldn't have any offset.
8193 bounds.MoveTo(0, 0);
8195 Maybe<float> opacity =
8196 (SVGUtils::DetermineMaskUsage(mFrame, false).IsSimpleClipShape() &&
8197 aBuilder.GetInheritedOpacity() != 1.0f)
8198 ? Some(aBuilder.GetInheritedOpacity())
8199 : Nothing();
8201 wr::StackingContextParams params;
8202 params.clip = wr::WrStackingContextClip::ClipChain(clip->id);
8203 params.opacity = opacity.ptrOr(nullptr);
8204 if (mWrapsBackdropFilter) {
8205 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8207 layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params,
8208 bounds);
8209 sc = layer.ptr();
8212 aBuilder.SetInheritedOpacity(1.0f);
8213 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8214 aBuilder.SetInheritedClipChain(nullptr);
8215 CreateWebRenderCommandsNewClipListOption(aBuilder, aResources, *sc, aManager,
8216 aDisplayListBuilder, layer.isSome());
8217 aBuilder.SetInheritedOpacity(oldOpacity);
8218 aBuilder.SetInheritedClipChain(oldClipChain);
8220 return true;
8223 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8224 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
8225 if (const DisplayItemClip* clip =
8226 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
8227 return Some(clip->GetClipRect());
8229 // This item does not have a clip with respect to |aASR|. However, we
8230 // might still have finite bounds with respect to |aASR|. Check our
8231 // children.
8232 nsDisplayList* childList = GetSameCoordinateSystemChildren();
8233 if (childList) {
8234 return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR));
8236 #ifdef DEBUG
8237 NS_ASSERTION(false, "item should have finite clip with respect to aASR");
8238 #endif
8239 return Nothing();
8242 #ifdef MOZ_DUMP_PAINTING
8243 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) {
8244 nsIFrame* firstFrame =
8245 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8246 bool first = true;
8247 aTo += " effects=(";
8248 SVGClipPathFrame* clipPathFrame;
8249 // XXX Check return value?
8250 SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
8251 if (clipPathFrame) {
8252 if (!first) {
8253 aTo += ", ";
8255 aTo += nsPrintfCString(
8256 "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
8257 first = false;
8258 } else if (mFrame->StyleSVGReset()->HasClipPath()) {
8259 if (!first) {
8260 aTo += ", ";
8262 aTo += "clip(basic-shape)";
8263 first = false;
8266 nsTArray<SVGMaskFrame*> masks;
8267 // XXX check return value?
8268 SVGObserverUtils::GetAndObserveMasks(firstFrame, &masks);
8269 if (!masks.IsEmpty() && masks[0]) {
8270 if (!first) {
8271 aTo += ", ";
8273 aTo += "mask";
8275 aTo += ")";
8277 #endif
8279 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8280 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8281 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8282 nsDisplayListBuilder* aDisplayListBuilder) {
8283 WrFiltersHolder wrFilters;
8284 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8285 auto filterChain = style.StyleEffects()->mBackdropFilters.AsSpan();
8286 bool initialized = true;
8287 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8288 wrFilters) &&
8289 !SVGIntegrationUtils::BuildWebRenderFilters(
8290 mFrame, filterChain, StyleFilterType::BackdropFilter, wrFilters,
8291 initialized)) {
8292 // TODO: If painting backdrop-filters on the content side is implemented,
8293 // consider returning false to fall back to that.
8294 wrFilters = {};
8297 if (!initialized) {
8298 wrFilters = {};
8301 nsCSSRendering::ImageLayerClipState clip;
8302 nsCSSRendering::GetImageLayerClip(
8303 style.StyleBackground()->BottomLayer(), mFrame, *style.StyleBorder(),
8304 mBackdropRect, mBackdropRect, false,
8305 mFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
8307 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
8308 mBackdropRect, mFrame->PresContext()->AppUnitsPerDevPixel());
8310 wr::ComplexClipRegion region =
8311 wr::ToComplexClipRegion(clip.mBGClipArea, clip.mRadii,
8312 mFrame->PresContext()->AppUnitsPerDevPixel());
8314 aBuilder.PushBackdropFilter(wr::ToLayoutRect(bounds), region,
8315 wrFilters.filters, wrFilters.filter_datas,
8316 !BackfaceIsHidden());
8318 wr::StackingContextParams params;
8319 params.clip =
8320 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8321 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8322 params);
8324 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8325 aDisplayListBuilder);
8326 return true;
8329 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder* aBuilder,
8330 gfxContext* aCtx) {
8331 // TODO: Implement backdrop filters
8332 GetChildren()->Paint(aBuilder, aCtx,
8333 mFrame->PresContext()->AppUnitsPerDevPixel());
8336 nsRect nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder* aBuilder,
8337 bool* aSnap) const {
8338 nsRect childBounds = nsDisplayWrapList::GetBounds(aBuilder, aSnap);
8340 *aSnap = false;
8342 return mBackdropRect.Union(childBounds);
8345 /* static */
8346 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder,
8347 nsIFrame* aFrame, nsDisplayList* aList,
8348 nsIFrame* aStyleFrame,
8349 bool aWrapsBackdropFilter)
8350 : nsDisplayEffectsBase(aBuilder, aFrame, aList),
8351 mStyle(aFrame == aStyleFrame ? nullptr : aStyleFrame->Style()),
8352 mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()),
8353 mWrapsBackdropFilter(aWrapsBackdropFilter) {
8354 MOZ_COUNT_CTOR(nsDisplayFilters);
8355 mVisibleRect = aBuilder->GetVisibleRect() +
8356 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
8359 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8360 PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) {
8361 GetChildren()->Paint(aBuilder, aContext,
8362 mFrame->PresContext()->AppUnitsPerDevPixel());
8366 void nsDisplayFilters::PaintWithContentsPaintCallback(
8367 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
8368 const std::function<void(gfxContext* aContext)>& aPaintChildren) {
8369 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
8370 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8371 PaintFramesParams params(*aCtx, mFrame, mVisibleRect, borderArea, aBuilder,
8372 false, imgParams);
8374 gfxPoint userSpaceToFrameSpaceOffset =
8375 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params);
8377 auto filterChain = mStyle ? mStyle->StyleEffects()->mFilters.AsSpan()
8378 : mFrame->StyleEffects()->mFilters.AsSpan();
8379 SVGIntegrationUtils::PaintFilter(
8380 params, filterChain,
8381 [&](gfxContext& aContext, imgDrawingParams&, const gfxMatrix*,
8382 const nsIntRect*) {
8383 gfxContextMatrixAutoSaveRestore autoSR(&aContext);
8384 aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(
8385 -userSpaceToFrameSpaceOffset));
8386 aPaintChildren(&aContext);
8390 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8391 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame);
8394 bool nsDisplayFilters::CreateWebRenderCommands(
8395 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8396 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8397 nsDisplayListBuilder* aDisplayListBuilder) {
8398 WrFiltersHolder wrFilters;
8399 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8400 auto filterChain = style.StyleEffects()->mFilters.AsSpan();
8401 bool initialized = true;
8402 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8403 wrFilters) &&
8404 !SVGIntegrationUtils::BuildWebRenderFilters(mFrame, filterChain,
8405 StyleFilterType::Filter,
8406 wrFilters, initialized)) {
8407 if (mStyle) {
8408 // TODO(bug 1769223): Support fallback filters in the root code-path,
8409 // perhaps. For now treat it the same way as invalid filters.
8410 wrFilters = {};
8411 } else {
8412 // Draw using fallback.
8413 return false;
8417 if (!initialized) {
8418 // https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
8420 // If the filter references a non-existent object or the referenced object
8421 // is not a filter element, then the whole filter chain is ignored. No
8422 // filter is applied to the object.
8424 // Note that other engines have a weird discrepancy between SVG and HTML
8425 // content here, but the spec is clear.
8426 wrFilters = {};
8429 uint64_t clipChainId;
8430 if (wrFilters.post_filters_clip) {
8431 auto devPxRect = LayoutDeviceRect::FromAppUnits(
8432 wrFilters.post_filters_clip.value() + ToReferenceFrame(),
8433 mFrame->PresContext()->AppUnitsPerDevPixel());
8434 auto clipId =
8435 aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect));
8436 clipChainId = aBuilder.DefineClipChain({clipId}, true).id;
8437 } else {
8438 clipChainId = aBuilder.CurrentClipChainId();
8440 wr::WrStackingContextClip clip =
8441 wr::WrStackingContextClip::ClipChain(clipChainId);
8443 float opacity = aBuilder.GetInheritedOpacity();
8444 aBuilder.SetInheritedOpacity(1.0f);
8445 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8446 aBuilder.SetInheritedClipChain(nullptr);
8447 wr::StackingContextParams params;
8448 params.mFilters = std::move(wrFilters.filters);
8449 params.mFilterDatas = std::move(wrFilters.filter_datas);
8450 params.opacity = opacity != 1.0f ? &opacity : nullptr;
8451 params.clip = clip;
8452 if (mWrapsBackdropFilter) {
8453 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8455 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8456 params);
8458 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
8459 aManager, aDisplayListBuilder);
8460 aBuilder.SetInheritedOpacity(opacity);
8461 aBuilder.SetInheritedClipChain(oldClipChain);
8463 return true;
8466 #ifdef MOZ_DUMP_PAINTING
8467 void nsDisplayFilters::PrintEffects(nsACString& aTo) {
8468 nsIFrame* firstFrame =
8469 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8470 bool first = true;
8471 aTo += " effects=(";
8472 // We may exist for a mix of CSS filter functions and/or references to SVG
8473 // filters. If we have invalid references to SVG filters then we paint
8474 // nothing, but otherwise we will apply one or more filters.
8475 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) !=
8476 SVGObserverUtils::eHasRefsSomeInvalid) {
8477 if (!first) {
8478 aTo += ", ";
8480 aTo += "filter";
8482 aTo += ")";
8484 #endif
8486 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder,
8487 nsIFrame* aFrame, nsDisplayList* aList)
8488 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8489 MOZ_COUNT_CTOR(nsDisplaySVGWrapper);
8492 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8493 return !aBuilder->GetWidgetLayerManager();
8496 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8497 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8498 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8499 nsDisplayListBuilder* aDisplayListBuilder) {
8500 return CreateWebRenderCommandsNewClipListOption(
8501 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8504 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder* aBuilder,
8505 nsIFrame* aFrame,
8506 nsDisplayList* aList)
8507 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8508 MOZ_COUNT_CTOR(nsDisplayForeignObject);
8511 #ifdef NS_BUILD_REFCNT_LOGGING
8512 nsDisplayForeignObject::~nsDisplayForeignObject() {
8513 MOZ_COUNT_DTOR(nsDisplayForeignObject);
8515 #endif
8517 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8518 return !aBuilder->GetWidgetLayerManager();
8521 bool nsDisplayForeignObject::CreateWebRenderCommands(
8522 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8523 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8524 nsDisplayListBuilder* aDisplayListBuilder) {
8525 AutoRestore<bool> restoreDoGrouping(aManager->CommandBuilder().mDoGrouping);
8526 aManager->CommandBuilder().mDoGrouping = false;
8527 return CreateWebRenderCommandsNewClipListOption(
8528 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8531 void nsDisplayLink::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8532 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8533 aCtx->GetDrawTarget()->Link(
8534 mLinkSpec.get(), NSRectToRect(GetPaintRect(aBuilder, aCtx), appPerDev));
8537 void nsDisplayDestination::Paint(nsDisplayListBuilder* aBuilder,
8538 gfxContext* aCtx) {
8539 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8540 aCtx->GetDrawTarget()->Destination(
8541 mDestinationName.get(),
8542 NSPointToPoint(GetPaintRect(aBuilder, aCtx).TopLeft(), appPerDev));
8545 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8546 nsDisplayList* aOutResultList, nsIContent* aContent) {
8547 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
8548 // in content document order and SortByZOrder is a stable sort which
8549 // guarantees that boxes produced by the same element are placed together
8550 // in the sort. Consider a position:relative inline element that breaks
8551 // across lines and has absolutely positioned children; all the abs-pos
8552 // children should be z-ordered after all the boxes for the position:relative
8553 // element itself.
8554 PositionedDescendants()->SortByZOrder();
8556 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8557 // 1,2: backgrounds and borders
8558 aOutResultList->AppendToTop(BorderBackground());
8559 // 3: negative z-index children.
8560 for (auto* item : PositionedDescendants()->TakeItems()) {
8561 if (item->ZIndex() < 0) {
8562 aOutResultList->AppendToTop(item);
8563 } else {
8564 PositionedDescendants()->AppendToTop(item);
8568 // 4: block backgrounds
8569 aOutResultList->AppendToTop(BlockBorderBackgrounds());
8570 // 5: floats
8571 aOutResultList->AppendToTop(Floats());
8572 // 7: general content
8573 aOutResultList->AppendToTop(Content());
8574 // 7.5: outlines, in content tree order. We need to sort by content order
8575 // because an element with outline that breaks and has children with outline
8576 // might have placed child outline items between its own outline items.
8577 // The element's outline items need to all come before any child outline
8578 // items.
8579 if (aContent) {
8580 Outlines()->SortByContentOrder(aContent);
8582 aOutResultList->AppendToTop(Outlines());
8583 // 8, 9: non-negative z-index children
8584 aOutResultList->AppendToTop(PositionedDescendants());
8587 uint32_t PaintTelemetry::sPaintLevel = 0;
8589 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8590 // Don't record nested paints.
8591 if (sPaintLevel++ > 0) {
8592 return;
8595 mStart = TimeStamp::Now();
8598 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8599 MOZ_ASSERT(sPaintLevel != 0);
8600 if (--sPaintLevel > 0) {
8601 return;
8604 // If we're in multi-process mode, don't include paint times for the parent
8605 // process.
8606 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8607 return;
8610 // Record the total time.
8611 mozilla::glean::gfx_content::paint_time.AccumulateRawDuration(
8612 TimeStamp::Now() - mStart);
8615 static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
8616 if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
8617 return aFrame;
8620 if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
8621 !aFrame->GetPrevInFlow()) {
8622 return aFrame->GetPlaceholderFrame();
8625 return aFrame;
8628 static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
8629 nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
8630 MOZ_ASSERT(f);
8631 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
8634 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8635 nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
8636 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
8637 const bool aIsTransformed)
8638 : mBuilder(aBuilder),
8639 mPrevFrame(aBuilder->mCurrentFrame),
8640 mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
8641 mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
8642 mPrevAdditionalOffset(aBuilder->mAdditionalOffset),
8643 mPrevVisibleRect(aBuilder->mVisibleRect),
8644 mPrevDirtyRect(aBuilder->mDirtyRect),
8645 mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo),
8646 mPrevAncestorHasApzAwareEventHandler(
8647 aBuilder->mAncestorHasApzAwareEventHandler),
8648 mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
8649 mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
8650 if (aIsTransformed) {
8651 aBuilder->mCurrentOffsetToReferenceFrame =
8652 aBuilder->AdditionalOffset().refOr(nsPoint());
8653 aBuilder->mCurrentReferenceFrame = aForChild;
8654 } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
8655 aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
8656 } else {
8657 aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
8658 aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
8661 // If aForChild is being visited from a frame other than it's ancestor frame,
8662 // mInInvalidSubtree will need to be recalculated the slow way.
8663 if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) {
8664 aBuilder->mInInvalidSubtree =
8665 aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
8666 } else {
8667 aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
8670 aBuilder->mCurrentFrame = aForChild;
8671 aBuilder->mVisibleRect = aVisibleRect;
8672 aBuilder->mDirtyRect =
8673 aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
8676 } // namespace mozilla