Bug 1752919: Make tiled images respect opacity applied as color. r=gw
[gecko.git] / layout / painting / nsDisplayList.cpp
blobbf4b4b14670c4f720ab7bc9b01296388734d9cb4
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/dom/BrowserChild.h"
23 #include "mozilla/dom/HTMLCanvasElement.h"
24 #include "mozilla/dom/RemoteBrowser.h"
25 #include "mozilla/dom/Selection.h"
26 #include "mozilla/dom/ServiceWorkerRegistrar.h"
27 #include "mozilla/dom/ServiceWorkerRegistration.h"
28 #include "mozilla/dom/SVGElement.h"
29 #include "mozilla/dom/TouchEvent.h"
30 #include "mozilla/gfx/2D.h"
31 #include "mozilla/PresShell.h"
32 #include "mozilla/ShapeUtils.h"
33 #include "mozilla/StaticPrefs_apz.h"
34 #include "mozilla/StaticPrefs_gfx.h"
35 #include "mozilla/StaticPrefs_layers.h"
36 #include "mozilla/StaticPrefs_layout.h"
37 #include "mozilla/StaticPrefs_print.h"
38 #include "mozilla/SVGIntegrationUtils.h"
39 #include "mozilla/SVGUtils.h"
40 #include "mozilla/ViewportUtils.h"
41 #include "nsCSSRendering.h"
42 #include "nsCSSRenderingGradients.h"
43 #include "nsRefreshDriver.h"
44 #include "nsRegion.h"
45 #include "nsStyleStructInlines.h"
46 #include "nsStyleTransformMatrix.h"
47 #include "nsTransitionManager.h"
48 #include "gfxMatrix.h"
49 #include "nsLayoutUtils.h"
50 #include "nsIScrollableFrame.h"
51 #include "nsIFrameInlines.h"
52 #include "nsStyleConsts.h"
53 #include "BorderConsts.h"
54 #include "mozilla/MathAlgorithms.h"
56 #include "imgIContainer.h"
57 #include "Layers.h"
58 #include "nsBoxFrame.h"
59 #include "nsImageFrame.h"
60 #include "nsSubDocumentFrame.h"
61 #include "nsViewManager.h"
62 #include "ImageContainer.h"
63 #include "nsCanvasFrame.h"
64 #include "nsSubDocumentFrame.h"
65 #include "StickyScrollContainer.h"
66 #include "mozilla/AnimationPerformanceWarning.h"
67 #include "mozilla/AnimationUtils.h"
68 #include "mozilla/AutoRestore.h"
69 #include "mozilla/EffectCompositor.h"
70 #include "mozilla/EffectSet.h"
71 #include "mozilla/EventStates.h"
72 #include "mozilla/HashTable.h"
73 #include "mozilla/LookAndFeel.h"
74 #include "mozilla/OperatorNewExtensions.h"
75 #include "mozilla/PendingAnimationTracker.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 "nsSliderFrame.h"
102 #include "nsFocusManager.h"
103 #include "TextDrawTarget.h"
104 #include "mozilla/layers/AnimationHelper.h"
105 #include "mozilla/layers/CompositorThread.h"
106 #include "mozilla/layers/InputAPZContext.h"
107 #include "mozilla/layers/RenderRootStateManager.h"
108 #include "mozilla/layers/StackingContextHelper.h"
109 #include "mozilla/layers/TreeTraversal.h"
110 #include "mozilla/layers/WebRenderBridgeChild.h"
111 #include "mozilla/layers/WebRenderLayerManager.h"
112 #include "mozilla/layers/WebRenderMessages.h"
113 #include "mozilla/layers/WebRenderScrollData.h"
115 namespace mozilla {
117 using namespace dom;
118 using namespace gfx;
119 using namespace layout;
120 using namespace layers;
121 using namespace image;
123 LazyLogModule sDisplayListLog("displaylist");
125 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
126 void AssertUniqueItem(nsDisplayItem* aItem) {
127 for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) {
128 if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
129 i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
130 if (i->IsPreProcessedItem() || i->IsPreProcessed()) {
131 continue;
133 MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
137 #endif
139 bool ShouldBuildItemForEvents(const DisplayItemType aType) {
140 return aType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO ||
141 (GetDisplayItemFlagsForType(aType) & TYPE_IS_CONTAINER);
144 static bool ItemTypeSupportsHitTesting(const DisplayItemType aType) {
145 switch (aType) {
146 case DisplayItemType::TYPE_BACKGROUND:
147 case DisplayItemType::TYPE_BACKGROUND_COLOR:
148 case DisplayItemType::TYPE_THEMED_BACKGROUND:
149 return true;
150 default:
151 return false;
155 void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder,
156 nsPaintedDisplayItem* aItem,
157 const DisplayItemType aType) {
158 if (ItemTypeSupportsHitTesting(aType)) {
159 aItem->InitializeHitTestInfo(aBuilder);
163 /* static */
164 already_AddRefed<ActiveScrolledRoot> ActiveScrolledRoot::CreateASRForFrame(
165 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame,
166 bool aIsRetained) {
167 nsIFrame* f = do_QueryFrame(aScrollableFrame);
169 RefPtr<ActiveScrolledRoot> asr;
170 if (aIsRetained) {
171 asr = f->GetProperty(ActiveScrolledRootCache());
174 if (!asr) {
175 asr = new ActiveScrolledRoot();
177 if (aIsRetained) {
178 RefPtr<ActiveScrolledRoot> ref = asr;
179 f->SetProperty(ActiveScrolledRootCache(), ref.forget().take());
182 asr->mParent = aParent;
183 asr->mScrollableFrame = aScrollableFrame;
184 asr->mViewId = Nothing();
185 asr->mDepth = aParent ? aParent->mDepth + 1 : 1;
186 asr->mRetained = aIsRetained;
188 return asr.forget();
191 /* static */
192 bool ActiveScrolledRoot::IsAncestor(const ActiveScrolledRoot* aAncestor,
193 const ActiveScrolledRoot* aDescendant) {
194 if (!aAncestor) {
195 // nullptr is the root
196 return true;
198 if (Depth(aAncestor) > Depth(aDescendant)) {
199 return false;
201 const ActiveScrolledRoot* asr = aDescendant;
202 while (asr) {
203 if (asr == aAncestor) {
204 return true;
206 asr = asr->mParent;
208 return false;
211 /* static */
212 bool ActiveScrolledRoot::IsProperAncestor(
213 const ActiveScrolledRoot* aAncestor,
214 const ActiveScrolledRoot* aDescendant) {
215 return aAncestor != aDescendant && IsAncestor(aAncestor, aDescendant);
218 /* static */
219 nsCString ActiveScrolledRoot::ToString(
220 const ActiveScrolledRoot* aActiveScrolledRoot) {
221 nsAutoCString str;
222 for (const auto* asr = aActiveScrolledRoot; asr; asr = asr->mParent) {
223 str.AppendPrintf("<0x%p>", asr->mScrollableFrame);
224 if (asr->mParent) {
225 str.AppendLiteral(", ");
228 return std::move(str);
231 ScrollableLayerGuid::ViewID ActiveScrolledRoot::ComputeViewId() const {
232 nsIContent* content = mScrollableFrame->GetScrolledFrame()->GetContent();
233 return nsLayoutUtils::FindOrCreateIDFor(content);
236 ActiveScrolledRoot::~ActiveScrolledRoot() {
237 if (mScrollableFrame && mRetained) {
238 nsIFrame* f = do_QueryFrame(mScrollableFrame);
239 f->RemoveProperty(ActiveScrolledRootCache());
243 static uint64_t AddAnimationsForWebRender(
244 nsDisplayItem* aItem, RenderRootStateManager* aManager,
245 nsDisplayListBuilder* aDisplayListBuilder,
246 const Maybe<LayoutDevicePoint>& aPosition = Nothing()) {
247 EffectSet* effects =
248 EffectSet::GetEffectSetForFrame(aItem->Frame(), aItem->GetType());
249 if (!effects || effects->IsEmpty()) {
250 // If there is no animation on the nsIFrame, that means
251 // 1) we've never created any animations on this frame or
252 // 2) the frame was reconstruced or
253 // 3) all animations on the frame have finished
254 // in such cases we don't need do anything here.
256 // Even if there is a WebRenderAnimationData for the display item type on
257 // this frame, it's going to be discarded since it's not marked as being
258 // used.
259 return 0;
262 RefPtr<WebRenderAnimationData> animationData =
263 aManager->CommandBuilder()
264 .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(aItem);
265 AnimationInfo& animationInfo = animationData->GetAnimationInfo();
266 animationInfo.AddAnimationsForDisplayItem(
267 aItem->Frame(), aDisplayListBuilder, aItem, aItem->GetType(),
268 aManager->LayerManager(), aPosition);
269 animationInfo.StartPendingAnimations(
270 aManager->LayerManager()->GetAnimationReadyTime());
272 // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
273 // are no active animations.
274 uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
275 if (!animationInfo.GetAnimations().IsEmpty()) {
276 OpAddCompositorAnimations anim(
277 CompositorAnimations(animationInfo.GetAnimations(), animationsId));
278 aManager->WrBridge()->AddWebRenderParentCommand(anim);
279 aManager->AddActiveCompositorAnimationId(animationsId);
280 } else if (animationsId) {
281 aManager->AddCompositorAnimationsIdForDiscard(animationsId);
282 animationsId = 0;
285 return animationsId;
288 static bool GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
289 const nsRect& aFillRect,
290 nsDisplayListBuilder* aBuilder) {
291 if (aBuilder->IsForGenerateGlyphMask()) {
292 return false;
295 SVGObserverUtils::GetAndObserveBackgroundClip(aFrame);
297 // The main function of enabling background-clip:text property value.
298 // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
299 // this function to
300 // 1. Generate a mask by all descendant text frames
301 // 2. Push the generated mask into aContext.
303 gfxContext* sourceCtx = aContext;
304 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
305 aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel());
307 // Create a mask surface.
308 RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget();
309 RefPtr<DrawTarget> maskDT = sourceTarget->CreateClippedDrawTarget(
310 bounds.ToUnknownRect(), SurfaceFormat::A8);
311 if (!maskDT || !maskDT->IsValid()) {
312 return false;
314 RefPtr<gfxContext> maskCtx =
315 gfxContext::CreatePreservingTransformOrNull(maskDT);
316 MOZ_ASSERT(maskCtx);
317 maskCtx->Multiply(Matrix::Translation(bounds.TopLeft().ToUnknownPoint()));
319 // Shade text shape into mask A8 surface.
320 nsLayoutUtils::PaintFrame(
321 maskCtx, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()),
322 NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph);
324 // Push the generated mask into aContext, so that the caller can pop and
325 // blend with it.
327 Matrix currentMatrix = sourceCtx->CurrentMatrix();
328 Matrix invCurrentMatrix = currentMatrix;
329 invCurrentMatrix.Invert();
331 RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
332 sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0,
333 maskSurface, invCurrentMatrix);
335 return true;
338 nsDisplayWrapper* nsDisplayWrapList::CreateShallowCopy(
339 nsDisplayListBuilder* aBuilder) {
340 const nsDisplayWrapList* wrappedItem = AsDisplayWrapList();
341 MOZ_ASSERT(wrappedItem);
343 // Create a new nsDisplayWrapList using a copy-constructor. This is done
344 // to preserve the information about bounds.
345 nsDisplayWrapper* wrapper =
346 new (aBuilder) nsDisplayWrapper(aBuilder, *wrappedItem);
347 wrapper->SetType(nsDisplayWrapper::ItemType());
348 MOZ_ASSERT(wrapper);
350 // Set the display list pointer of the new wrapper item to the display list
351 // of the wrapped item.
352 wrapper->mListPtr = wrappedItem->mListPtr;
353 return wrapper;
356 nsDisplayWrapList* nsDisplayListBuilder::MergeItems(
357 nsTArray<nsDisplayItem*>& aItems) {
358 // For merging, we create a temporary item by cloning the last item of the
359 // mergeable items list. This ensures that the temporary item will have the
360 // correct frame and bounds.
361 nsDisplayWrapList* last = aItems.PopLastElement()->AsDisplayWrapList();
362 MOZ_ASSERT(last);
363 nsDisplayWrapList* merged = last->Clone(this);
364 MOZ_ASSERT(merged);
365 AddTemporaryItem(merged);
367 // Create nsDisplayWrappers that point to the internal display lists of the
368 // items we are merging. These nsDisplayWrappers are added to the display list
369 // of the temporary item.
370 for (nsDisplayItem* item : aItems) {
371 MOZ_ASSERT(item);
372 MOZ_ASSERT(merged->CanMerge(item));
373 merged->Merge(item);
374 MOZ_ASSERT(item->AsDisplayWrapList());
375 merged->GetChildren()->AppendToTop(
376 static_cast<nsDisplayWrapList*>(item)->CreateShallowCopy(this));
379 merged->GetChildren()->AppendToTop(last->CreateShallowCopy(this));
381 return merged;
384 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
385 SetCurrentActiveScrolledRoot(
386 const ActiveScrolledRoot* aActiveScrolledRoot) {
387 MOZ_ASSERT(!mUsed);
389 // Set the builder's mCurrentActiveScrolledRoot.
390 mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;
392 // We also need to adjust the builder's mCurrentContainerASR.
393 // mCurrentContainerASR needs to be an ASR that all the container's
394 // contents have finite bounds with respect to. If aActiveScrolledRoot
395 // is an ancestor ASR of mCurrentContainerASR, that means we need to
396 // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
397 // the items that will be created with aActiveScrolledRoot wouldn't
398 // have finite bounds with respect to mCurrentContainerASR. There's one
399 // exception, in the case where there's a content clip on the builder
400 // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
401 // content clip will clip all items that are created while this
402 // AutoCurrentActiveScrolledRootSetter exists. This means that the items
403 // created during our lifetime will have finite bounds with respect to
404 // the content clip's ASR, even if the items' actual ASR is an ancestor
405 // of that. And it also means that mCurrentContainerASR only needs to be
406 // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
407 // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
408 // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
410 // finiteBoundsASR is the leafmost ASR that all items created during
411 // object's lifetime have finite bounds with respect to.
412 const ActiveScrolledRoot* finiteBoundsASR =
413 ActiveScrolledRoot::PickDescendant(mContentClipASR, aActiveScrolledRoot);
415 // mCurrentContainerASR is adjusted so that it's still an ancestor of
416 // finiteBoundsASR.
417 mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
418 mBuilder->mCurrentContainerASR, finiteBoundsASR);
420 // If we are entering out-of-flow content inside a CSS filter, mark
421 // scroll frames wrt. which the content is fixed as containing such content.
422 if (mBuilder->mFilterASR && ActiveScrolledRoot::IsAncestor(
423 aActiveScrolledRoot, mBuilder->mFilterASR)) {
424 for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
425 asr && asr != aActiveScrolledRoot; asr = asr->mParent) {
426 asr->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
430 mUsed = true;
433 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
434 InsertScrollFrame(nsIScrollableFrame* aScrollableFrame) {
435 MOZ_ASSERT(!mUsed);
436 size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
437 const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot;
438 const ActiveScrolledRoot* asr =
439 mBuilder->AllocateActiveScrolledRoot(parentASR, aScrollableFrame);
440 mBuilder->mCurrentActiveScrolledRoot = asr;
442 // All child ASRs of parentASR that were created while this
443 // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
444 // now. Reparent them to asr.
445 for (size_t i = mDescendantsStartIndex; i < descendantsEndIndex; i++) {
446 ActiveScrolledRoot* descendantASR = mBuilder->mActiveScrolledRoots[i];
447 if (ActiveScrolledRoot::IsAncestor(parentASR, descendantASR)) {
448 descendantASR->IncrementDepth();
449 if (descendantASR->mParent == parentASR) {
450 descendantASR->mParent = asr;
455 mUsed = true;
458 nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
459 nsDisplayListBuilder* aBuilder)
460 : mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) {
461 mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot;
464 nsPresContext* nsDisplayListBuilder::CurrentPresContext() {
465 return CurrentPresShellState()->mPresShell->GetPresContext();
468 /* static */
469 nsRect nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
470 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
471 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
472 nsRect* aOutDirtyRect) {
473 nsRect visible = aVisibleRect;
474 nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
476 bool inPartialUpdate =
477 aBuilder->IsRetainingDisplayList() && aBuilder->IsPartialUpdate();
478 if (StaticPrefs::apz_allow_zooming() &&
479 DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame) &&
480 aBuilder->IsPaintingToWindow() && !inPartialUpdate) {
481 dirtyRectRelativeToDirtyFrame =
482 nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize());
484 // If there's a visual viewport size set, restrict the amount of the
485 // fixed-position element we paint to the visual viewport. (In general
486 // the fixed-position element can be as large as the layout viewport,
487 // which at a high zoom level can cause us to paint too large of an
488 // area.)
489 PresShell* presShell = aFrame->PresShell();
490 if (presShell->IsVisualViewportSizeSet()) {
491 dirtyRectRelativeToDirtyFrame =
492 nsRect(presShell->GetVisualViewportOffsetRelativeToLayoutViewport(),
493 presShell->GetVisualViewportSize());
494 // But if we have a displayport, expand it to the displayport, so
495 // that async-scrolling the visual viewport within the layout viewport
496 // will not checkerboard.
497 if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
498 nsRect displayport;
499 // Note that the displayport here is already in the right coordinate
500 // space: it's relative to the scroll port (= layout viewport), but
501 // covers the visual viewport with some margins around it, which is
502 // exactly what we want.
503 if (DisplayPortUtils::GetDisplayPort(
504 rootScrollFrame->GetContent(), &displayport,
505 DisplayPortOptions().With(ContentGeometryType::Fixed))) {
506 dirtyRectRelativeToDirtyFrame = displayport;
510 visible = dirtyRectRelativeToDirtyFrame;
511 if (StaticPrefs::apz_test_logging_enabled() &&
512 presShell->GetDocument()->IsContentDocument()) {
513 nsLayoutUtils::LogAdditionalTestData(
514 aBuilder, "fixedPosDisplayport",
515 ToString(CSSSize::FromAppUnits(visible)));
519 *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
520 visible -= aFrame->GetPosition();
522 nsRect overflowRect = aFrame->InkOverflowRect();
524 if (aFrame->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
525 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
527 * Add a fuzz factor to the overflow rectangle so that elements only
528 * just out of view are pulled into the display list, so they can be
529 * prerendered if necessary.
531 overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
534 visible.IntersectRect(visible, overflowRect);
535 aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect);
537 return visible;
540 nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder* aBuilder,
541 nsIFrame* aFrame,
542 nsDisplayList* aList)
543 : mList(aList) {
544 // Find the element that we need to check for link-ness, bailing out if
545 // we can't find one.
546 Element* elem = Element::FromNodeOrNull(aFrame->GetContent());
547 if (!elem) {
548 return;
551 // If the element has an id and/or name attribute, generate a destination
552 // for possible internal linking.
553 auto maybeGenerateDest = [&](const nsAtom* aAttr) {
554 nsAutoString attrValue;
555 elem->GetAttr(aAttr, attrValue);
556 if (!attrValue.IsEmpty()) {
557 NS_ConvertUTF16toUTF8 dest(attrValue);
558 // Ensure that we only emit a given destination once, although there may
559 // be multiple frames associated with a given element; we'll simply use
560 // the first of them as the target of any links to it.
561 // XXX(jfkthame) This prevents emitting duplicate destinations *on the
562 // same page*, but does not prevent duplicates on subsequent pages, as
563 // each new page is handled by a new temporary DisplayListBuilder. This
564 // seems to be harmless in practice, though a bit wasteful of space. To
565 // fix, we need to maintain the set of already-seen destinations globally
566 // for the print job, rather than attached to the (per-page) builder.
567 if (aBuilder->mDestinations.EnsureInserted(dest)) {
568 auto* destination = MakeDisplayItem<nsDisplayDestination>(
569 aBuilder, aFrame, dest.get(), aFrame->GetRect().TopLeft());
570 mList->AppendToTop(destination);
575 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
576 if (elem->HasID()) {
577 maybeGenerateDest(nsGkAtoms::id);
579 if (elem->HasName()) {
580 maybeGenerateDest(nsGkAtoms::name);
584 // Links don't nest, so if the builder already has a destination, no need to
585 // check for a link element here.
586 if (!aBuilder->mLinkSpec.IsEmpty()) {
587 return;
590 // Check if we have actually found a link.
591 if (!elem->IsLink()) {
592 return;
595 nsCOMPtr<nsIURI> uri = elem->GetHrefURI();
596 if (!uri) {
597 return;
600 // Is it a local (in-page) destination?
601 bool hasRef, eqExRef;
602 nsIURI* docURI;
603 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
604 NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef &&
605 (docURI = aFrame->PresContext()->Document()->GetDocumentURI()) &&
606 NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &eqExRef)) && eqExRef) {
607 if (NS_FAILED(uri->GetRef(aBuilder->mLinkSpec)) ||
608 aBuilder->mLinkSpec.IsEmpty()) {
609 return;
611 // The destination name is simply a string; we don't want URL-escaping
612 // applied to it.
613 NS_UnescapeURL(aBuilder->mLinkSpec);
614 // Mark the link spec as being an internal destination
615 aBuilder->mLinkSpec.Insert('#', 0);
616 } else {
617 if (NS_FAILED(uri->GetSpec(aBuilder->mLinkSpec)) ||
618 aBuilder->mLinkSpec.IsEmpty()) {
619 return;
623 // Record that we need to reset the builder's state on destruction.
624 mBuilderToReset = aBuilder;
627 void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
628 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
629 // Note that we may generate a link here even if the constructor bailed out
630 // without updating aBuilder->LinkSpec(), because it may have been set by
631 // an ancestor that was associated with a link element.
632 if (!aBuilder->mLinkSpec.IsEmpty()) {
633 auto* link = MakeDisplayItem<nsDisplayLink>(
634 aBuilder, aFrame, aBuilder->mLinkSpec.get(), aFrame->GetRect());
635 mList->AppendToTop(link);
639 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
640 nsDisplayListBuilderMode aMode,
641 bool aBuildCaret,
642 bool aRetainingDisplayList)
643 : mReferenceFrame(aReferenceFrame),
644 mIgnoreScrollFrame(nullptr),
645 mCurrentActiveScrolledRoot(nullptr),
646 mCurrentContainerASR(nullptr),
647 mCurrentFrame(aReferenceFrame),
648 mCurrentReferenceFrame(aReferenceFrame),
649 mBuildingExtraPagesForPageNum(0),
650 mDirtyRect(-1, -1, -1, -1),
651 mGlassDisplayItem(nullptr),
652 mHasGlassItemDuringPartial(false),
653 mCaretFrame(nullptr),
654 mScrollInfoItemsForHoisting(nullptr),
655 mFirstClipChainToDestroy(nullptr),
656 mMode(aMode),
657 mTableBackgroundSet(nullptr),
658 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
659 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
660 mFilterASR(nullptr),
661 mContainsBlendMode(false),
662 mIsBuildingScrollbar(false),
663 mCurrentScrollbarWillHaveLayer(false),
664 mBuildCaret(aBuildCaret),
665 mRetainingDisplayList(aRetainingDisplayList),
666 mPartialUpdate(false),
667 mIgnoreSuppression(false),
668 mIncludeAllOutOfFlows(false),
669 mDescendIntoSubdocuments(true),
670 mSelectedFramesOnly(false),
671 mAllowMergingAndFlattening(true),
672 mInTransform(false),
673 mInEventsOnly(false),
674 mInFilter(false),
675 mInPageSequence(false),
676 mIsInChromePresContext(false),
677 mSyncDecodeImages(false),
678 mIsPaintingToWindow(false),
679 mUseHighQualityScaling(false),
680 mIsPaintingForWebRender(false),
681 mIsCompositingCheap(false),
682 mAncestorHasApzAwareEventHandler(false),
683 mHaveScrollableDisplayPort(false),
684 mWindowDraggingAllowed(false),
685 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
686 mForceLayerForScrollParent(false),
687 mContainsNonMinimalDisplayPort(false),
688 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
689 mBuildingInvisibleItems(false),
690 mIsBuilding(false),
691 mInInvalidSubtree(false),
692 mDisablePartialUpdates(false),
693 mPartialBuildFailed(false),
694 mIsInActiveDocShell(false),
695 mBuildAsyncZoomContainer(false),
696 mIsRelativeToLayoutViewport(false),
697 mUseOverlayScrollbars(false),
698 mAlwaysLayerizeScrollbars(false) {
699 MOZ_COUNT_CTOR(nsDisplayListBuilder);
701 mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting();
703 ShouldRebuildDisplayListDueToPrefChange();
705 mUseOverlayScrollbars =
706 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
708 mAlwaysLayerizeScrollbars =
709 StaticPrefs::layout_scrollbars_always_layerize_track();
711 static_assert(
712 static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
713 "Check TYPE_MAX should not overflow");
714 mIsForContent = XRE_IsContentProcess();
715 mIsReusingStackingContextItems =
716 mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
719 static PresShell* GetFocusedPresShell() {
720 nsPIDOMWindowOuter* focusedWnd =
721 nsFocusManager::GetFocusManager()->GetFocusedWindow();
722 if (!focusedWnd) {
723 return nullptr;
726 nsCOMPtr<nsIDocShell> focusedDocShell = focusedWnd->GetDocShell();
727 if (!focusedDocShell) {
728 return nullptr;
731 return focusedDocShell->GetPresShell();
734 void nsDisplayListBuilder::BeginFrame() {
735 nsCSSRendering::BeginFrameTreesLocked();
737 mIsPaintingToWindow = false;
738 mUseHighQualityScaling = false;
739 mIgnoreSuppression = false;
740 mInTransform = false;
741 mInFilter = false;
742 mSyncDecodeImages = false;
744 if (!mBuildCaret) {
745 return;
748 RefPtr<PresShell> presShell = GetFocusedPresShell();
749 if (presShell) {
750 RefPtr<nsCaret> caret = presShell->GetCaret();
751 mCaretFrame = caret->GetPaintGeometry(&mCaretRect);
753 // The focused pres shell may not be in the document that we're
754 // painting, or be in a popup. Check if the display root for
755 // the caret matches the display root that we're painting, and
756 // only use it if it matches.
757 if (mCaretFrame &&
758 nsLayoutUtils::GetDisplayRootFrame(mCaretFrame) !=
759 nsLayoutUtils::GetDisplayRootFrame(mReferenceFrame)) {
760 mCaretFrame = nullptr;
765 void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser* aBrowser,
766 const dom::EffectsInfo& aUpdate) {
767 dom::EffectsInfo update = aUpdate;
768 // For printing we create one display item for each page that an iframe
769 // appears on, the proper visible rect is the union of all the visible rects
770 // we get from each display item.
771 nsPresContext* pc =
772 mReferenceFrame ? mReferenceFrame->PresContext() : nullptr;
773 if (pc && (pc->Type() != nsPresContext::eContext_Galley)) {
774 Maybe<dom::EffectsInfo> existing = mEffectsUpdates.MaybeGet(aBrowser);
775 if (existing.isSome()) {
776 // Only the visible rect should differ, the scales should match.
777 MOZ_ASSERT(existing->mRasterScale == aUpdate.mRasterScale &&
778 existing->mTransformToAncestorScale ==
779 aUpdate.mTransformToAncestorScale);
780 update.mVisibleRect = update.mVisibleRect.Union(existing->mVisibleRect);
783 mEffectsUpdates.InsertOrUpdate(aBrowser, update);
786 void nsDisplayListBuilder::EndFrame() {
787 NS_ASSERTION(!mInInvalidSubtree,
788 "Someone forgot to cleanup mInInvalidSubtree!");
789 mActiveScrolledRoots.Clear();
790 mEffectsUpdates.Clear();
791 FreeClipChains();
792 FreeTemporaryItems();
793 nsCSSRendering::EndFrameTreesLocked();
794 mCaretFrame = nullptr;
797 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
798 const nsIFrame* aStopAtFrame) {
799 mFramesMarkedForDisplay.AppendElement(aFrame);
800 for (nsIFrame* f = aFrame; f;
801 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
802 if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
803 return;
805 f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
806 if (f == aStopAtFrame) {
807 // we've reached a frame that we know will be painted, so we can stop.
808 break;
813 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) {
814 mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
817 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
818 nsIFrame* aFrame, const nsIFrame* aStopAtFrame) {
819 AddFrameMarkedForDisplayIfVisible(aFrame);
820 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
821 if (f->ForceDescendIntoIfVisible()) {
822 return;
824 f->SetForceDescendIntoIfVisible(true);
825 if (f == aStopAtFrame) {
826 // we've reached a frame that we know will be painted, so we can stop.
827 break;
832 void nsDisplayListBuilder::SetGlassDisplayItem(nsDisplayItem* aItem) {
833 // Web pages or extensions could trigger the "Multiple glass backgrounds
834 // found?" warning by using -moz-appearance:win-borderless-glass etc on their
835 // own elements (as long as they are DocElementBoxFrames, which is rare as
836 // each xul doc only gets one near the root). We only care about first one,
837 // since that will be the background of the root window.
839 if (IsPartialUpdate()) {
840 if (aItem->Frame()->IsDocElementBoxFrame()) {
841 #ifdef DEBUG
842 if (mHasGlassItemDuringPartial) {
843 NS_WARNING("Multiple glass backgrounds found?");
844 } else
845 #endif
846 if (!mHasGlassItemDuringPartial) {
847 mHasGlassItemDuringPartial = true;
848 aItem->SetIsGlassItem();
851 return;
854 if (aItem->Frame()->IsDocElementBoxFrame()) {
855 #ifdef DEBUG
856 if (mGlassDisplayItem) {
857 NS_WARNING("Multiple glass backgrounds found?");
858 } else
859 #endif
860 if (!mGlassDisplayItem) {
861 mGlassDisplayItem = aItem;
862 mGlassDisplayItem->SetIsGlassItem();
867 bool nsDisplayListBuilder::NeedToForceTransparentSurfaceForItem(
868 nsDisplayItem* aItem) {
869 return aItem == mGlassDisplayItem;
872 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
873 mIsRelativeToLayoutViewport = true;
874 UpdateShouldBuildAsyncZoomContainer();
877 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
878 const Document* document = mReferenceFrame->PresContext()->Document();
879 mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
880 !document->Fullscreen() &&
881 nsLayoutUtils::AllowZoomingForDocument(document);
884 // Certain prefs may cause display list items to be added or removed when they
885 // are toggled. In those cases, we need to fully rebuild the display list.
886 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
887 // If we transition between wrapping the RCD-RSF contents into an async
888 // zoom container vs. not, we need to rebuild the display list. This only
889 // happens when the zooming or container scrolling prefs are toggled
890 // (manually by the user, or during test setup).
891 bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer;
892 UpdateShouldBuildAsyncZoomContainer();
894 bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars;
895 mUseOverlayScrollbars =
896 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
898 bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars;
899 mAlwaysLayerizeScrollbars =
900 StaticPrefs::layout_scrollbars_always_layerize_track();
902 if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) {
903 return true;
906 if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) {
907 return true;
910 if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) {
911 return true;
914 return false;
917 void nsDisplayListBuilder::AddScrollFrameToNotify(
918 nsIScrollableFrame* aScrollFrame) {
919 mScrollFramesToNotify.insert(aScrollFrame);
922 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
923 for (const auto& it : mScrollFramesToNotify) {
924 it->NotifyApzTransaction();
926 mScrollFramesToNotify.clear();
929 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
930 nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect,
931 const nsRect& aDirtyRect) {
932 MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
933 nsRect dirty;
934 nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
935 this, aFrame, aVisibleRect, aDirtyRect, &dirty);
936 if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
937 visible.IsEmpty()) {
938 return false;
941 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
942 // frame, then it will also mark any outer frames to ensure that building
943 // reaches the dirty feame.
944 if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
945 MarkFrameForDisplay(aFrame, aDirtyFrame);
948 return true;
951 static void UnmarkFrameForDisplay(nsIFrame* aFrame,
952 const nsIFrame* aStopAtFrame) {
953 for (nsIFrame* f = aFrame; f;
954 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
955 if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
956 return;
958 f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
959 if (f == aStopAtFrame) {
960 // we've reached a frame that we know will be painted, so we can stop.
961 break;
966 static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
967 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
968 if (!f->ForceDescendIntoIfVisible()) {
969 return;
971 f->SetForceDescendIntoIfVisible(false);
975 nsDisplayListBuilder::~nsDisplayListBuilder() {
976 NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
977 "All frames should have been unmarked");
978 NS_ASSERTION(mFramesWithOOFData.Length() == 0,
979 "All OOF data should have been removed");
980 NS_ASSERTION(mPresShellStates.Length() == 0,
981 "All presshells should have been exited");
983 DisplayItemClipChain* c = mFirstClipChainToDestroy;
984 while (c) {
985 DisplayItemClipChain* next = c->mNextClipChainToDestroy;
986 c->DisplayItemClipChain::~DisplayItemClipChain();
987 c = next;
990 MOZ_COUNT_DTOR(nsDisplayListBuilder);
993 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
994 uint32_t flags = 0;
995 if (mSyncDecodeImages) {
996 flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
998 if (mIsPaintingToWindow) {
999 flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
1001 if (mUseHighQualityScaling) {
1002 flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING;
1004 return flags;
1007 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
1008 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
1009 uint32_t flags = 0;
1010 if (mSyncDecodeImages) {
1011 flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
1013 if (mIsPaintingToWindow) {
1014 flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
1016 if (mUseHighQualityScaling) {
1017 flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
1019 return flags;
1022 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1023 uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
1024 if (mSyncDecodeImages) {
1025 flags |= imgIContainer::FLAG_SYNC_DECODE;
1026 } else {
1027 flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
1029 if (mIsPaintingToWindow || mUseHighQualityScaling) {
1030 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1032 return flags;
1035 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
1036 const nsRegion& aRegion) {
1037 if (aRegion.IsEmpty()) {
1038 return;
1041 nsRegion tmp;
1042 tmp.Sub(*aVisibleRegion, aRegion);
1043 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1044 // to its bounds either, which can be very bad (see bug 516740).
1045 // Do let aVisibleRegion get more complex if by doing so we reduce its
1046 // area by at least half.
1047 if (tmp.GetNumRects() <= 15 || tmp.Area() <= aVisibleRegion->Area() / 2) {
1048 *aVisibleRegion = tmp;
1052 nsCaret* nsDisplayListBuilder::GetCaret() {
1053 RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
1054 return caret;
1057 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) {
1058 if (mIsPaintingToWindow) {
1059 aPresShell->IncrementPaintCount();
1063 void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
1064 bool aPointerEventsNoneDoc) {
1065 PresShellState* state = mPresShellStates.AppendElement();
1066 state->mPresShell = aReferenceFrame->PresShell();
1067 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
1068 state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
1070 nsIScrollableFrame* sf = state->mPresShell->GetRootScrollFrameAsScrollable();
1071 if (sf && IsInSubdocument()) {
1072 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1073 // that the canvas background color will be set correctly, and that only one
1074 // unscrollable item will be created.
1075 // This is done to avoid, for example, a case where only scrollbar frames
1076 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1077 // and possibly end up with an extra nsDisplaySolidColor item.
1078 // We skip this for the root document, since we don't want to use
1079 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1080 // do it manually there.
1081 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
1082 if (canvasFrame) {
1083 MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
1087 #ifdef DEBUG
1088 state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
1089 nsLayoutPhase::DisplayListBuilding);
1090 #endif
1092 state->mPresShell->UpdateCanvasBackground();
1094 bool buildCaret = mBuildCaret;
1095 if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
1096 state->mIsBackgroundOnly = false;
1097 } else {
1098 state->mIsBackgroundOnly = true;
1099 buildCaret = false;
1102 bool pointerEventsNone = aPointerEventsNoneDoc;
1103 if (IsInSubdocument()) {
1104 pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
1105 .mInsidePointerEventsNoneDoc;
1107 state->mInsidePointerEventsNoneDoc = pointerEventsNone;
1109 state->mPresShellIgnoreScrollFrame =
1110 state->mPresShell->IgnoringViewportScrolling()
1111 ? state->mPresShell->GetRootScrollFrame()
1112 : nullptr;
1114 nsPresContext* pc = aReferenceFrame->PresContext();
1115 mIsInChromePresContext = pc->IsChrome();
1116 nsIDocShell* docShell = pc->GetDocShell();
1118 if (docShell) {
1119 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1122 state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);
1124 if (!buildCaret) {
1125 return;
1128 // Caret frames add visual area to their frame, but we don't update the
1129 // overflow area. Use flags to make sure we build display items for that frame
1130 // instead.
1131 if (mCaretFrame && mCaretFrame->PresShell() == state->mPresShell) {
1132 MarkFrameForDisplay(mCaretFrame, aReferenceFrame);
1136 // A non-blank paint is a paint that does not just contain the canvas
1137 // background.
1138 static bool DisplayListIsNonBlank(nsDisplayList* aList) {
1139 for (nsDisplayItem* i : *aList) {
1140 switch (i->GetType()) {
1141 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
1142 case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
1143 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
1144 continue;
1145 case DisplayItemType::TYPE_SOLID_COLOR:
1146 case DisplayItemType::TYPE_BACKGROUND:
1147 case DisplayItemType::TYPE_BACKGROUND_COLOR:
1148 if (i->Frame()->IsCanvasFrame()) {
1149 continue;
1151 return true;
1152 default:
1153 return true;
1156 return false;
1159 // A contentful paint is a paint that does contains DOM content (text,
1160 // images, non-blank canvases, SVG): "First Contentful Paint entry
1161 // contains a DOMHighResTimeStamp reporting the time when the browser
1162 // first rendered any text, image (including background images),
1163 // non-white canvas or SVG. This excludes any content of iframes, but
1164 // includes text with pending webfonts. This is the first time users
1165 // could start consuming page content."
1166 static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
1167 nsDisplayList* aList) {
1168 for (nsDisplayItem* i : *aList) {
1169 DisplayItemType type = i->GetType();
1170 nsDisplayList* children = i->GetChildren();
1172 switch (type) {
1173 case DisplayItemType::TYPE_SUBDOCUMENT: // iframes are ignored
1174 break;
1175 // CANVASes check if they may have been modified (as a stand-in
1176 // actually tracking all modifications)
1177 default:
1178 if (i->IsContentful()) {
1179 bool dummy;
1180 nsRect bound = i->GetBounds(aBuilder, &dummy);
1181 if (!bound.IsEmpty()) {
1182 return true;
1185 if (children) {
1186 if (DisplayListIsContentful(aBuilder, children)) {
1187 return true;
1190 break;
1193 return false;
1196 void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
1197 nsDisplayList* aPaintedContents) {
1198 NS_ASSERTION(
1199 CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
1200 "Presshell mismatch");
1202 if (mIsPaintingToWindow && aPaintedContents) {
1203 nsPresContext* pc = aReferenceFrame->PresContext();
1204 if (!pc->HadNonBlankPaint()) {
1205 if (!CurrentPresShellState()->mIsBackgroundOnly &&
1206 DisplayListIsNonBlank(aPaintedContents)) {
1207 pc->NotifyNonBlankPaint();
1210 nsRootPresContext* rootPresContext = pc->GetRootPresContext();
1211 if (!pc->HadContentfulPaint() && rootPresContext) {
1212 if (!CurrentPresShellState()->mIsBackgroundOnly) {
1213 if (pc->HasEverBuiltInvisibleText() ||
1214 DisplayListIsContentful(this, aPaintedContents)) {
1215 pc->NotifyContentfulPaint();
1221 ResetMarkedFramesForDisplayList(aReferenceFrame);
1222 mPresShellStates.RemoveLastElement();
1224 if (!mPresShellStates.IsEmpty()) {
1225 nsPresContext* pc = CurrentPresContext();
1226 nsIDocShell* docShell = pc->GetDocShell();
1227 if (docShell) {
1228 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1230 mIsInChromePresContext = pc->IsChrome();
1231 } else {
1232 for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
1233 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
1235 mFramesMarkedForDisplayIfVisible.SetLength(0);
1239 void nsDisplayListBuilder::FreeClipChains() {
1240 // Iterate the clip chains from newest to oldest (forward
1241 // iteration), so that we destroy descendants first which
1242 // will drop the ref count on their ancestors.
1243 DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;
1245 while (*indirect) {
1246 if (!(*indirect)->mRefCount) {
1247 DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;
1249 mClipDeduplicator.erase(*indirect);
1250 (*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
1251 Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);
1253 *indirect = next;
1254 } else {
1255 indirect = &(*indirect)->mNextClipChainToDestroy;
1260 void nsDisplayListBuilder::FreeTemporaryItems() {
1261 for (nsDisplayItem* i : mTemporaryItems) {
1262 // Temporary display items are not added to the frames.
1263 MOZ_ASSERT(i->Frame());
1264 i->RemoveFrame(i->Frame());
1265 i->Destroy(this);
1268 mTemporaryItems.Clear();
1271 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1272 const nsIFrame* aReferenceFrame) {
1273 // Unmark and pop off the frames marked for display in this pres shell.
1274 uint32_t firstFrameForShell =
1275 CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1276 for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
1277 ++i) {
1278 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
1280 mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1282 firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
1283 for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
1284 mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
1286 mFramesWithOOFData.SetLength(firstFrameForShell);
1289 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1290 CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
1293 void nsDisplayListBuilder::MarkFramesForDisplayList(
1294 nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
1295 nsRect visibleRect = GetVisibleRect();
1296 nsRect dirtyRect = GetDirtyRect();
1298 // If we are entering content that is fixed to the RCD-RSF, we are
1299 // crossing the async zoom container boundary, and need to convert from
1300 // visual to layout coordinates.
1301 if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
1302 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1303 viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
1304 if (viewportFrame->PresShell()->GetRootScrollFrame()) {
1305 #ifdef DEBUG
1306 for (nsIFrame* f : aFrames) {
1307 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
1309 #endif
1310 visibleRect = ViewportUtils::VisualToLayout(visibleRect,
1311 viewportFrame->PresShell());
1312 dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
1313 viewportFrame->PresShell());
1315 #ifdef DEBUG
1316 else {
1317 // This is an edge case that should only happen if we are in a
1318 // document with a XUL root element so that it does not have a root
1319 // scroll frame but it has fixed pos content and all of the frames in
1320 // aFrames are that fixed pos content.
1321 for (nsIFrame* f : aFrames) {
1322 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
1323 f->GetParent() == aDirtyFrame &&
1324 f->StyleDisplay()->mPosition ==
1325 StylePositionProperty::Fixed);
1327 // There's no root scroll frame so there can't be any zooming or async
1328 // panning so we don't need to adjust the visible and dirty rects.
1330 #endif
1334 bool markedFrames = false;
1335 for (nsIFrame* e : aFrames) {
1336 // Skip the AccessibleCaret frame when building no caret.
1337 if (!IsBuildingCaret()) {
1338 nsIContent* content = e->GetContent();
1339 if (content && content->IsInNativeAnonymousSubtree() &&
1340 content->IsElement()) {
1341 auto* classList = content->AsElement()->ClassList();
1342 if (classList->Contains(u"moz-accessiblecaret"_ns)) {
1343 continue;
1347 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
1348 markedFrames = true;
1352 if (markedFrames) {
1353 // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1354 // to objects on the stack, so we need to clone the chain.
1355 const DisplayItemClipChain* clipChain =
1356 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1357 const DisplayItemClipChain* combinedClipChain =
1358 mClipState.GetCurrentCombinedClipChain(this);
1359 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1361 OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
1362 clipChain, combinedClipChain, asr, visibleRect, dirtyRect);
1363 aDirtyFrame->SetProperty(
1364 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
1365 mFramesWithOOFData.AppendElement(aDirtyFrame);
1368 if (!aDirtyFrame->GetParent()) {
1369 // This is the viewport frame of aDirtyFrame's presshell.
1370 // Store the current display data so that it can be used for fixed
1371 // background images.
1372 NS_ASSERTION(
1373 CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
1374 "Presshell mismatch");
1375 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
1376 "already traversed this presshell's root frame?");
1378 const DisplayItemClipChain* clipChain =
1379 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1380 const DisplayItemClipChain* combinedClipChain =
1381 mClipState.GetCurrentCombinedClipChain(this);
1382 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1383 CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
1384 clipChain, combinedClipChain, asr, GetVisibleRect(), GetDirtyRect());
1389 * Mark all preserve-3d children with
1390 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1391 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute
1392 * dirty rect for preserve-3d children.
1394 * @param aDirtyFrame is the frame to mark children extending context.
1396 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1397 nsIFrame* aDirtyFrame) {
1398 for (const auto& childList : aDirtyFrame->ChildLists()) {
1399 for (nsIFrame* child : childList.mList) {
1400 if (child->Combines3DTransformWithAncestors()) {
1401 MarkFrameForDisplay(child, aDirtyFrame);
1404 if (child->IsBlockWrapper()) {
1405 // Mark preserve-3d frames inside the block wrapper.
1406 MarkPreserve3DFramesForDisplayList(child);
1412 ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1413 const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame) {
1414 RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
1415 aParent, aScrollableFrame, IsRetainingDisplayList());
1416 mActiveScrolledRoots.AppendElement(asr);
1417 return asr;
1420 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1421 const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
1422 const DisplayItemClipChain* aParent) {
1423 MOZ_ASSERT(!(aParent && aParent->mOnStack));
1424 void* p = Allocate(sizeof(DisplayItemClipChain),
1425 DisplayListArenaObjectId::CLIPCHAIN);
1426 DisplayItemClipChain* c = new (KnownNotNull, p)
1427 DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy);
1428 #ifdef DEBUG
1429 c->mOnStack = false;
1430 #endif
1431 auto result = mClipDeduplicator.insert(c);
1432 if (!result.second) {
1433 // An equivalent clip chain item was already created, so let's return that
1434 // instead. Destroy the one we just created.
1435 // Note that this can cause clip chains from different coordinate systems to
1436 // collapse into the same clip chain object, because clip chains do not keep
1437 // track of the reference frame that they were created in.
1438 c->DisplayItemClipChain::~DisplayItemClipChain();
1439 Destroy(DisplayListArenaObjectId::CLIPCHAIN, c);
1440 return *(result.first);
1442 mFirstClipChainToDestroy = c;
1443 return c;
1446 struct ClipChainItem {
1447 DisplayItemClip clip;
1448 const ActiveScrolledRoot* asr;
1451 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
1452 const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
1453 for (const ActiveScrolledRoot* asr =
1454 ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
1455 asr; asr = asr->mParent) {
1456 if (aOne == aTwo) {
1457 return aOne;
1459 if (aOne->mASR == asr) {
1460 aOne = aOne->mParent;
1462 if (aTwo->mASR == asr) {
1463 aTwo = aTwo->mParent;
1465 if (!aOne) {
1466 return aTwo;
1468 if (!aTwo) {
1469 return aOne;
1472 return nullptr;
1475 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1476 const DisplayItemClipChain* aAncestor,
1477 const DisplayItemClipChain* aLeafClip1,
1478 const DisplayItemClipChain* aLeafClip2) {
1479 AutoTArray<ClipChainItem, 8> intersectedClips;
1481 const DisplayItemClipChain* clip1 = aLeafClip1;
1482 const DisplayItemClipChain* clip2 = aLeafClip2;
1484 const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
1485 clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);
1487 // Build up the intersection from the leaf to the root and put it into
1488 // intersectedClips. The loop below will convert intersectedClips into an
1489 // actual DisplayItemClipChain.
1490 // (We need to do this in two passes because we need the parent clip in order
1491 // to create the DisplayItemClipChain object, but the parent clip has not
1492 // been created at that point.)
1493 while (!aAncestor || asr != aAncestor->mASR) {
1494 if (clip1 && clip1->mASR == asr) {
1495 if (clip2 && clip2->mASR == asr) {
1496 DisplayItemClip intersection = clip1->mClip;
1497 intersection.IntersectWith(clip2->mClip);
1498 intersectedClips.AppendElement(ClipChainItem{intersection, asr});
1499 clip2 = clip2->mParent;
1500 } else {
1501 intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
1503 clip1 = clip1->mParent;
1504 } else if (clip2 && clip2->mASR == asr) {
1505 intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
1506 clip2 = clip2->mParent;
1508 if (!asr) {
1509 MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
1510 break;
1512 asr = asr->mParent;
1515 // Convert intersectedClips into a DisplayItemClipChain.
1516 const DisplayItemClipChain* parentSC = aAncestor;
1517 for (auto& sc : Reversed(intersectedClips)) {
1518 parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
1520 return parentSC;
1523 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1524 const DisplayItemClipChain* aLeafClip1,
1525 const DisplayItemClipChain* aLeafClip2) {
1526 // aLeafClip2 might be a reference to a clip on the stack. We need to make
1527 // sure that CreateClipChainIntersection will allocate the actual intersected
1528 // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1529 // we supply nullptr as the common ancestor so that
1530 // CreateClipChainIntersection clones the whole chain.
1531 const DisplayItemClipChain* ancestorClip =
1532 aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
1533 : nullptr;
1535 return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2);
1538 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
1539 const DisplayItemClipChain* aClipChain) {
1540 return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
1543 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
1544 const nsIFrame* aFrame, nsPoint* aOffset) const {
1545 auto MaybeApplyAdditionalOffset = [&]() {
1546 if (auto offset = AdditionalOffset()) {
1547 *aOffset += *offset;
1551 if (aFrame == mCurrentFrame) {
1552 if (aOffset) {
1553 *aOffset = mCurrentOffsetToReferenceFrame;
1555 return mCurrentReferenceFrame;
1558 for (const nsIFrame* f = aFrame; f;
1559 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
1560 if (f == mReferenceFrame || f->IsTransformed()) {
1561 if (aOffset) {
1562 *aOffset = aFrame->GetOffsetToCrossDoc(f);
1563 MaybeApplyAdditionalOffset();
1565 return f;
1569 if (aOffset) {
1570 *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1571 MaybeApplyAdditionalOffset();
1574 return mReferenceFrame;
1577 // Sticky frames are active if their nearest scrollable frame is also active.
1578 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
1579 nsIFrame* aFrame) {
1580 MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
1581 StylePositionProperty::Sticky);
1583 StickyScrollContainer* stickyScrollContainer =
1584 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
1585 return stickyScrollContainer &&
1586 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1589 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
1590 nsIFrame** aParent) {
1591 if (aFrame == mReferenceFrame) {
1592 return true;
1595 if (!IsPaintingToWindow()) {
1596 if (aParent) {
1597 *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1599 return false;
1602 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1603 if (!parent) {
1604 return true;
1606 *aParent = parent;
1608 if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
1609 IsStickyFrameActive(this, aFrame)) {
1610 return true;
1613 if (aFrame->IsTransformed()) {
1614 if (EffectCompositor::HasAnimationsForCompositor(
1615 aFrame, DisplayItemType::TYPE_TRANSFORM)) {
1616 return true;
1620 LayoutFrameType parentType = parent->Type();
1621 if (parentType == LayoutFrameType::Scroll ||
1622 parentType == LayoutFrameType::ListControl) {
1623 nsIScrollableFrame* sf = do_QueryFrame(parent);
1624 if (sf->GetScrolledFrame() == aFrame) {
1625 MOZ_ASSERT(!aFrame->IsTransformed());
1626 return sf->IsMaybeAsynchronouslyScrolled();
1630 return false;
1633 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1634 nsIFrame* aFrame) {
1635 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1636 RootReferenceFrame(), aFrame));
1637 nsIFrame* cursor = aFrame;
1638 while (cursor != RootReferenceFrame()) {
1639 nsIFrame* next;
1640 if (IsAnimatedGeometryRoot(cursor, &next)) {
1641 return cursor;
1643 cursor = next;
1645 return cursor;
1648 static nsRect ApplyAllClipNonRoundedIntersection(
1649 const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
1650 nsRect result = aRect;
1651 while (aClipChain) {
1652 result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
1653 aClipChain = aClipChain->mParent;
1655 return result;
1658 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
1659 if (!mWindowDraggingAllowed || !IsForPainting()) {
1660 return;
1663 const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
1664 if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
1665 // This frame has the default value and doesn't influence the window
1666 // dragging region.
1667 return;
1670 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;
1672 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1673 nsIFrame* referenceFrame =
1674 const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1676 if (IsInTransform()) {
1677 // Only support 2d rectilinear transforms. Transform support is needed for
1678 // the horizontal flip transform that's applied to the urlbar textbox in
1679 // RTL mode - it should be able to exclude itself from the draggable region.
1680 referenceFrameToRootReferenceFrame =
1681 ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
1682 nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
1683 RelativeTo{mReferenceFrame})
1684 .GetMatrix());
1685 Matrix referenceFrameToRootReferenceFrame2d;
1686 if (!referenceFrameToRootReferenceFrame.Is2D(
1687 &referenceFrameToRootReferenceFrame2d) ||
1688 !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1689 return;
1691 } else {
1692 MOZ_ASSERT(referenceFrame == mReferenceFrame,
1693 "referenceFrameToRootReferenceFrame needs to be adjusted");
1696 // We do some basic visibility checking on the frame's border box here.
1697 // We intersect it both with the current dirty rect and with the current
1698 // clip. Either one is just a conservative approximation on its own, but
1699 // their intersection luckily works well enough for our purposes, so that
1700 // we don't have to do full-blown visibility computations.
1701 // The most important case we need to handle is the scrolled-off tab:
1702 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1703 // should not be allowed to interfere with the window dragging region. Using
1704 // just the current DisplayItemClip is not enough to cover this case
1705 // completely because clips are reset while building stacking context
1706 // contents, so for example we'd fail to clip frames that have a clip path
1707 // applied to them. But the current dirty rect doesn't get reset in that
1708 // case, so we use it to make this case work.
1709 nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
1710 borderBox += ToReferenceFrame(aFrame);
1711 const DisplayItemClipChain* clip =
1712 ClipState().GetCurrentCombinedClipChain(this);
1713 borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
1714 if (borderBox.IsEmpty()) {
1715 return;
1718 LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
1719 borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1721 LayoutDeviceRect transformedDevPixelBorderBox =
1722 TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1723 transformedDevPixelBorderBox.Round();
1724 LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1726 if (!transformedDevPixelBorderBox.ToIntRect(
1727 &transformedDevPixelBorderBoxInt)) {
1728 return;
1731 LayoutDeviceIntRegion& region =
1732 styleUI->mWindowDragging == StyleWindowDragging::Drag
1733 ? mWindowDraggingRegion
1734 : mWindowNoDraggingRegion;
1736 if (!IsRetainingDisplayList()) {
1737 region.OrWith(transformedDevPixelBorderBoxInt);
1738 return;
1741 gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
1742 if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
1743 mRetainedWindowDraggingRegion.Add(aFrame, rect);
1744 } else {
1745 mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
1749 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
1750 LayoutDeviceIntRegion result;
1751 if (!IsRetainingDisplayList()) {
1752 result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
1753 return result;
1756 LayoutDeviceIntRegion dragRegion =
1757 mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();
1759 LayoutDeviceIntRegion noDragRegion =
1760 mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();
1762 result.Sub(dragRegion, noDragRegion);
1763 return result;
1766 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1767 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes);
1768 aSizes.mLayoutRetainedDisplayListSize +=
1769 aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get());
1772 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1773 mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);
1775 size_t n = 0;
1776 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
1777 n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1778 n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1779 n += mEffectsUpdates.ShallowSizeOfExcludingThis(mallocSizeOf);
1780 n += mWindowExcludeGlassRegion.SizeOfExcludingThis(mallocSizeOf);
1781 n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1782 n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1783 n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
1784 // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1786 aSizes.mLayoutRetainedDisplayListSize += n;
1789 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1790 for (nsDisplayItem* item : *this) {
1791 item->AddSizeOfExcludingThis(aSizes);
1792 if (RetainedDisplayList* children = item->GetChildren()) {
1793 children->AddSizeOfExcludingThis(aSizes);
1797 size_t n = 0;
1799 n += mDAG.mDirectPredecessorList.ShallowSizeOfExcludingThis(
1800 aSizes.mState.mMallocSizeOf);
1801 n += mDAG.mNodesInfo.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1802 n += mOldItems.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1804 aSizes.mLayoutRetainedDisplayListSize += n;
1807 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1808 MallocSizeOf aMallocSizeOf) const {
1809 size_t n = 0;
1810 n += mFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
1811 for (const auto& frame : mFrames) {
1812 const UniquePtr<WeakFrame>& weakFrame = frame.mWeakFrame;
1813 n += aMallocSizeOf(weakFrame.get());
1815 n += mRects.ShallowSizeOfExcludingThis(aMallocSizeOf);
1816 return n;
1820 * Removes modified frames and rects from this WeakFrameRegion.
1822 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1823 MOZ_ASSERT(mFrames.Length() == mRects.Length());
1825 uint32_t i = 0;
1826 uint32_t length = mFrames.Length();
1828 while (i < length) {
1829 auto& wrapper = mFrames[i];
1831 if (!wrapper.mWeakFrame->IsAlive() ||
1832 AnyContentAncestorModified(wrapper.mWeakFrame->GetFrame())) {
1833 // To avoid multiple O(n) shifts in the array, move the last element of
1834 // the array to the current position and decrease the array length.
1835 mFrameSet.Remove(wrapper.mFrame);
1836 mFrames[i] = std::move(mFrames[length - 1]);
1837 mRects[i] = std::move(mRects[length - 1]);
1838 length--;
1839 } else {
1840 i++;
1844 mFrames.TruncateLength(length);
1845 mRects.TruncateLength(length);
1848 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1849 mRetainedWindowDraggingRegion.RemoveModifiedFramesAndRects();
1850 mRetainedWindowNoDraggingRegion.RemoveModifiedFramesAndRects();
1851 mWindowExcludeGlassRegion.RemoveModifiedFramesAndRects();
1852 mRetainedWindowOpaqueRegion.RemoveModifiedFramesAndRects();
1854 mHasGlassItemDuringPartial = false;
1857 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1858 mRetainedWindowDraggingRegion.Clear();
1859 mRetainedWindowNoDraggingRegion.Clear();
1860 mWindowExcludeGlassRegion.Clear();
1861 mRetainedWindowOpaqueRegion.Clear();
1863 mGlassDisplayItem = nullptr;
1866 const uint32_t gWillChangeAreaMultiplier = 3;
1867 static uint32_t GetLayerizationCost(const nsSize& aSize) {
1868 // There's significant overhead for each layer created from Gecko
1869 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1870 // Therefore we set a minimum cost threshold of a 64x64 area.
1871 const int minBudgetCost = 64 * 64;
1873 const uint32_t budgetCost = std::max(
1874 minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
1875 nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
1877 return budgetCost;
1880 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
1881 const nsSize& aSize) {
1882 MOZ_ASSERT(IsForPainting());
1884 if (aFrame->MayHaveWillChangeBudget()) {
1885 // The frame is already in the will-change budget.
1886 return true;
1889 const nsPresContext* presContext = aFrame->PresContext();
1890 const nsRect area = presContext->GetVisibleArea();
1891 const uint32_t budgetLimit =
1892 nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1893 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1894 const uint32_t cost = GetLayerizationCost(aSize);
1896 DocumentWillChangeBudget& documentBudget =
1897 mDocumentWillChangeBudgets.LookupOrInsert(presContext);
1899 const bool onBudget =
1900 (documentBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
1902 if (onBudget) {
1903 documentBudget += cost;
1904 mFrameWillChangeBudgets.InsertOrUpdate(
1905 aFrame, FrameWillChangeBudget(presContext, cost));
1906 aFrame->SetMayHaveWillChangeBudget(true);
1909 return onBudget;
1912 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
1913 const nsSize& aSize) {
1914 if (!IsForPainting()) {
1915 // If this nsDisplayListBuilder is not for painting, the layerization should
1916 // not matter. Do the simple thing and return false.
1917 return false;
1920 const bool onBudget = AddToWillChangeBudget(aFrame, aSize);
1921 if (onBudget) {
1922 return true;
1925 auto* pc = aFrame->PresContext();
1926 auto* doc = pc->Document();
1927 if (!doc->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget)) {
1928 AutoTArray<nsString, 2> params;
1929 params.AppendElement()->AppendInt(gWillChangeAreaMultiplier);
1931 nsRect area = pc->GetVisibleArea();
1932 uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1933 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1934 params.AppendElement()->AppendInt(budgetLimit);
1936 doc->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget, false, params);
1939 return false;
1942 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame* aFrame) {
1943 MOZ_ASSERT(IsForPainting());
1945 if (!aFrame->MayHaveWillChangeBudget()) {
1946 return;
1949 aFrame->SetMayHaveWillChangeBudget(false);
1950 RemoveFromWillChangeBudgets(aFrame);
1953 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame* aFrame) {
1954 if (auto entry = mFrameWillChangeBudgets.Lookup(aFrame)) {
1955 const FrameWillChangeBudget& frameBudget = entry.Data();
1957 auto documentBudget =
1958 mDocumentWillChangeBudgets.Lookup(frameBudget.mPresContext);
1960 if (documentBudget) {
1961 *documentBudget -= frameBudget.mUsage;
1964 entry.Remove();
1968 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1969 mFrameWillChangeBudgets.Clear();
1970 mDocumentWillChangeBudgets.Clear();
1973 void nsDisplayListBuilder::EnterSVGEffectsContents(
1974 nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) {
1975 MOZ_ASSERT(aHoistedItemsStorage);
1976 if (mSVGEffectsFrames.IsEmpty()) {
1977 MOZ_ASSERT(!mScrollInfoItemsForHoisting);
1978 mScrollInfoItemsForHoisting = aHoistedItemsStorage;
1980 mSVGEffectsFrames.AppendElement(aEffectsFrame);
1983 void nsDisplayListBuilder::ExitSVGEffectsContents() {
1984 MOZ_ASSERT(!mSVGEffectsFrames.IsEmpty());
1985 mSVGEffectsFrames.RemoveLastElement();
1986 MOZ_ASSERT(mScrollInfoItemsForHoisting);
1987 if (mSVGEffectsFrames.IsEmpty()) {
1988 mScrollInfoItemsForHoisting = nullptr;
1992 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
1994 * Note: if changing the conditions under which scroll info layers
1995 * are created, make a corresponding change to
1996 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
1998 for (nsIFrame* frame : mSVGEffectsFrames) {
1999 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame)) {
2000 return true;
2003 return false;
2006 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
2007 nsDisplayScrollInfoLayer* aScrollInfoItem) {
2008 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2009 MOZ_ASSERT(mScrollInfoItemsForHoisting);
2010 mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
2013 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2014 nsIFrame* aFrame, nsDisplayList* aList) {
2015 MOZ_ASSERT(aFrame);
2016 MOZ_ASSERT(aList);
2018 if (!BuildCompositorHitTestInfo()) {
2019 return;
2022 const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
2023 if (info != CompositorHitTestInvisibleToHit) {
2024 aList->AppendNewToTop<nsDisplayCompositorHitTestInfo>(this, aFrame);
2028 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem* aItem) {
2029 mReuseableItems.Insert(aItem);
2032 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem* aItem) {
2033 MOZ_ASSERT(aItem->IsReusedItem());
2034 mReuseableItems.Remove(aItem);
2037 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2038 const size_t total = mReuseableItems.Count();
2040 size_t reused = 0;
2041 for (auto* item : mReuseableItems) {
2042 if (item->IsReusedItem()) {
2043 reused++;
2044 item->SetReusable();
2045 } else {
2046 item->Destroy(this);
2050 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
2051 mReuseableItems.Clear();
2054 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
2055 const auto* previous = mCurrentContainerASR;
2056 const auto* asr = aItem->GetActiveScrolledRoot();
2057 mCurrentContainerASR =
2058 ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
2060 if (previous != mCurrentContainerASR) {
2061 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
2062 mCurrentContainerASR);
2065 aItem->SetReusedItem();
2068 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
2069 aDestination.BorderBackground()->AppendToTop(BorderBackground());
2070 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2071 aDestination.Floats()->AppendToTop(Floats());
2072 aDestination.Content()->AppendToTop(Content());
2073 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
2074 aDestination.Outlines()->AppendToTop(Outlines());
2077 nsRect nsDisplayList::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2078 nsRect bounds;
2079 for (nsDisplayItem* i : *this) {
2080 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
2082 return bounds;
2085 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR(
2086 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
2087 nsRect* aBuildingRect) const {
2088 nsRect bounds;
2089 for (nsDisplayItem* i : *this) {
2090 nsRect r = i->GetClippedBounds(aBuilder);
2091 if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
2092 if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) {
2093 r = clip.ref();
2096 if (aBuildingRect) {
2097 aBuildingRect->UnionRect(*aBuildingRect, i->GetBuildingRect());
2099 bounds.UnionRect(bounds, r);
2101 return bounds;
2104 nsRect nsDisplayList::GetBuildingRect() const {
2105 nsRect result;
2106 for (nsDisplayItem* i : *this) {
2107 result.UnionRect(result, i->GetBuildingRect());
2109 return result;
2112 static void TriggerPendingAnimations(Document& aDoc,
2113 const TimeStamp& aReadyTime) {
2114 MOZ_ASSERT(!aReadyTime.IsNull(),
2115 "Animation ready time is not set. Perhaps we're using a layer"
2116 " manager that doesn't update it");
2117 if (PendingAnimationTracker* tracker = aDoc.GetPendingAnimationTracker()) {
2118 PresShell* presShell = aDoc.GetPresShell();
2119 // If paint-suppression is in effect then we haven't finished painting
2120 // this document yet so we shouldn't start animations
2121 if (!presShell || !presShell->IsPaintingSuppressed()) {
2122 tracker->TriggerPendingAnimationsOnNextTick(aReadyTime);
2125 auto recurse = [&aReadyTime](Document& aDoc) {
2126 TriggerPendingAnimations(aDoc, aReadyTime);
2127 return CallState::Continue;
2129 aDoc.EnumerateSubDocuments(recurse);
2132 WindowRenderer* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView** aView) {
2133 if (aView) {
2134 *aView = RootReferenceFrame()->GetView();
2136 if (RootReferenceFrame() !=
2137 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2138 return nullptr;
2140 nsIWidget* window = RootReferenceFrame()->GetNearestWidget();
2141 if (window) {
2142 return window->GetWindowRenderer();
2144 return nullptr;
2147 WebRenderLayerManager* nsDisplayListBuilder::GetWidgetLayerManager(
2148 nsView** aView) {
2149 WindowRenderer* renderer = GetWidgetWindowRenderer();
2150 if (renderer) {
2151 return renderer->AsWebRender();
2153 return nullptr;
2156 void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2157 int32_t aAppUnitsPerDevPixel) {
2158 FlattenedDisplayListIterator iter(aBuilder, this);
2159 while (iter.HasNext()) {
2160 nsPaintedDisplayItem* item = iter.GetNextItem()->AsPaintedDisplayItem();
2161 if (!item) {
2162 continue;
2165 nsRegion visible(item->GetClippedBounds(aBuilder));
2166 visible.And(visible, item->GetPaintRect(aBuilder, aCtx));
2167 if (visible.IsEmpty()) {
2168 continue;
2170 nsRect buildingRect = item->GetBuildingRect();
2171 item->SetBuildingRect(visible.GetBounds());
2173 DisplayItemClip currentClip = item->GetClip();
2174 if (currentClip.HasClip()) {
2175 aCtx->Save();
2176 if (currentClip.IsRectClippedByRoundedCorner(visible.GetBounds())) {
2177 currentClip.ApplyTo(aCtx, aAppUnitsPerDevPixel);
2178 } else {
2179 currentClip.ApplyRectTo(aCtx, aAppUnitsPerDevPixel);
2182 aCtx->NewPath();
2184 item->Paint(aBuilder, aCtx);
2186 if (currentClip.HasClip()) {
2187 aCtx->Restore();
2189 item->SetBuildingRect(buildingRect);
2194 * We paint by executing a layer manager transaction, constructing a
2195 * single layer representing the display list, and then making it the
2196 * root of the layer manager, drawing into the PaintedLayers.
2198 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2199 uint32_t aFlags,
2200 Maybe<double> aDisplayListBuildTime) {
2201 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS);
2203 RefPtr<WebRenderLayerManager> layerManager;
2204 WindowRenderer* renderer = nullptr;
2205 bool widgetTransaction = false;
2206 bool doBeginTransaction = true;
2207 nsView* view = nullptr;
2208 if (aFlags & PAINT_USE_WIDGET_LAYERS) {
2209 renderer = aBuilder->GetWidgetWindowRenderer(&view);
2210 if (renderer) {
2211 // The fallback renderer doesn't retain any content, so it's
2212 // not meaningful to use it when drawing to an external context.
2213 if (aCtx && renderer->AsFallback()) {
2214 MOZ_ASSERT(!(aFlags & PAINT_EXISTING_TRANSACTION));
2215 renderer = nullptr;
2216 } else {
2217 layerManager = renderer->AsWebRender();
2218 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
2219 widgetTransaction = true;
2224 nsIFrame* frame = aBuilder->RootReferenceFrame();
2225 nsPresContext* presContext = frame->PresContext();
2226 PresShell* presShell = presContext->PresShell();
2227 Document* document = presShell->GetDocument();
2229 ScopeExit g([&]() {
2230 // For layers-free mode, we check the invalidation state bits in the
2231 // EndTransaction. So we clear the invalidation state bits after
2232 // EndTransaction.
2233 if (widgetTransaction ||
2234 // SVG-as-an-image docs don't paint as part of the retained layer tree,
2235 // but they still need the invalidation state bits cleared in order for
2236 // invalidation for CSS/SMIL animation to work properly.
2237 (document && document->IsBeingUsedAsImage())) {
2238 DL_LOGD("Clearing invalidation state bits");
2239 frame->ClearInvalidationStateBits();
2243 if (!renderer) {
2244 if (!aCtx) {
2245 NS_WARNING("Nowhere to paint into");
2246 return;
2248 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(false);
2249 Paint(aBuilder, aCtx, presContext->AppUnitsPerDevPixel());
2251 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2252 return;
2255 if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) {
2256 MOZ_ASSERT(layerManager);
2257 if (doBeginTransaction) {
2258 if (aCtx) {
2259 if (!layerManager->BeginTransactionWithTarget(aCtx, nsCString())) {
2260 return;
2262 } else {
2263 if (!layerManager->BeginTransaction(nsCString())) {
2264 return;
2269 bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(true);
2270 layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
2272 bool sent = false;
2273 if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
2274 sent = layerManager->EndEmptyTransaction();
2277 if (!sent) {
2278 auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
2280 nsIDocShell* docShell = presContext->GetDocShell();
2281 WrFiltersHolder wrFilters;
2282 gfx::Matrix5x4* colorMatrix =
2283 nsDocShell::Cast(docShell)->GetColorMatrix();
2284 if (colorMatrix) {
2285 wrFilters.filters.AppendElement(
2286 wr::FilterOp::ColorMatrix(colorMatrix->components));
2289 wrManager->EndTransactionWithoutLayer(this, aBuilder,
2290 std::move(wrFilters), nullptr,
2291 aDisplayListBuildTime.valueOr(0.0));
2294 aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2295 if (document && widgetTransaction) {
2296 TriggerPendingAnimations(*document,
2297 layerManager->GetAnimationReadyTime());
2300 if (presContext->RefreshDriver()->HasScheduleFlush()) {
2301 presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2302 frame->GetRect());
2305 return;
2308 FallbackRenderer* fallback = renderer->AsFallback();
2309 MOZ_ASSERT(fallback);
2311 if (doBeginTransaction) {
2312 MOZ_ASSERT(!aCtx);
2313 if (!fallback->BeginTransaction()) {
2314 return;
2318 bool temp = aBuilder->SetIsCompositingCheap(renderer->IsCompositingCheap());
2319 fallback->EndTransactionWithList(aBuilder, this,
2320 presContext->AppUnitsPerDevPixel(),
2321 WindowRenderer::END_DEFAULT);
2323 aBuilder->SetIsCompositingCheap(temp);
2325 if (document && widgetTransaction) {
2326 TriggerPendingAnimations(*document, renderer->GetAnimationReadyTime());
2330 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
2331 for (auto* item : TakeItems()) {
2332 item->Destroy(aBuilder);
2336 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) {
2337 return aFrame->Style()->PointerEvents() != StylePointerEvents::None;
2340 // A list of frames, and their z depth. Used for sorting
2341 // the results of hit testing.
2342 struct FramesWithDepth {
2343 explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {}
2345 bool operator<(const FramesWithDepth& aOther) const {
2346 if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
2347 // We want to sort so that the shallowest item (highest depth value) is
2348 // first
2349 return mDepth > aOther.mDepth;
2351 return this < &aOther;
2353 bool operator==(const FramesWithDepth& aOther) const {
2354 return this == &aOther;
2357 float mDepth;
2358 nsTArray<nsIFrame*> mFrames;
2361 // Sort the frames by depth and then moves all the contained frames to the
2362 // destination
2363 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource,
2364 nsTArray<nsIFrame*>* aDest) {
2365 if (aSource.IsEmpty()) {
2366 return;
2368 aSource.Sort();
2369 uint32_t length = aSource.Length();
2370 for (uint32_t i = 0; i < length; i++) {
2371 aDest->AppendElements(std::move(aSource[i].mFrames));
2373 aSource.Clear();
2376 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2377 nsDisplayItem::HitTestState* aState,
2378 nsTArray<nsIFrame*>* aOutFrames) const {
2379 nsDisplayItem* item;
2381 if (aState->mInPreserves3D) {
2382 // Collect leaves of the current 3D rendering context.
2383 for (nsDisplayItem* item : *this) {
2384 auto itemType = item->GetType();
2385 if (itemType != DisplayItemType::TYPE_TRANSFORM ||
2386 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2387 item->HitTest(aBuilder, aRect, aState, aOutFrames);
2388 } else {
2389 // One of leaves in the current 3D rendering context.
2390 aState->mItemBuffer.AppendElement(item);
2393 return;
2396 int32_t itemBufferStart = aState->mItemBuffer.Length();
2397 for (nsDisplayItem* item : *this) {
2398 aState->mItemBuffer.AppendElement(item);
2401 AutoTArray<FramesWithDepth, 16> temp;
2402 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart;
2403 --i) {
2404 // Pop element off the end of the buffer. We want to shorten the buffer
2405 // so that recursive calls to HitTest have more buffer space.
2406 item = aState->mItemBuffer[i];
2407 aState->mItemBuffer.SetLength(i);
2409 bool snap;
2410 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
2411 auto itemType = item->GetType();
2412 bool same3DContext =
2413 (itemType == DisplayItemType::TYPE_TRANSFORM &&
2414 static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
2415 (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
2416 item->Frame()->Extend3DContext());
2417 if (same3DContext &&
2418 (itemType != DisplayItemType::TYPE_TRANSFORM ||
2419 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
2420 if (!item->GetClip().MayIntersect(aRect)) {
2421 continue;
2423 AutoTArray<nsIFrame*, 1> neverUsed;
2424 // Start gethering leaves of the 3D rendering context, and
2425 // append leaves at the end of mItemBuffer. Leaves are
2426 // processed at following iterations.
2427 aState->mInPreserves3D = true;
2428 item->HitTest(aBuilder, aRect, aState, &neverUsed);
2429 aState->mInPreserves3D = false;
2430 i = aState->mItemBuffer.Length();
2431 continue;
2433 if (same3DContext || item->GetClip().MayIntersect(r)) {
2434 AutoTArray<nsIFrame*, 16> outFrames;
2435 item->HitTest(aBuilder, aRect, aState, &outFrames);
2437 // For 3d transforms with preserve-3d we add hit frames into the temp list
2438 // so we can sort them later, otherwise we add them directly to the output
2439 // list.
2440 nsTArray<nsIFrame*>* writeFrames = aOutFrames;
2441 if (item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
2442 static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2443 if (outFrames.Length()) {
2444 nsDisplayTransform* transform =
2445 static_cast<nsDisplayTransform*>(item);
2446 nsPoint point = aRect.TopLeft();
2447 // A 1x1 rect means a point, otherwise use the center of the rect
2448 if (aRect.width != 1 || aRect.height != 1) {
2449 point = aRect.Center();
2451 temp.AppendElement(
2452 FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
2453 writeFrames = &temp[temp.Length() - 1].mFrames;
2455 } else {
2456 // We may have just finished a run of consecutive preserve-3d
2457 // transforms, so flush these into the destination array before
2458 // processing our frame list.
2459 FlushFramesArray(temp, aOutFrames);
2462 for (uint32_t j = 0; j < outFrames.Length(); j++) {
2463 nsIFrame* f = outFrames.ElementAt(j);
2464 // Filter out some frames depending on the type of hittest
2465 // we are doing. For visibility tests, pass through all frames.
2466 // For pointer tests, only pass through frames that are styled
2467 // to receive pointer events.
2468 if (aBuilder->HitTestIsForVisibility() ||
2469 IsFrameReceivingPointerEvents(f)) {
2470 writeFrames->AppendElement(f);
2474 if (aBuilder->HitTestIsForVisibility()) {
2475 aState->mHitOccludingItem = [&] {
2476 if (aState->mHitOccludingItem) {
2477 // We already hit something before.
2478 return true;
2480 if (aState->mCurrentOpacity == 1.0f &&
2481 item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) {
2482 // An opaque item always occludes everything. Note that we need to
2483 // check wrapping opacity and such as well.
2484 return true;
2486 float threshold = aBuilder->VisibilityThreshold();
2487 if (threshold == 1.0f) {
2488 return false;
2490 float itemOpacity = [&] {
2491 switch (item->GetType()) {
2492 case DisplayItemType::TYPE_OPACITY:
2493 return static_cast<nsDisplayOpacity*>(item)->GetOpacity();
2494 case DisplayItemType::TYPE_BACKGROUND_COLOR:
2495 return static_cast<nsDisplayBackgroundColor*>(item)
2496 ->GetOpacity();
2497 default:
2498 // Be conservative and assume it won't occlude other items.
2499 return 0.0f;
2501 }();
2502 return itemOpacity * aState->mCurrentOpacity >= threshold;
2503 }();
2505 if (aState->mHitOccludingItem) {
2506 // We're exiting early, so pop the remaining items off the buffer.
2507 aState->mItemBuffer.TruncateLength(itemBufferStart);
2508 break;
2513 // Clear any remaining preserve-3d transforms.
2514 FlushFramesArray(temp, aOutFrames);
2515 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
2516 "How did we forget to pop some elements?");
2519 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, Document* aDoc) {
2520 nsIFrame* f = aItem->Frame();
2521 while (f) {
2522 nsPresContext* pc = f->PresContext();
2523 if (pc->Document() == aDoc) {
2524 return f->GetContent();
2526 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(
2527 pc->PresShell()->GetRootFrame());
2529 return nullptr;
2532 struct ZSortItem {
2533 nsDisplayItem* item;
2534 int32_t zIndex;
2536 explicit ZSortItem(nsDisplayItem* aItem)
2537 : item(aItem), zIndex(aItem->ZIndex()) {}
2539 operator nsDisplayItem*() { return item; }
2542 struct ZOrderComparator {
2543 bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const {
2544 // Note that we can't just take the difference of the two
2545 // z-indices here, because that might overflow a 32-bit int.
2546 return aLeft.zIndex < aRight.zIndex;
2550 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); }
2552 struct ContentComparator {
2553 nsIContent* mCommonAncestor;
2555 explicit ContentComparator(nsIContent* aCommonAncestor)
2556 : mCommonAncestor(aCommonAncestor) {}
2558 bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const {
2559 // It's possible that the nsIContent for aItem1 or aItem2 is in a
2560 // subdocument of commonAncestor, because display items for subdocuments
2561 // have been mixed into the same list. Ensure that we're looking at content
2562 // in commonAncestor's document.
2563 Document* commonAncestorDoc = mCommonAncestor->OwnerDoc();
2564 nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc);
2565 nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc);
2566 if (!content1 || !content2) {
2567 NS_ERROR("Document trees are mixed up!");
2568 // Something weird going on
2569 return true;
2571 return nsLayoutUtils::CompareTreePosition(content1, content2,
2572 mCommonAncestor) < 0;
2576 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) {
2577 Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor));
2580 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2581 static_assert(sizeof(nsDisplayItem) <= 176, "nsDisplayItem has grown");
2582 #endif
2584 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2585 : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
2587 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2588 const ActiveScrolledRoot* aActiveScrolledRoot)
2589 : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) {
2590 MOZ_COUNT_CTOR(nsDisplayItem);
2591 MOZ_ASSERT(mFrame);
2592 if (aBuilder->IsRetainingDisplayList()) {
2593 mFrame->AddDisplayItem(this);
2596 aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
2597 NS_ASSERTION(
2598 aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
2599 "visible rect not set");
2601 mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2603 // The visible rect is for mCurrentFrame, so we have to use
2604 // mCurrentOffsetToReferenceFrame
2605 nsRect visible = aBuilder->GetVisibleRect() +
2606 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
2607 SetBuildingRect(visible);
2609 const nsStyleDisplay* disp = mFrame->StyleDisplay();
2610 if (mFrame->BackfaceIsHidden(disp)) {
2611 mItemFlags += ItemFlag::BackfaceHidden;
2613 if (mFrame->Combines3DTransformWithAncestors()) {
2614 mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
2618 void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
2620 bool nsDisplayItem::HasDeletedFrame() const {
2621 bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) ||
2622 (GetType() == DisplayItemType::TYPE_REMOTE &&
2623 !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader());
2624 MOZ_ASSERT(retval || mFrame);
2625 return retval;
2628 /* static */
2629 bool nsDisplayItem::ForceActiveLayers() {
2630 return StaticPrefs::layers_force_active();
2633 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); }
2635 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
2636 bool aStore) {
2637 mClipChain = aClipChain;
2640 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
2641 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2642 if (const DisplayItemClip* clip =
2643 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
2644 return Some(clip->GetClipRect());
2646 #ifdef DEBUG
2647 MOZ_ASSERT(false, "item should have finite clip with respect to aASR");
2648 #endif
2649 return Nothing();
2652 const DisplayItemClip& nsDisplayItem::GetClip() const {
2653 const DisplayItemClip* clip =
2654 DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot);
2655 return clip ? *clip : DisplayItemClip::NoClip();
2658 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
2659 const DisplayItemClipChain* aOther,
2660 bool aStore) {
2661 if (!aOther || mClipChain == aOther) {
2662 return;
2665 // aOther might be a reference to a clip on the stack. We need to make sure
2666 // that CreateClipChainIntersection will allocate the actual intersected
2667 // clip in the builder's arena, so for the mClipChain == nullptr case,
2668 // we supply nullptr as the common ancestor so that
2669 // CreateClipChainIntersection clones the whole chain.
2670 const DisplayItemClipChain* ancestorClip =
2671 mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther)
2672 : nullptr;
2674 SetClipChain(
2675 aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther),
2676 aStore);
2679 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2680 bool snap;
2681 nsRect r = GetBounds(aBuilder, &snap);
2682 return GetClip().ApplyNonRoundedIntersection(r);
2685 nsDisplayContainer::nsDisplayContainer(
2686 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2687 const ActiveScrolledRoot* aActiveScrolledRoot, nsDisplayList* aList)
2688 : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
2689 mChildren(aBuilder) {
2690 MOZ_COUNT_CTOR(nsDisplayContainer);
2691 mChildren.AppendToTop(aList);
2692 UpdateBounds(aBuilder);
2694 // Clear and store the clip chain set by nsDisplayItem constructor.
2695 nsDisplayItem::SetClipChain(nullptr, true);
2698 nsRect nsDisplayItem::GetPaintRect(nsDisplayListBuilder* aBuilder,
2699 gfxContext* aCtx) {
2700 bool dummy;
2701 nsRect result = GetBounds(aBuilder, &dummy);
2702 if (aCtx) {
2703 result.IntersectRect(result,
2704 nsLayoutUtils::RoundGfxRectToAppRect(
2705 aCtx->GetClipExtents(),
2706 mFrame->PresContext()->AppUnitsPerDevPixel()));
2708 return result;
2711 bool nsDisplayContainer::CreateWebRenderCommands(
2712 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2713 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2714 nsDisplayListBuilder* aDisplayListBuilder) {
2715 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2716 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
2717 false);
2718 return true;
2721 nsRect nsDisplayContainer::GetBounds(nsDisplayListBuilder* aBuilder,
2722 bool* aSnap) const {
2723 *aSnap = false;
2724 return mBounds;
2727 nsRect nsDisplayContainer::GetComponentAlphaBounds(
2728 nsDisplayListBuilder* aBuilder) const {
2729 return mChildren.GetComponentAlphaBounds(aBuilder);
2732 static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2733 nsDisplayList* aList,
2734 const nsRect& aListBounds) {
2735 return aList->GetOpaqueRegion(aBuilder);
2738 nsRegion nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2739 bool* aSnap) const {
2740 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
2741 GetBounds(aBuilder, aSnap));
2744 Maybe<nsRect> nsDisplayContainer::GetClipWithRespectToASR(
2745 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2746 // Our children should have finite bounds with respect to |aASR|.
2747 if (aASR == mActiveScrolledRoot) {
2748 return Some(mBounds);
2751 return Some(mChildren.GetClippedBoundsWithRespectToASR(aBuilder, aASR));
2754 void nsDisplayContainer::HitTest(nsDisplayListBuilder* aBuilder,
2755 const nsRect& aRect, HitTestState* aState,
2756 nsTArray<nsIFrame*>* aOutFrames) {
2757 mChildren.HitTest(aBuilder, aRect, aState, aOutFrames);
2760 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder* aBuilder) {
2761 // Container item bounds are expected to be clipped.
2762 mBounds =
2763 mChildren.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
2766 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder,
2767 bool* aSnap) const {
2768 *aSnap = true;
2769 return mBounds;
2772 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
2773 gfxContext* aCtx) {
2774 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2775 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2776 Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx),
2777 appUnitsPerDevPixel, *drawTarget);
2778 drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
2781 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) {
2782 aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
2783 << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
2784 << ")";
2787 bool nsDisplaySolidColor::CreateWebRenderCommands(
2788 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2789 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2790 nsDisplayListBuilder* aDisplayListBuilder) {
2791 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
2792 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
2793 wr::LayoutRect r = wr::ToLayoutRect(bounds);
2794 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, mIsCheckerboardBackground,
2795 wr::ToColorF(ToDeviceColor(mColor)));
2797 return true;
2800 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
2801 bool* aSnap) const {
2802 *aSnap = true;
2803 return mRegion.GetBounds();
2806 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
2807 gfxContext* aCtx) {
2808 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2809 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2810 ColorPattern color(ToDeviceColor(mColor));
2811 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2812 Rect rect =
2813 NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
2814 drawTarget->FillRect(rect, color);
2818 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) {
2819 aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255)
2820 << "," << int(mColor.b * 255) << "," << mColor.a << ")";
2823 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2824 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2825 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2826 nsDisplayListBuilder* aDisplayListBuilder) {
2827 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2828 nsRect rect = iter.Get();
2829 LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
2830 rect, mFrame->PresContext()->AppUnitsPerDevPixel());
2831 wr::LayoutRect r = wr::ToLayoutRect(layerRects);
2832 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
2833 wr::ToColorF(ToDeviceColor(mColor)));
2836 return true;
2839 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder,
2840 nsDisplayItem* aItem, nsIFrame* aFrame,
2841 nsITheme::ThemeGeometryType aType) {
2842 if (aBuilder->IsInChromeDocumentOrPopup()) {
2843 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2844 bool preservesAxisAlignedRectangles = false;
2845 nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(
2846 aFrame, aFrame->GetRectRelativeToSelf(), displayRoot,
2847 &preservesAxisAlignedRectangles);
2848 if (preservesAxisAlignedRectangles) {
2849 aBuilder->RegisterThemeGeometry(
2850 aType, aItem,
2851 LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels(
2852 aFrame->PresContext()->AppUnitsPerDevPixel())));
2857 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2858 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
2859 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
2860 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
2861 nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
2862 nsRect rootRect = rootFrame->GetRectRelativeToSelf();
2863 if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) ==
2864 nsLayoutUtils::TRANSFORM_SUCCEEDED) {
2865 return Some(rootRect + aBuilder->ToReferenceFrame(aFrame));
2867 return Nothing();
2870 /* static */ nsDisplayBackgroundImage::InitData
2871 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder,
2872 nsIFrame* aFrame, uint16_t aLayer,
2873 const nsRect& aBackgroundRect,
2874 ComputedStyle* aBackgroundStyle) {
2875 nsPresContext* presContext = aFrame->PresContext();
2876 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2877 const nsStyleImageLayers::Layer& layer =
2878 aBackgroundStyle->StyleBackground()->mImage.mLayers[aLayer];
2880 bool isTransformedFixed;
2881 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
2882 presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer,
2883 &isTransformedFixed);
2885 // background-attachment:fixed is treated as background-attachment:scroll
2886 // if it's affected by a transform.
2887 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2888 bool shouldTreatAsFixed =
2889 layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2890 !isTransformedFixed;
2892 bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsNone();
2893 bool isRasterImage = state.mImageRenderer.IsRasterImage();
2894 nsCOMPtr<imgIContainer> image;
2895 if (isRasterImage) {
2896 image = state.mImageRenderer.GetImage();
2898 return InitData{aBuilder, aBackgroundStyle, image,
2899 aBackgroundRect, state.mFillArea, state.mDestArea,
2900 aLayer, isRasterImage, shouldFixToViewport};
2903 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2904 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aInitData,
2905 nsIFrame* aFrameForBounds)
2906 : nsPaintedDisplayItem(aBuilder, aFrame),
2907 mBackgroundStyle(aInitData.backgroundStyle),
2908 mImage(aInitData.image),
2909 mDependentFrame(nullptr),
2910 mBackgroundRect(aInitData.backgroundRect),
2911 mFillRect(aInitData.fillArea),
2912 mDestRect(aInitData.destArea),
2913 mLayer(aInitData.layer),
2914 mIsRasterImage(aInitData.isRasterImage),
2915 mShouldFixToViewport(aInitData.shouldFixToViewport) {
2916 MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
2917 #ifdef DEBUG
2918 if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) {
2919 // If this changes, then you also need to adjust css::ImageLoader to
2920 // invalidate mFrame as needed.
2921 MOZ_ASSERT(mFrame->IsCanvasFrame() ||
2922 mFrame->IsFrameOfType(nsIFrame::eTablePart));
2924 #endif
2926 mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
2927 if (mShouldFixToViewport) {
2928 // Expand the item's visible rect to cover the entire bounds, limited to the
2929 // viewport rect. This is necessary because the background's clip can move
2930 // asynchronously.
2931 if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
2932 aInitData.builder, mFrame)) {
2933 SetBuildingRect(mBounds.Intersect(*viewportRect));
2938 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2939 MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
2940 if (mDependentFrame) {
2941 mDependentFrame->RemoveDisplayItem(this);
2945 static nsIFrame* GetBackgroundComputedStyleFrame(nsIFrame* aFrame) {
2946 nsIFrame* f;
2947 if (!nsCSSRendering::FindBackgroundFrame(aFrame, &f)) {
2948 // We don't want to bail out if moz-appearance is set on a root
2949 // node. If it has a parent content node, bail because it's not
2950 // a root, other wise keep going in order to let the theme stuff
2951 // draw the background. The canvas really should be drawing the
2952 // bg, but there's no way to hook that up via css.
2953 if (!aFrame->StyleDisplay()->HasAppearance()) {
2954 return nullptr;
2957 nsIContent* content = aFrame->GetContent();
2958 if (!content || content->GetParent()) {
2959 return nullptr;
2962 f = aFrame;
2964 return f;
2967 static void SetBackgroundClipRegion(
2968 DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame,
2969 const nsStyleImageLayers::Layer& aLayer, const nsRect& aBackgroundRect,
2970 bool aWillPaintBorder) {
2971 nsCSSRendering::ImageLayerClipState clip;
2972 nsCSSRendering::GetImageLayerClip(
2973 aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect,
2974 aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
2976 if (clip.mHasAdditionalBGClipArea) {
2977 aClipState.ClipContentDescendants(
2978 clip.mAdditionalBGClipArea, clip.mBGClipArea,
2979 clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2980 } else {
2981 aClipState.ClipContentDescendants(
2982 clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2987 * This is used for the find bar highlighter overlay. It's only accessible
2988 * through the AnonymousContent API, so it's not exposed to general web pages.
2990 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
2991 nsIFrame* aFrame,
2992 const nsRect& aBackgroundRect,
2993 nsDisplayList* aList, nscolor aColor) {
2994 nsIContent* content = aFrame->GetContent();
2995 if (!content) {
2996 return false;
2999 void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
3000 if (!cutoutRegion) {
3001 return false;
3004 if (NS_GET_A(aColor) == 0) {
3005 return true;
3008 nsRegion region;
3009 region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
3010 region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
3011 aList->AppendNewToTop<nsDisplaySolidColorRegion>(aBuilder, aFrame, region,
3012 aColor);
3014 return true;
3017 enum class TableType : uint8_t {
3018 Table,
3019 TableCol,
3020 TableColGroup,
3021 TableRow,
3022 TableRowGroup,
3023 TableCell,
3025 MAX,
3028 enum class TableTypeBits : uint8_t { Count = 3 };
3030 static_assert(static_cast<uint8_t>(TableType::MAX) <
3031 (1 << (static_cast<uint8_t>(TableTypeBits::Count) + 1)),
3032 "TableType cannot fit with TableTypeBits::Count");
3033 TableType GetTableTypeFromFrame(nsIFrame* aFrame);
3035 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex,
3036 const TableType aType) {
3037 const uint32_t key = (aIndex << static_cast<uint8_t>(TableTypeBits::Count)) |
3038 static_cast<uint8_t>(aType);
3040 return static_cast<uint16_t>(key);
3043 static nsDisplayBackgroundImage* CreateBackgroundImage(
3044 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3045 const nsDisplayBackgroundImage::InitData& aBgData) {
3046 const auto index = aBgData.layer;
3048 if (aSecondaryFrame) {
3049 const auto tableType = GetTableTypeFromFrame(aFrame);
3050 const uint16_t tableItemIndex = CalculateTablePerFrameKey(index, tableType);
3052 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundImage>(
3053 aBuilder, aSecondaryFrame, tableItemIndex, aBgData, aFrame);
3056 return MakeDisplayItemWithIndex<nsDisplayBackgroundImage>(aBuilder, aFrame,
3057 index, aBgData);
3060 static nsDisplayThemedBackground* CreateThemedBackground(
3061 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3062 nsRect& aBgRect) {
3063 if (aSecondaryFrame) {
3064 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3065 return MakeDisplayItemWithIndex<nsDisplayTableThemedBackground>(
3066 aBuilder, aSecondaryFrame, index, aBgRect, aFrame);
3069 return MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, aBgRect);
3072 static nsDisplayBackgroundColor* CreateBackgroundColor(
3073 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3074 nsRect& aBgRect, ComputedStyle* aBgSC, nscolor aColor) {
3075 if (aSecondaryFrame) {
3076 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3077 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>(
3078 aBuilder, aSecondaryFrame, index, aBgRect, aBgSC, aColor, aFrame);
3081 return MakeDisplayItem<nsDisplayBackgroundColor>(aBuilder, aFrame, aBgRect,
3082 aBgSC, aColor);
3085 /*static*/
3086 AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3087 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3088 const nsRect& aBackgroundRect, nsDisplayList* aList,
3089 bool aAllowWillPaintBorderOptimization, ComputedStyle* aComputedStyle,
3090 const nsRect& aBackgroundOriginRect, nsIFrame* aSecondaryReferenceFrame,
3091 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
3092 aAutoBuildingDisplayList) {
3093 ComputedStyle* bgSC = aComputedStyle;
3094 const nsStyleBackground* bg = nullptr;
3095 nsRect bgRect = aBackgroundRect;
3096 nsRect bgOriginRect = bgRect;
3097 if (!aBackgroundOriginRect.IsEmpty()) {
3098 bgOriginRect = aBackgroundOriginRect;
3100 nsPresContext* presContext = aFrame->PresContext();
3101 bool isThemed = aFrame->IsThemed();
3102 nsIFrame* dependentFrame = nullptr;
3103 if (!isThemed) {
3104 if (!bgSC) {
3105 dependentFrame = GetBackgroundComputedStyleFrame(aFrame);
3106 if (dependentFrame) {
3107 bgSC = dependentFrame->Style();
3108 if (dependentFrame == aFrame) {
3109 dependentFrame = nullptr;
3113 if (bgSC) {
3114 bg = bgSC->StyleBackground();
3118 bool drawBackgroundColor = false;
3119 // XUL root frames need special handling for now even though they return true
3120 // from nsCSSRendering::IsCanvasFrame they rely on us painting the background
3121 // image from here, see bug 1665476.
3122 bool drawBackgroundImage =
3123 aFrame->IsXULRootFrame() && aFrame->ComputeShouldPaintBackground().mImage;
3124 nscolor color = NS_RGBA(0, 0, 0, 0);
3125 if (!nsCSSRendering::IsCanvasFrame(aFrame) && bg) {
3126 color = nsCSSRendering::DetermineBackgroundColor(
3127 presContext, bgSC, aFrame, drawBackgroundImage, drawBackgroundColor);
3130 if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
3131 color)) {
3132 return AppendedBackgroundType::None;
3135 const nsStyleBorder* borderStyle = aFrame->StyleBorder();
3136 const nsStyleEffects* effectsStyle = aFrame->StyleEffects();
3137 bool hasInsetShadow = effectsStyle->HasBoxShadowWithInset(true);
3138 bool willPaintBorder = aAllowWillPaintBorderOptimization && !isThemed &&
3139 !hasInsetShadow && borderStyle->HasBorder();
3141 // An auxiliary list is necessary in case we have background blending; if that
3142 // is the case, background items need to be wrapped by a blend container to
3143 // isolate blending to the background
3144 nsDisplayList bgItemList(aBuilder);
3145 // Even if we don't actually have a background color to paint, we may still
3146 // need to create an item for hit testing and we still need to create an item
3147 // for background-color animations.
3148 if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) ||
3149 aBuilder->IsForEventDelivery() ||
3150 EffectCompositor::HasAnimationsForCompositor(
3151 aFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3152 if (aAutoBuildingDisplayList && !*aAutoBuildingDisplayList) {
3153 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3154 aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3155 aBuilder->GetVisibleRect() + offset,
3156 aBuilder->GetDirtyRect() + offset);
3158 Maybe<DisplayListClipState::AutoSaveRestore> clipState;
3159 nsRect bgColorRect = bgRect;
3160 if (bg && !aBuilder->IsForEventDelivery()) {
3161 // Disable the will-paint-border optimization for background
3162 // colors with no border-radius. Enabling it for background colors
3163 // doesn't help much (there are no tiling issues) and clipping the
3164 // background breaks detection of the element's border-box being
3165 // opaque. For nonzero border-radius we still need it because we
3166 // want to inset the background if possible to avoid antialiasing
3167 // artifacts along the rounded corners.
3168 bool useWillPaintBorderOptimization =
3169 willPaintBorder &&
3170 nsLayoutUtils::HasNonZeroCorner(borderStyle->mBorderRadius);
3172 nsCSSRendering::ImageLayerClipState clip;
3173 nsCSSRendering::GetImageLayerClip(
3174 bg->BottomLayer(), aFrame, *aFrame->StyleBorder(), bgRect, bgRect,
3175 useWillPaintBorderOptimization,
3176 aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3178 bgColorRect = bgColorRect.Intersect(clip.mBGClipArea);
3179 if (clip.mHasAdditionalBGClipArea) {
3180 bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea);
3182 if (clip.mHasRoundedCorners) {
3183 clipState.emplace(aBuilder);
3184 clipState->ClipContentDescendants(clip.mBGClipArea, clip.mRadii);
3188 nsDisplayBackgroundColor* bgItem = CreateBackgroundColor(
3189 aBuilder, aFrame, aSecondaryReferenceFrame, bgColorRect, bgSC,
3190 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0));
3192 if (bgItem) {
3193 bgItem->SetDependentFrame(aBuilder, dependentFrame);
3194 bgItemList.AppendToTop(bgItem);
3198 if (isThemed) {
3199 nsDisplayThemedBackground* bgItem = CreateThemedBackground(
3200 aBuilder, aFrame, aSecondaryReferenceFrame, bgRect);
3202 if (bgItem) {
3203 bgItem->Init(aBuilder);
3204 bgItemList.AppendToTop(bgItem);
3207 if (!bgItemList.IsEmpty()) {
3208 aList->AppendToTop(&bgItemList);
3209 return AppendedBackgroundType::ThemedBackground;
3212 return AppendedBackgroundType::None;
3215 if (!bg || !drawBackgroundImage) {
3216 if (!bgItemList.IsEmpty()) {
3217 aList->AppendToTop(&bgItemList);
3218 return AppendedBackgroundType::Background;
3221 return AppendedBackgroundType::None;
3224 const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
3226 bool needBlendContainer = false;
3228 // Passing bg == nullptr in this macro will result in one iteration with
3229 // i = 0.
3230 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
3231 if (bg->mImage.mLayers[i].mImage.IsNone()) {
3232 continue;
3235 if (aAutoBuildingDisplayList && !*aAutoBuildingDisplayList) {
3236 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3237 aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3238 aBuilder->GetVisibleRect() + offset,
3239 aBuilder->GetDirtyRect() + offset);
3242 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3243 needBlendContainer = true;
3246 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3247 if (!aBuilder->IsForEventDelivery()) {
3248 const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
3249 SetBackgroundClipRegion(clipState, aFrame, layer, bgRect,
3250 willPaintBorder);
3253 nsDisplayList thisItemList(aBuilder);
3254 nsDisplayBackgroundImage::InitData bgData =
3255 nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect,
3256 bgSC);
3258 if (bgData.shouldFixToViewport) {
3259 auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
3260 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3261 aBuilder, aFrame, aBuilder->GetVisibleRect(),
3262 aBuilder->GetDirtyRect());
3264 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
3265 aBuilder);
3266 if (displayData) {
3267 asrSetter.SetCurrentActiveScrolledRoot(
3268 displayData->mContainingBlockActiveScrolledRoot);
3269 if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
3270 // Override the dirty rect on the builder to be the dirty rect of
3271 // the viewport.
3272 // displayData->mDirtyRect is relative to the presshell's viewport
3273 // frame (the root frame), and we need it to be relative to aFrame.
3274 nsIFrame* rootFrame =
3275 aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
3276 // There cannot be any transforms between aFrame and rootFrame
3277 // because then bgData.shouldFixToViewport would have been false.
3278 nsRect visibleRect =
3279 displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame);
3280 aBuilder->SetVisibleRect(visibleRect);
3281 nsRect dirtyRect =
3282 displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
3283 aBuilder->SetDirtyRect(dirtyRect);
3287 nsDisplayBackgroundImage* bgItem = nullptr;
3289 // The clip is captured by the nsDisplayFixedPosition, so clear the
3290 // clip for the nsDisplayBackgroundImage inside.
3291 DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
3292 bgImageClip.Clear();
3293 bgItem = CreateBackgroundImage(aBuilder, aFrame,
3294 aSecondaryReferenceFrame, bgData);
3296 if (bgItem) {
3297 bgItem->SetDependentFrame(aBuilder, dependentFrame);
3299 thisItemList.AppendToTop(
3300 nsDisplayFixedPosition::CreateForFixedBackground(
3301 aBuilder, aFrame, aSecondaryReferenceFrame, bgItem, i, asr));
3303 } else { // bgData.shouldFixToViewport == false
3304 nsDisplayBackgroundImage* bgItem = CreateBackgroundImage(
3305 aBuilder, aFrame, aSecondaryReferenceFrame, bgData);
3306 if (bgItem) {
3307 bgItem->SetDependentFrame(aBuilder, dependentFrame);
3308 thisItemList.AppendToTop(bgItem);
3312 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3313 DisplayListClipState::AutoSaveRestore blendClip(aBuilder);
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 DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder);
3336 bgItemList.AppendToTop(
3337 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3338 aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr));
3341 if (!bgItemList.IsEmpty()) {
3342 aList->AppendToTop(&bgItemList);
3343 return AppendedBackgroundType::Background;
3346 return AppendedBackgroundType::None;
3349 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3350 // intersects aRect. Assumes that the unrounded border has already
3351 // been checked for intersection.
3352 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame,
3353 const nsPoint& aFrameToReferenceFrame,
3354 const nsRect& aTestRect) {
3355 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize())
3356 .Intersects(aTestRect)) {
3357 return false;
3360 nscoord radii[8];
3361 return !aFrame->GetBorderRadii(radii) ||
3362 nsLayoutUtils::RoundedRectIntersectsRect(
3363 nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii,
3364 aTestRect);
3367 // Returns TRUE if aContainedRect is guaranteed to be contained in
3368 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3369 // handled conservatively by returning FALSE in some situations where
3370 // a more thorough analysis could return TRUE.
3372 // See also RoundedRectIntersectsRect.
3373 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
3374 const nscoord aRadii[8],
3375 const nsRect& aContainedRect) {
3376 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii,
3377 aContainedRect);
3378 return rgn.Contains(aContainedRect);
3381 bool nsDisplayBackgroundImage::CanApplyOpacity(
3382 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3383 return CanBuildWebRenderDisplayItems(aManager, aBuilder);
3386 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3387 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3388 return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip !=
3389 StyleGeometryBox::Text &&
3390 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3391 aManager, *StyleFrame()->PresContext(), StyleFrame(),
3392 mBackgroundStyle->StyleBackground(), mLayer,
3393 aBuilder->GetBackgroundPaintFlags());
3396 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3397 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3398 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3399 nsDisplayListBuilder* aDisplayListBuilder) {
3400 if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(),
3401 aDisplayListBuilder)) {
3402 return false;
3405 uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
3406 bool dummy;
3407 nsCSSRendering::PaintBGParams params =
3408 nsCSSRendering::PaintBGParams::ForSingleLayer(
3409 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy),
3410 mBackgroundRect, StyleFrame(), paintFlags, mLayer,
3411 CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity());
3412 params.bgClipRect = &mBounds;
3413 ImgDrawResult result =
3414 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3415 params, aBuilder, aResources, aSc, aManager, this);
3416 if (result == ImgDrawResult::NOT_SUPPORTED) {
3417 return false;
3420 nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
3421 return true;
3424 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
3425 const nsRect& aRect,
3426 HitTestState* aState,
3427 nsTArray<nsIFrame*>* aOutFrames) {
3428 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3429 aOutFrames->AppendElement(mFrame);
3433 static nsRect GetInsideClipRect(const nsDisplayItem* aItem,
3434 StyleGeometryBox aClip, const nsRect& aRect,
3435 const nsRect& aBackgroundRect) {
3436 if (aRect.IsEmpty()) {
3437 return {};
3440 nsIFrame* frame = aItem->Frame();
3442 nsRect clipRect = aBackgroundRect;
3443 if (frame->IsCanvasFrame()) {
3444 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3445 clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
3446 } else if (aClip == StyleGeometryBox::PaddingBox ||
3447 aClip == StyleGeometryBox::ContentBox) {
3448 nsMargin border = frame->GetUsedBorder();
3449 if (aClip == StyleGeometryBox::ContentBox) {
3450 border += frame->GetUsedPadding();
3452 border.ApplySkipSides(frame->GetSkipSides());
3453 clipRect.Deflate(border);
3456 return clipRect.Intersect(aRect);
3459 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
3460 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3461 nsRegion result;
3462 *aSnap = false;
3464 if (!mBackgroundStyle) {
3465 return result;
3468 *aSnap = true;
3470 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3471 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3472 // which expects frames to be sent to it in content order, not reverse
3473 // content order which we'll produce here.
3474 // Of course, if there's only one frame in the flow, it doesn't matter.
3475 if (mFrame->StyleBorder()->mBoxDecorationBreak ==
3476 StyleBoxDecorationBreak::Clone ||
3477 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
3478 const nsStyleImageLayers::Layer& layer =
3479 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3480 if (layer.mImage.IsOpaque() && layer.mBlendMode == StyleBlend::Normal &&
3481 layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
3482 layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
3483 layer.mClip != StyleGeometryBox::Text) {
3484 result = GetInsideClipRect(this, layer.mClip, mBounds, mBackgroundRect);
3488 return result;
3491 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform(
3492 nsDisplayListBuilder* aBuilder) const {
3493 if (!mBackgroundStyle) {
3494 return Some(NS_RGBA(0, 0, 0, 0));
3496 return Nothing();
3499 nsRect nsDisplayBackgroundImage::GetPositioningArea() const {
3500 if (!mBackgroundStyle) {
3501 return nsRect();
3503 nsIFrame* attachedToFrame;
3504 bool transformedFixed;
3505 return nsCSSRendering::ComputeImageLayerPositioningArea(
3506 mFrame->PresContext(), mFrame, mBackgroundRect,
3507 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer],
3508 &attachedToFrame, &transformedFixed) +
3509 ToReferenceFrame();
3512 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3513 const {
3514 if (!mBackgroundStyle) {
3515 return false;
3518 nscoord radii[8];
3519 if (mFrame->GetBorderRadii(radii)) {
3520 // A change in the size of the positioning area might change the position
3521 // of the rounded corners.
3522 return true;
3525 const nsStyleImageLayers::Layer& layer =
3526 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3527 return layer.RenderingMightDependOnPositioningAreaSizeChange();
3530 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
3531 gfxContext* aCtx) {
3532 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &mBounds);
3535 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
3536 gfxContext* aCtx,
3537 const nsRect& aBounds,
3538 nsRect* aClipRect) {
3539 gfxContext* ctx = aCtx;
3540 StyleGeometryBox clip =
3541 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip;
3543 if (clip == StyleGeometryBox::Text) {
3544 if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect,
3545 aBuilder)) {
3546 return;
3550 nsCSSRendering::PaintBGParams params =
3551 nsCSSRendering::PaintBGParams::ForSingleLayer(
3552 *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
3553 aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER,
3554 1.0f);
3555 params.bgClipRect = aClipRect;
3556 ImgDrawResult result = nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
3558 if (clip == StyleGeometryBox::Text) {
3559 ctx->PopGroupAndBlend();
3562 nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
3565 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3566 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3567 nsRegion* aInvalidRegion) const {
3568 if (!mBackgroundStyle) {
3569 return;
3572 const auto* geometry =
3573 static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
3575 bool snap;
3576 nsRect bounds = GetBounds(aBuilder, &snap);
3577 nsRect positioningArea = GetPositioningArea();
3578 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
3579 (positioningArea.Size() != geometry->mPositioningArea.Size() &&
3580 RenderingMightDependOnPositioningAreaSizeChange())) {
3581 // Positioning area changed in a way that could cause everything to change,
3582 // so invalidate everything (both old and new painting areas).
3583 aInvalidRegion->Or(bounds, geometry->mBounds);
3584 return;
3586 if (!mDestRect.IsEqualInterior(geometry->mDestRect)) {
3587 // Dest area changed in a way that could cause everything to change,
3588 // so invalidate everything (both old and new painting areas).
3589 aInvalidRegion->Or(bounds, geometry->mBounds);
3590 return;
3592 if (aBuilder->ShouldSyncDecodeImages()) {
3593 const auto& image =
3594 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mImage;
3595 if (image.IsImageRequestType() &&
3596 geometry->ShouldInvalidateToSyncDecodeImages()) {
3597 aInvalidRegion->Or(*aInvalidRegion, bounds);
3600 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3601 // Positioning area is unchanged, so invalidate just the change in the
3602 // painting area.
3603 aInvalidRegion->Xor(bounds, geometry->mBounds);
3607 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder,
3608 bool* aSnap) const {
3609 *aSnap = true;
3610 return mBounds;
3613 nsRect nsDisplayBackgroundImage::GetBoundsInternal(
3614 nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) {
3615 // This allows nsDisplayTableBackgroundImage to change the frame used for
3616 // bounds calculation.
3617 nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame;
3619 nsPresContext* presContext = frame->PresContext();
3621 if (!mBackgroundStyle) {
3622 return nsRect();
3625 nsRect clipRect = mBackgroundRect;
3626 if (frame->IsCanvasFrame()) {
3627 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3628 clipRect = canvasFrame->CanvasArea() + ToReferenceFrame();
3630 const nsStyleImageLayers::Layer& layer =
3631 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3632 return nsCSSRendering::GetBackgroundLayerRect(
3633 presContext, frame, mBackgroundRect, clipRect, layer,
3634 aBuilder->GetBackgroundPaintFlags());
3637 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3638 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aData,
3639 nsIFrame* aCellFrame)
3640 : nsDisplayBackgroundImage(aBuilder, aFrame, aData, aCellFrame),
3641 mStyleFrame(aCellFrame) {
3642 if (aBuilder->IsRetainingDisplayList()) {
3643 mStyleFrame->AddDisplayItem(this);
3647 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3648 if (mStyleFrame) {
3649 mStyleFrame->RemoveDisplayItem(this);
3653 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const {
3654 bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
3655 aRect += ToReferenceFrame();
3656 return result;
3659 nsDisplayThemedBackground::nsDisplayThemedBackground(
3660 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3661 const nsRect& aBackgroundRect)
3662 : nsPaintedDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) {
3663 MOZ_COUNT_CTOR(nsDisplayThemedBackground);
3666 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) {
3667 const nsStyleDisplay* disp = StyleFrame()->StyleDisplay();
3668 mAppearance = disp->EffectiveAppearance();
3669 StyleFrame()->IsThemed(disp, &mThemeTransparency);
3671 // Perform necessary RegisterThemeGeometry
3672 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3673 nsITheme::ThemeGeometryType type =
3674 theme->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance);
3675 if (type != nsITheme::eThemeGeometryTypeUnknown) {
3676 RegisterThemeGeometry(aBuilder, this, StyleFrame(), type);
3679 if (mAppearance == StyleAppearance::MozWinBorderlessGlass ||
3680 mAppearance == StyleAppearance::MozWinGlass) {
3681 aBuilder->SetGlassDisplayItem(this);
3684 mBounds = GetBoundsInternal();
3687 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) {
3688 aStream << " (themed, appearance:" << (int)mAppearance << ")";
3691 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
3692 const nsRect& aRect,
3693 HitTestState* aState,
3694 nsTArray<nsIFrame*>* aOutFrames) {
3695 // Assume that any point in our background rect is a hit.
3696 if (mBackgroundRect.Intersects(aRect)) {
3697 aOutFrames->AppendElement(mFrame);
3701 nsRegion nsDisplayThemedBackground::GetOpaqueRegion(
3702 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3703 nsRegion result;
3704 *aSnap = false;
3706 if (mThemeTransparency == nsITheme::eOpaque) {
3707 *aSnap = true;
3708 result = mBackgroundRect;
3710 return result;
3713 Maybe<nscolor> nsDisplayThemedBackground::IsUniform(
3714 nsDisplayListBuilder* aBuilder) const {
3715 if (mAppearance == StyleAppearance::MozWinBorderlessGlass ||
3716 mAppearance == StyleAppearance::MozWinGlass) {
3717 return Some(NS_RGBA(0, 0, 0, 0));
3719 return Nothing();
3722 nsRect nsDisplayThemedBackground::GetPositioningArea() const {
3723 return mBackgroundRect;
3726 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
3727 gfxContext* aCtx) {
3728 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), nullptr);
3731 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
3732 gfxContext* aCtx,
3733 const nsRect& aBounds,
3734 nsRect* aClipRect) {
3735 // XXXzw this ignores aClipRect.
3736 nsPresContext* presContext = StyleFrame()->PresContext();
3737 nsITheme* theme = presContext->Theme();
3738 nsRect drawing(mBackgroundRect);
3739 theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(),
3740 mAppearance, &drawing);
3741 drawing.IntersectRect(drawing, aBounds);
3742 theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect,
3743 drawing);
3746 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3747 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3748 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3749 nsDisplayListBuilder* aDisplayListBuilder) {
3750 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3751 return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc,
3752 aManager, StyleFrame(),
3753 mAppearance, mBackgroundRect);
3756 bool nsDisplayThemedBackground::IsWindowActive() const {
3757 EventStates docState = mFrame->GetContent()->OwnerDoc()->GetDocumentState();
3758 return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
3761 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3762 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3763 nsRegion* aInvalidRegion) const {
3764 const auto* geometry =
3765 static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
3767 bool snap;
3768 nsRect bounds = GetBounds(aBuilder, &snap);
3769 nsRect positioningArea = GetPositioningArea();
3770 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
3771 // Invalidate everything (both old and new painting areas).
3772 aInvalidRegion->Or(bounds, geometry->mBounds);
3773 return;
3775 if (!bounds.IsEqualInterior(geometry->mBounds)) {
3776 // Positioning area is unchanged, so invalidate just the change in the
3777 // painting area.
3778 aInvalidRegion->Xor(bounds, geometry->mBounds);
3780 nsITheme* theme = StyleFrame()->PresContext()->Theme();
3781 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
3782 IsWindowActive() != geometry->mWindowIsActive) {
3783 aInvalidRegion->Or(*aInvalidRegion, bounds);
3787 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder,
3788 bool* aSnap) const {
3789 *aSnap = true;
3790 return mBounds;
3793 nsRect nsDisplayThemedBackground::GetBoundsInternal() {
3794 nsPresContext* presContext = mFrame->PresContext();
3796 nsRect r = mBackgroundRect - ToReferenceFrame();
3797 presContext->Theme()->GetWidgetOverflow(
3798 presContext->DeviceContext(), mFrame,
3799 mFrame->StyleDisplay()->EffectiveAppearance(), &r);
3800 return r + ToReferenceFrame();
3803 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
3804 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder,
3805 gfxContext* aCtx) {
3806 mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(),
3807 mFrame, ToReferenceFrame(), mColor);
3809 #endif
3811 bool nsDisplayBackgroundColor::CanApplyOpacity(
3812 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3813 // Don't apply opacity if the background color is animated since the color is
3814 // going to be changed on the compositor.
3815 return !EffectCompositor::HasAnimationsForCompositor(
3816 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR);
3819 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3820 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3821 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3822 nsDisplayListBuilder* aDisplayListBuilder) {
3823 gfx::sRGBColor color = mColor;
3824 color.a *= aBuilder.GetInheritedOpacity();
3826 if (color == sRGBColor() &&
3827 !EffectCompositor::HasAnimationsForCompositor(
3828 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3829 return true;
3832 if (HasBackgroundClipText()) {
3833 return false;
3836 uint64_t animationsId = 0;
3837 // We don't support background-color animations on table elements yet.
3838 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
3839 animationsId =
3840 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
3843 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
3844 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3845 wr::LayoutRect r = wr::ToLayoutRect(bounds);
3847 if (animationsId) {
3848 wr::WrAnimationProperty prop{
3849 wr::WrAnimationType::BackgroundColor,
3850 animationsId,
3852 aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(),
3853 wr::ToColorF(ToDeviceColor(color)), &prop);
3854 } else {
3855 aBuilder.StartGroup(this);
3856 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false,
3857 wr::ToColorF(ToDeviceColor(color)));
3858 aBuilder.FinishGroup();
3861 return true;
3864 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder,
3865 gfxContext* aCtx,
3866 const DisplayItemClip& aClip) {
3867 MOZ_ASSERT(!HasBackgroundClipText());
3869 if (mColor == sRGBColor()) {
3870 return;
3873 nsRect fillRect = mBackgroundRect;
3874 if (aClip.HasClip()) {
3875 fillRect.IntersectRect(fillRect, aClip.GetClipRect());
3878 DrawTarget* dt = aCtx->GetDrawTarget();
3879 int32_t A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
3880 Rect bounds = ToRect(nsLayoutUtils::RectToGfxRect(fillRect, A2D));
3881 MaybeSnapToDevicePixels(bounds, *dt);
3882 ColorPattern fill(ToDeviceColor(mColor));
3884 if (aClip.GetRoundedRectCount()) {
3885 MOZ_ASSERT(aClip.GetRoundedRectCount() == 1);
3887 AutoTArray<DisplayItemClip::RoundedRect, 1> roundedRect;
3888 aClip.AppendRoundedRects(&roundedRect);
3890 bool pushedClip = false;
3891 if (!fillRect.Contains(roundedRect[0].mRect)) {
3892 dt->PushClipRect(bounds);
3893 pushedClip = true;
3896 RectCornerRadii pixelRadii;
3897 nsCSSRendering::ComputePixelRadii(roundedRect[0].mRadii, A2D, &pixelRadii);
3898 dt->FillRoundedRect(
3899 RoundedRect(NSRectToSnappedRect(roundedRect[0].mRect, A2D, *dt),
3900 pixelRadii),
3901 fill);
3902 if (pushedClip) {
3903 dt->PopClip();
3905 } else {
3906 dt->FillRect(bounds, fill);
3910 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
3911 gfxContext* aCtx) {
3912 if (mColor == sRGBColor()) {
3913 return;
3916 #if 0
3917 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3918 // results in a precision induced rounding issue that makes the rect one
3919 // pixel shorter in rare cases. Disabled in favor of the old code for now.
3920 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3921 // reproduce the bug.
3923 // TODO:
3924 // This new path does not include support for background-clip:text; need to
3925 // be fixed if/when we switch to this new code path.
3927 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
3929 Rect rect = NSRectToSnappedRect(mBackgroundRect,
3930 mFrame->PresContext()->AppUnitsPerDevPixel(),
3931 aDrawTarget);
3932 ColorPattern color(ToDeviceColor(mColor));
3933 aDrawTarget.FillRect(rect, color);
3934 #else
3935 gfxContext* ctx = aCtx;
3936 gfxRect bounds = nsLayoutUtils::RectToGfxRect(
3937 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3939 if (HasBackgroundClipText()) {
3940 if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
3941 return;
3944 ctx->SetColor(mColor);
3945 ctx->NewPath();
3946 ctx->SnappedRectangle(bounds);
3947 ctx->Fill();
3948 ctx->PopGroupAndBlend();
3949 return;
3952 ctx->SetColor(mColor);
3953 ctx->NewPath();
3954 ctx->SnappedRectangle(bounds);
3955 ctx->Fill();
3956 #endif
3959 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion(
3960 nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3961 *aSnap = false;
3963 if (mColor.a != 1 ||
3964 // Even if the current alpha channel is 1, we treat this item as if it's
3965 // non-opaque if there is a background-color animation since the animation
3966 // might change the alpha channel.
3967 EffectCompositor::HasAnimationsForCompositor(
3968 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3969 return nsRegion();
3972 if (!mHasStyle || HasBackgroundClipText()) {
3973 return nsRegion();
3976 *aSnap = true;
3977 return GetInsideClipRect(this, mBottomLayerClip, mBackgroundRect,
3978 mBackgroundRect);
3981 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform(
3982 nsDisplayListBuilder* aBuilder) const {
3983 return Some(mColor.ToABGR());
3986 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
3987 const nsRect& aRect,
3988 HitTestState* aState,
3989 nsTArray<nsIFrame*>* aOutFrames) {
3990 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3991 // aRect doesn't intersect our border-radius curve.
3992 return;
3995 aOutFrames->AppendElement(mFrame);
3998 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) {
3999 aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << ","
4000 << mColor.a << ")";
4001 aStream << " backgroundRect" << mBackgroundRect;
4004 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
4005 bool* aSnap) const {
4006 *aSnap = false;
4007 return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
4010 nsRect nsDisplayOutline::GetInnerRect() const {
4011 if (nsRect* savedOutlineInnerRect =
4012 mFrame->GetProperty(nsIFrame::OutlineInnerRectProperty())) {
4013 return *savedOutlineInnerRect;
4015 return mFrame->GetRectRelativeToSelf();
4018 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4019 // TODO join outlines together
4020 MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(),
4021 "Should have not created a nsDisplayOutline!");
4023 nsRect rect = GetInnerRect() + ToReferenceFrame();
4024 nsPresContext* pc = mFrame->PresContext();
4025 if (IsThemedOutline()) {
4026 rect.Inflate(mFrame->StyleOutline()->mOutlineOffset.ToAppUnits());
4027 pc->Theme()->DrawWidgetBackground(aCtx, mFrame,
4028 StyleAppearance::FocusOutline, rect,
4029 GetPaintRect(aBuilder, aCtx));
4030 return;
4033 nsCSSRendering::PaintNonThemedOutline(
4034 pc, *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), rect, mFrame->Style());
4037 bool nsDisplayOutline::IsThemedOutline() const {
4038 const auto& outlineStyle = mFrame->StyleOutline()->mOutlineStyle;
4039 if (!outlineStyle.IsAuto() ||
4040 !StaticPrefs::layout_css_outline_style_auto_enabled()) {
4041 return false;
4044 nsPresContext* pc = mFrame->PresContext();
4045 nsITheme* theme = pc->Theme();
4046 return theme->ThemeSupportsWidget(pc, mFrame, StyleAppearance::FocusOutline);
4049 bool nsDisplayOutline::CreateWebRenderCommands(
4050 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4051 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4052 nsDisplayListBuilder* aDisplayListBuilder) {
4053 nsPresContext* pc = mFrame->PresContext();
4054 nsRect rect = GetInnerRect() + ToReferenceFrame();
4055 if (IsThemedOutline()) {
4056 rect.Inflate(mFrame->StyleOutline()->mOutlineOffset.ToAppUnits());
4057 return pc->Theme()->CreateWebRenderCommandsForWidget(
4058 aBuilder, aResources, aSc, aManager, mFrame,
4059 StyleAppearance::FocusOutline, rect);
4062 bool dummy;
4063 Maybe<nsCSSBorderRenderer> borderRenderer =
4064 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4065 pc, /* aDrawTarget = */ nullptr, mFrame,
4066 GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style());
4068 if (!borderRenderer) {
4069 // No border renderer means "there is no outline".
4070 // Paint nothing and return success.
4071 return true;
4074 borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
4075 return true;
4078 bool nsDisplayOutline::HasRadius() const {
4079 const auto& radius = mFrame->StyleBorder()->mBorderRadius;
4080 return !nsLayoutUtils::HasNonZeroCorner(radius);
4083 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const {
4084 const nsStyleOutline* outline = mFrame->StyleOutline();
4085 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
4086 if (borderBox.Contains(aRect) && !HasRadius() &&
4087 outline->mOutlineOffset.ToCSSPixels() >= 0.0f) {
4088 // aRect is entirely inside the border-rect, and the outline isn't rendered
4089 // inside the border-rect, so the outline is not visible.
4090 return true;
4092 return false;
4095 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
4096 const nsRect& aRect, HitTestState* aState,
4097 nsTArray<nsIFrame*>* aOutFrames) {
4098 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4099 // aRect doesn't intersect our border-radius curve.
4100 return;
4103 aOutFrames->AppendElement(mFrame);
4106 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4107 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4108 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4109 nsDisplayListBuilder* aDisplayListBuilder) {
4110 return true;
4113 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4114 return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
4117 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) {
4118 mOverrideZIndex = Some(aZIndex);
4121 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
4122 nsIFrame* aCaretFrame)
4123 : nsPaintedDisplayItem(aBuilder, aCaretFrame),
4124 mCaret(aBuilder->GetCaret()),
4125 mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) {
4126 MOZ_COUNT_CTOR(nsDisplayCaret);
4127 // The presence of a caret doesn't change the overflow rect
4128 // of the owning frame, so the normal building rect might not
4129 // include the caret at all. We use MarkFrameForDisplay to ensure
4130 // we build this item, and here we override the building rect
4131 // to cover the pixels we're going to draw.
4132 SetBuildingRect(mBounds);
4135 #ifdef NS_BUILD_REFCNT_LOGGING
4136 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret); }
4137 #endif
4139 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder,
4140 bool* aSnap) const {
4141 *aSnap = true;
4142 // The caret returns a rect in the coordinates of mFrame.
4143 return mBounds;
4146 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4147 // Note: Because we exist, we know that the caret is visible, so we don't
4148 // need to check for the caret's visibility.
4149 mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
4152 bool nsDisplayCaret::CreateWebRenderCommands(
4153 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4154 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4155 nsDisplayListBuilder* aDisplayListBuilder) {
4156 using namespace layers;
4157 nsRect caretRect;
4158 nsRect hookRect;
4159 nscolor caretColor;
4160 nsIFrame* frame =
4161 mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor);
4162 MOZ_ASSERT(frame == mFrame, "We're referring different frame");
4163 if (!frame) {
4164 return true;
4167 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
4168 gfx::DeviceColor color = ToDeviceColor(caretColor);
4169 LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
4170 caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
4171 LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
4172 hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
4174 wr::LayoutRect caret = wr::ToLayoutRect(devCaretRect);
4175 wr::LayoutRect hook = wr::ToLayoutRect(devHookRect);
4177 // Note, WR will pixel snap anything that is layout aligned.
4178 aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), false, false,
4179 wr::ToColorF(color));
4181 if (!devHookRect.IsEmpty()) {
4182 aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), false, false,
4183 wr::ToColorF(color));
4185 return true;
4188 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder,
4189 nsIFrame* aFrame)
4190 : nsPaintedDisplayItem(aBuilder, aFrame) {
4191 MOZ_COUNT_CTOR(nsDisplayBorder);
4193 mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
4196 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const {
4197 nsRect paddingRect = GetPaddingRect();
4198 const nsStyleBorder* styleBorder;
4199 if (paddingRect.Contains(aRect) &&
4200 !(styleBorder = mFrame->StyleBorder())->IsBorderImageSizeAvailable() &&
4201 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
4202 // aRect is entirely inside the content rect, and no part
4203 // of the border is rendered inside the content rect, so we are not
4204 // visible
4205 // Skip this if there's a border-image (which draws a background
4206 // too) or if there is a border-radius (which makes the border draw
4207 // further in).
4208 return true;
4211 return false;
4214 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry(
4215 nsDisplayListBuilder* aBuilder) {
4216 return new nsDisplayBorderGeometry(this, aBuilder);
4219 void nsDisplayBorder::ComputeInvalidationRegion(
4220 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4221 nsRegion* aInvalidRegion) const {
4222 const auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
4223 bool snap;
4225 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
4226 // We can probably get away with only invalidating the difference
4227 // between the border and padding rects, but the XUL ui at least
4228 // is apparently painting a background with this?
4229 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4232 if (aBuilder->ShouldSyncDecodeImages() &&
4233 geometry->ShouldInvalidateToSyncDecodeImages()) {
4234 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
4238 bool nsDisplayBorder::CreateWebRenderCommands(
4239 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4240 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4241 nsDisplayListBuilder* aDisplayListBuilder) {
4242 nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
4244 ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder(
4245 this, mFrame, rect, aBuilder, aResources, aSc, aManager,
4246 aDisplayListBuilder);
4248 if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
4249 return false;
4252 nsDisplayBorderGeometry::UpdateDrawResult(this, drawResult);
4253 return true;
4256 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4257 nsPoint offset = ToReferenceFrame();
4259 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
4260 ? PaintBorderFlags::SyncDecodeImages
4261 : PaintBorderFlags();
4263 ImgDrawResult result = nsCSSRendering::PaintBorder(
4264 mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
4265 nsRect(offset, mFrame->GetSize()), mFrame->Style(), flags,
4266 mFrame->GetSkipSides());
4268 nsDisplayBorderGeometry::UpdateDrawResult(this, result);
4271 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder,
4272 bool* aSnap) const {
4273 *aSnap = true;
4274 return mBounds;
4277 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
4278 gfxContext* aCtx) {
4279 nsPoint offset = ToReferenceFrame();
4280 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4281 nsPresContext* presContext = mFrame->PresContext();
4283 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
4285 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
4286 GetPaintRect(aBuilder, aCtx), 1.0f);
4289 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
4290 bool* aSnap) const {
4291 *aSnap = false;
4292 return mBounds;
4295 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() {
4296 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
4297 ToReferenceFrame();
4300 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const {
4301 nsPoint origin = ToReferenceFrame();
4302 nsRect frameRect(origin, mFrame->GetSize());
4303 if (!frameRect.Contains(aRect)) {
4304 return false;
4307 // the visible region is entirely inside the border-rect, and box shadows
4308 // never render within the border-rect (unless there's a border radius).
4309 nscoord twipsRadii[8];
4310 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
4311 if (!hasBorderRadii) {
4312 return true;
4315 return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
4318 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4319 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4320 if (shadows.IsEmpty()) {
4321 return false;
4324 bool hasBorderRadius;
4325 // We don't support native themed things yet like box shadows around
4326 // input buttons.
4328 // TODO(emilio): The non-native theme could provide the right rect+radius
4329 // instead relatively painlessly, if we find this causes performance issues or
4330 // what not.
4331 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4334 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4335 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4336 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4337 nsDisplayListBuilder* aDisplayListBuilder) {
4338 if (!CanBuildWebRenderDisplayItems()) {
4339 return false;
4342 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4343 nsPoint offset = ToReferenceFrame();
4344 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4345 bool snap;
4346 nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
4348 bool hasBorderRadius;
4349 bool nativeTheme =
4350 nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4352 // Don't need the full size of the shadow rect like we do in
4353 // nsCSSRendering since WR takes care of calculations for blur
4354 // and spread radius.
4355 nsRect frameRect =
4356 nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
4358 RectCornerRadii borderRadii;
4359 if (hasBorderRadius) {
4360 hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
4361 mFrame, borderRadii);
4364 // Everything here is in app units, change to device units.
4365 LayoutDeviceRect clipRect =
4366 LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel);
4367 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4368 MOZ_ASSERT(!shadows.IsEmpty());
4370 for (const auto& shadow : Reversed(shadows)) {
4371 if (shadow.inset) {
4372 continue;
4375 float blurRadius =
4376 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4377 gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor(
4378 shadow.base, mFrame, aBuilder.GetInheritedOpacity());
4380 // We don't move the shadow rect here since WR does it for us
4381 // Now translate everything to device pixels.
4382 const nsRect& shadowRect = frameRect;
4383 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4384 nsPoint(shadow.base.horizontal.ToAppUnits(),
4385 shadow.base.vertical.ToAppUnits()),
4386 appUnitsPerDevPixel);
4388 LayoutDeviceRect deviceBox =
4389 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4390 wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
4391 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4393 LayoutDeviceSize zeroSize;
4394 wr::BorderRadius borderRadius =
4395 wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
4396 if (hasBorderRadius) {
4397 borderRadius = wr::ToBorderRadius(
4398 LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
4399 LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
4400 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
4401 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
4404 float spreadRadius =
4405 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4407 aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
4408 deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
4409 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius,
4410 spreadRadius, borderRadius,
4411 wr::BoxShadowClipMode::Outset);
4414 return true;
4417 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4418 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4419 nsRegion* aInvalidRegion) const {
4420 const auto* geometry =
4421 static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
4422 bool snap;
4423 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
4424 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
4425 nsRegion oldShadow, newShadow;
4426 nscoord dontCare[8];
4427 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
4428 if (hasBorderRadius) {
4429 // If we have rounded corners then we need to invalidate the frame area
4430 // too since we paint into it.
4431 oldShadow = geometry->mBounds;
4432 newShadow = GetBounds(aBuilder, &snap);
4433 } else {
4434 oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
4435 newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
4437 aInvalidRegion->Or(oldShadow, newShadow);
4441 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
4442 gfxContext* aCtx) {
4443 nsPoint offset = ToReferenceFrame();
4444 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4445 nsPresContext* presContext = mFrame->PresContext();
4447 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS);
4449 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
4452 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4453 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4454 const nsPoint& aReferenceOffset) {
4455 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4456 if (shadows.IsEmpty()) {
4457 // Means we don't have to paint anything
4458 return true;
4461 bool hasBorderRadius;
4462 bool nativeTheme =
4463 nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
4465 // We don't support native themed things yet like box shadows around
4466 // input buttons.
4467 return !nativeTheme;
4470 /* static */
4471 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4472 wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
4473 nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect) {
4474 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) {
4475 return;
4478 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
4480 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4482 LayoutDeviceRect clipRect =
4483 LayoutDeviceRect::FromAppUnits(aVisibleRect, appUnitsPerDevPixel);
4485 for (const auto& shadow : Reversed(shadows)) {
4486 if (!shadow.inset) {
4487 continue;
4490 nsRect shadowRect =
4491 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
4492 RectCornerRadii innerRadii;
4493 nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
4495 // Now translate everything to device pixels.
4496 LayoutDeviceRect deviceBoxRect =
4497 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4498 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4499 sRGBColor shadowColor =
4500 nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0);
4502 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4503 nsPoint(shadow.base.horizontal.ToAppUnits(),
4504 shadow.base.vertical.ToAppUnits()),
4505 appUnitsPerDevPixel);
4507 float blurRadius =
4508 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4510 wr::BorderRadius borderRadius = wr::ToBorderRadius(
4511 LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
4512 LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
4513 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
4514 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
4515 // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4516 float spreadRadius =
4517 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4519 aBuilder.PushBoxShadow(
4520 wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
4521 !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
4522 wr::ToLayoutVector2D(shadowOffset),
4523 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, spreadRadius,
4524 borderRadius, wr::BoxShadowClipMode::Inset);
4528 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4529 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4530 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4531 nsDisplayListBuilder* aDisplayListBuilder) {
4532 if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame,
4533 ToReferenceFrame())) {
4534 return false;
4537 bool snap;
4538 nsRect visible = GetBounds(aDisplayListBuilder, &snap);
4539 nsPoint offset = ToReferenceFrame();
4540 nsRect borderRect = nsRect(offset, mFrame->GetSize());
4541 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4542 aBuilder, aSc, visible, mFrame, borderRect);
4544 return true;
4547 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4548 nsIFrame* aFrame, nsDisplayList* aList)
4549 : nsDisplayWrapList(aBuilder, aFrame, aList,
4550 aBuilder->CurrentActiveScrolledRoot(), false) {}
4552 nsDisplayWrapList::nsDisplayWrapList(
4553 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4554 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
4555 : nsPaintedDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
4556 mList(aBuilder),
4557 mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
4558 mOverrideZIndex(0),
4559 mHasZIndexOverride(false),
4560 mClearingClipChain(aClearClipChain) {
4561 MOZ_COUNT_CTOR(nsDisplayWrapList);
4563 mBaseBuildingRect = GetBuildingRect();
4565 mListPtr = &mList;
4566 mListPtr->AppendToTop(aList);
4567 mOriginalClipChain = mClipChain;
4568 nsDisplayWrapList::UpdateBounds(aBuilder);
4571 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4572 nsIFrame* aFrame, nsDisplayItem* aItem)
4573 : nsPaintedDisplayItem(aBuilder, aFrame,
4574 aBuilder->CurrentActiveScrolledRoot()),
4575 mList(aBuilder),
4576 mOverrideZIndex(0),
4577 mHasZIndexOverride(false) {
4578 MOZ_COUNT_CTOR(nsDisplayWrapList);
4580 mBaseBuildingRect = GetBuildingRect();
4582 mListPtr = &mList;
4583 mListPtr->AppendToTop(aItem);
4584 mOriginalClipChain = mClipChain;
4585 nsDisplayWrapList::UpdateBounds(aBuilder);
4587 if (!aFrame || !aFrame->IsTransformed()) {
4588 return;
4591 // See the previous nsDisplayWrapList constructor
4592 if (aItem->Frame() == aFrame) {
4593 mToReferenceFrame = aItem->ToReferenceFrame();
4596 nsRect visible = aBuilder->GetVisibleRect() +
4597 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
4599 SetBuildingRect(visible);
4602 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList); }
4604 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder,
4605 const nsRect& aRect, HitTestState* aState,
4606 nsTArray<nsIFrame*>* aOutFrames) {
4607 mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames);
4610 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder,
4611 bool* aSnap) const {
4612 *aSnap = false;
4613 return mBounds;
4616 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4617 bool* aSnap) const {
4618 *aSnap = false;
4619 bool snap;
4620 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
4621 GetBounds(aBuilder, &snap));
4624 Maybe<nscolor> nsDisplayWrapList::IsUniform(
4625 nsDisplayListBuilder* aBuilder) const {
4626 // We could try to do something but let's conservatively just return Nothing.
4627 return Nothing();
4630 void nsDisplayWrapper::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4631 NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4634 nsRect nsDisplayWrapList::GetComponentAlphaBounds(
4635 nsDisplayListBuilder* aBuilder) const {
4636 return mListPtr->GetComponentAlphaBounds(aBuilder);
4639 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4640 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4641 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4642 nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList) {
4643 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4644 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
4645 aNewClipList);
4646 return true;
4649 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder,
4650 nsIFrame* aFrame, nsDisplayList* aList,
4651 nsDisplayItemWrapper* aWrapper) {
4652 if (!aList->GetTop()) {
4653 return NS_OK;
4655 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
4656 if (!item) {
4657 return NS_ERROR_OUT_OF_MEMORY;
4659 // aList was emptied
4660 aList->AppendToTop(item);
4661 return NS_OK;
4664 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
4665 nsDisplayList* aList,
4666 nsDisplayItemWrapper* aWrapper) {
4667 for (nsDisplayItem* item : aList->TakeItems()) {
4668 item = aWrapper->WrapItem(aBuilder, item);
4669 if (!item) {
4670 return NS_ERROR_OUT_OF_MEMORY;
4672 aList->AppendToTop(item);
4674 // aList was emptied
4675 return NS_OK;
4678 nsresult nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
4679 nsIFrame* aFrame,
4680 const nsDisplayListSet& aIn,
4681 const nsDisplayListSet& aOut) {
4682 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
4683 NS_ENSURE_SUCCESS(rv, rv);
4685 if (&aOut == &aIn) {
4686 return NS_OK;
4688 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
4689 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
4690 aOut.Floats()->AppendToTop(aIn.Floats());
4691 aOut.Content()->AppendToTop(aIn.Content());
4692 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
4693 aOut.Outlines()->AppendToTop(aIn.Outlines());
4694 return NS_OK;
4697 nsresult nsDisplayItemWrapper::WrapListsInPlace(
4698 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4699 const nsDisplayListSet& aLists) {
4700 nsresult rv;
4701 if (WrapBorderBackground()) {
4702 // Our border-backgrounds are in-flow
4703 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
4704 NS_ENSURE_SUCCESS(rv, rv);
4706 // Our block border-backgrounds are in-flow
4707 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
4708 NS_ENSURE_SUCCESS(rv, rv);
4709 // The floats are not in flow
4710 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
4711 NS_ENSURE_SUCCESS(rv, rv);
4712 // Our child content is in flow
4713 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
4714 NS_ENSURE_SUCCESS(rv, rv);
4715 // The positioned descendants may not be in-flow
4716 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
4717 NS_ENSURE_SUCCESS(rv, rv);
4718 // The outlines may not be in-flow
4719 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
4722 nsDisplayOpacity::nsDisplayOpacity(
4723 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4724 const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsOnly,
4725 bool aNeedsActiveLayer, bool aWrapsBackdropFilter)
4726 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4727 mOpacity(aFrame->StyleEffects()->mOpacity),
4728 mForEventsOnly(aForEventsOnly),
4729 mNeedsActiveLayer(aNeedsActiveLayer),
4730 mChildOpacityState(ChildOpacityState::Unknown),
4731 mWrapsBackdropFilter(aWrapsBackdropFilter) {
4732 MOZ_COUNT_CTOR(nsDisplayOpacity);
4735 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder,
4736 const nsRect& aRect,
4737 nsDisplayItem::HitTestState* aState,
4738 nsTArray<nsIFrame*>* aOutFrames) {
4739 AutoRestore<float> opacity(aState->mCurrentOpacity);
4740 aState->mCurrentOpacity *= mOpacity;
4742 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4743 // only consider fully opaque items? Or make this configurable somehow?
4744 if (aBuilder->HitTestIsForVisibility() && mOpacity == 0.0f) {
4745 return;
4747 nsDisplayWrapList::HitTest(aBuilder, aRect, aState, aOutFrames);
4750 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4751 bool* aSnap) const {
4752 *aSnap = false;
4753 // The only time where mOpacity == 1.0 should be when we have will-change.
4754 // We could report this as opaque then but when the will-change value starts
4755 // animating the element would become non opaque and could cause repaints.
4756 return nsRegion();
4759 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4760 if (GetOpacity() == 0.0f) {
4761 return;
4764 if (GetOpacity() == 1.0f) {
4765 GetChildren()->Paint(aBuilder, aCtx,
4766 mFrame->PresContext()->AppUnitsPerDevPixel());
4767 return;
4770 // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4771 // allocation.
4772 aCtx->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4773 GetChildren()->Paint(aBuilder, aCtx,
4774 mFrame->PresContext()->AppUnitsPerDevPixel());
4775 aCtx->GetDrawTarget()->PopLayer();
4778 /* static */
4779 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
4780 nsIFrame* aFrame) {
4781 return EffectCompositor::HasAnimationsForCompositor(
4782 aFrame, DisplayItemType::TYPE_OPACITY) ||
4783 (ActiveLayerTracker::IsStyleAnimated(
4784 aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()));
4787 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager,
4788 nsDisplayListBuilder* aBuilder) const {
4789 return !EffectCompositor::HasAnimationsForCompositor(
4790 mFrame, DisplayItemType::TYPE_OPACITY);
4793 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4794 // children that don't overlap and can all apply the opacity to themselves.
4795 static const size_t kOpacityMaxChildCount = 3;
4797 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4798 // are likely have more child items than |kOpacityMaxChildCount|.
4799 static const size_t kOpacityMaxListSize = kOpacityMaxChildCount * 2;
4802 * Recursively iterates through |aList| and collects at most
4803 * |kOpacityMaxChildCount| display item pointers to items that return true for
4804 * CanApplyOpacity(). The item pointers are added to |aArray|.
4806 * LayerEventRegions and WrapList items are ignored.
4808 * We need to do this recursively, because the child display items might contain
4809 * nested nsDisplayWrapLists.
4811 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4812 * item that returns false for CanApplyOpacity() is encountered.
4813 * Otherwise returns true.
4815 static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager,
4816 nsDisplayListBuilder* aBuilder,
4817 nsDisplayList* aList,
4818 nsTArray<nsPaintedDisplayItem*>& aArray) {
4819 if (aList->Length() > kOpacityMaxListSize) {
4820 // Exit early, since |aList| will likely contain more than
4821 // |kOpacityMaxChildCount| items.
4822 return false;
4825 for (nsDisplayItem* i : *aList) {
4826 const DisplayItemType type = i->GetType();
4828 if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
4829 continue;
4832 // Descend only into wraplists.
4833 if (type == DisplayItemType::TYPE_WRAP_LIST ||
4834 type == DisplayItemType::TYPE_CONTAINER) {
4835 // The current display item has children, process them first.
4836 if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(),
4837 aArray)) {
4838 return false;
4841 continue;
4844 if (aArray.Length() == kOpacityMaxChildCount) {
4845 return false;
4848 auto* item = i->AsPaintedDisplayItem();
4849 if (!item || !item->CanApplyOpacity(aManager, aBuilder)) {
4850 return false;
4853 aArray.AppendElement(item);
4856 return true;
4859 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager,
4860 nsDisplayListBuilder* aBuilder) {
4861 if (mChildOpacityState == ChildOpacityState::Deferred) {
4862 return false;
4865 // Iterate through the child display list and copy at most
4866 // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4867 AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items;
4868 if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) {
4869 mChildOpacityState = ChildOpacityState::Deferred;
4870 return false;
4873 struct {
4874 nsPaintedDisplayItem* item{};
4875 nsRect bounds;
4876 } children[kOpacityMaxChildCount];
4878 bool snap;
4879 size_t childCount = 0;
4880 for (nsPaintedDisplayItem* item : items) {
4881 children[childCount].item = item;
4882 children[childCount].bounds = item->GetBounds(aBuilder, &snap);
4883 childCount++;
4886 for (size_t i = 0; i < childCount; i++) {
4887 for (size_t j = i + 1; j < childCount; j++) {
4888 if (children[i].bounds.Intersects(children[j].bounds)) {
4889 mChildOpacityState = ChildOpacityState::Deferred;
4890 return false;
4895 mChildOpacityState = ChildOpacityState::Applied;
4896 return true;
4900 * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4901 * that has the same frame as the opacity item, and that supports painting with
4902 * opacity. In this case the opacity item can be optimized away.
4904 bool nsDisplayOpacity::ApplyToMask() {
4905 if (mList.Length() != 1) {
4906 return false;
4909 nsDisplayItem* item = mList.GetBottom();
4910 if (item->Frame() != mFrame) {
4911 // The effect item needs to have the same frame as the opacity item.
4912 return false;
4915 const DisplayItemType type = item->GetType();
4916 if (type == DisplayItemType::TYPE_MASK) {
4917 return true;
4920 return false;
4923 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4924 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder,
4925 float aInheritedOpacity) {
4926 if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
4927 mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
4928 // If we've been split, then we might need to merge, so
4929 // don't flatten us away.
4930 return false;
4933 if (mNeedsActiveLayer || mOpacity == 0.0) {
4934 // If our opacity is zero then we'll discard all descendant display items
4935 // except for layer event regions, so there's no point in doing this
4936 // optimization (and if we do do it, then invalidations of those descendants
4937 // might trigger repainting).
4938 return false;
4941 if (mList.IsEmpty()) {
4942 return false;
4945 // We can only flatten opacity items into a mask if we haven't
4946 // already flattened an earlier ancestor, since the SVG code pulls the opacity
4947 // from style directly, and won't know about the outer opacity value.
4948 if (aInheritedOpacity == 1.0f && ApplyToMask()) {
4949 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame));
4950 mChildOpacityState = ChildOpacityState::Applied;
4951 return true;
4954 // Return true if we successfully applied opacity to child items.
4955 return CanApplyToChildren(aManager, aBuilder);
4958 void nsDisplayOpacity::ComputeInvalidationRegion(
4959 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4960 nsRegion* aInvalidRegion) const {
4961 const auto* geometry =
4962 static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
4964 bool snap;
4965 if (mOpacity != geometry->mOpacity) {
4966 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4970 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) {
4971 aStream << " (opacity " << mOpacity << ", mChildOpacityState: ";
4972 switch (mChildOpacityState) {
4973 case ChildOpacityState::Unknown:
4974 aStream << "Unknown";
4975 break;
4976 case ChildOpacityState::Applied:
4977 aStream << "Applied";
4978 break;
4979 case ChildOpacityState::Deferred:
4980 aStream << "Deferred";
4981 break;
4982 default:
4983 break;
4986 aStream << ")";
4989 bool nsDisplayOpacity::CreateWebRenderCommands(
4990 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4991 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4992 nsDisplayListBuilder* aDisplayListBuilder) {
4993 MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied);
4994 float oldOpacity = aBuilder.GetInheritedOpacity();
4995 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
4996 aBuilder.SetInheritedOpacity(1.0f);
4997 aBuilder.SetInheritedClipChain(nullptr);
4998 float opacity = mOpacity * oldOpacity;
4999 float* opacityForSC = &opacity;
5001 uint64_t animationsId =
5002 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
5003 wr::WrAnimationProperty prop{
5004 wr::WrAnimationType::Opacity,
5005 animationsId,
5008 wr::StackingContextParams params;
5009 params.animation = animationsId ? &prop : nullptr;
5010 params.opacity = opacityForSC;
5011 params.clip =
5012 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5013 if (mWrapsBackdropFilter) {
5014 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
5016 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5017 params);
5019 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
5020 &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
5021 aBuilder.SetInheritedOpacity(oldOpacity);
5022 aBuilder.SetInheritedClipChain(oldClipChain);
5023 return true;
5026 nsDisplayBlendMode::nsDisplayBlendMode(
5027 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5028 StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
5029 const bool aIsForBackground)
5030 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5031 mBlendMode(aBlendMode),
5032 mIsForBackground(aIsForBackground) {
5033 MOZ_COUNT_CTOR(nsDisplayBlendMode);
5036 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5037 bool* aSnap) const {
5038 *aSnap = false;
5039 // We are never considered opaque
5040 return nsRegion();
5043 bool nsDisplayBlendMode::CreateWebRenderCommands(
5044 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5045 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5046 nsDisplayListBuilder* aDisplayListBuilder) {
5047 wr::StackingContextParams params;
5048 params.mix_blend_mode =
5049 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
5050 params.clip =
5051 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5052 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5053 params);
5055 return nsDisplayWrapList::CreateWebRenderCommands(
5056 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5059 void nsDisplayBlendMode::Paint(nsDisplayListBuilder* aBuilder,
5060 gfxContext* aCtx) {
5061 // This should be switched to use PushLayerWithBlend, once it's
5062 // been implemented for all DrawTarget backends.
5063 DrawTarget* dt = aCtx->GetDrawTarget();
5064 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5065 Rect rect = NSRectToRect(GetPaintRect(aBuilder, aCtx), appUnitsPerDevPixel);
5066 rect.RoundOut();
5068 // Create a temporary DrawTarget that is clipped to the area that
5069 // we're going to draw to. This will include the same transform as
5070 // is currently on |dt|.
5071 RefPtr<DrawTarget> temp =
5072 dt->CreateClippedDrawTarget(rect, SurfaceFormat::B8G8R8A8);
5073 if (!temp) {
5074 return;
5077 RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(temp);
5079 GetChildren()->Paint(aBuilder, ctx,
5080 mFrame->PresContext()->AppUnitsPerDevPixel());
5082 // Draw the temporary DT to the real destination, applying the blend mode, but
5083 // no transform.
5084 temp->Flush();
5085 RefPtr<SourceSurface> surface = temp->Snapshot();
5086 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
5087 dt->SetTransform(Matrix());
5088 dt->DrawSurface(
5089 surface, Rect(surface->GetRect()), Rect(surface->GetRect()),
5090 DrawSurfaceOptions(),
5091 DrawOptions(1.0f, nsCSSRendering::GetGFXBlendMode(mBlendMode)));
5094 gfx::CompositionOp nsDisplayBlendMode::BlendMode() {
5095 return nsCSSRendering::GetGFXBlendMode(mBlendMode);
5098 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const {
5099 // Items for the same content element should be merged into a single
5100 // compositing group.
5101 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
5102 !HasSameContent(aItem)) {
5103 return false;
5106 const auto* item = static_cast<const nsDisplayBlendMode*>(aItem);
5107 if (mIsForBackground || item->mIsForBackground) {
5108 // Don't merge background-blend-mode items
5109 return false;
5112 return true;
5115 /* static */
5116 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForMixBlendMode(
5117 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5118 const ActiveScrolledRoot* aActiveScrolledRoot) {
5119 return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
5120 aActiveScrolledRoot, false);
5123 /* static */
5124 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5125 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5126 nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot) {
5127 if (aSecondaryFrame) {
5128 auto type = GetTableTypeFromFrame(aFrame);
5129 auto index = static_cast<uint16_t>(type);
5131 return MakeDisplayItemWithIndex<nsDisplayTableBlendContainer>(
5132 aBuilder, aSecondaryFrame, index, aList, aActiveScrolledRoot, true,
5133 aFrame);
5136 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>(
5137 aBuilder, aFrame, 1, aList, aActiveScrolledRoot, true);
5140 nsDisplayBlendContainer::nsDisplayBlendContainer(
5141 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5142 const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground)
5143 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5144 mIsForBackground(aIsForBackground) {
5145 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
5148 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder* aBuilder,
5149 gfxContext* aCtx) {
5150 aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5151 GetChildren()->Paint(aBuilder, aCtx,
5152 mFrame->PresContext()->AppUnitsPerDevPixel());
5153 aCtx->GetDrawTarget()->PopLayer();
5156 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5157 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5158 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5159 nsDisplayListBuilder* aDisplayListBuilder) {
5160 wr::StackingContextParams params;
5161 params.flags |= wr::StackingContextFlags::IS_BLEND_CONTAINER;
5162 params.clip =
5163 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5164 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5165 params);
5167 return nsDisplayWrapList::CreateWebRenderCommands(
5168 aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5171 nsDisplayOwnLayer::nsDisplayOwnLayer(
5172 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5173 const ActiveScrolledRoot* aActiveScrolledRoot,
5174 nsDisplayOwnLayerFlags aFlags, const ScrollbarData& aScrollbarData,
5175 bool aForceActive, bool aClearClipChain)
5176 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
5177 aClearClipChain),
5178 mFlags(aFlags),
5179 mScrollbarData(aScrollbarData),
5180 mForceActive(aForceActive),
5181 mWrAnimationId(0) {
5182 MOZ_COUNT_CTOR(nsDisplayOwnLayer);
5185 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5186 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb;
5189 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5190 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container;
5193 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5194 if (!IsScrollbarContainer()) {
5195 return false;
5198 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5199 mScrollbarData.mTargetViewId ==
5200 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5203 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5204 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM;
5207 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5208 return GetType() == DisplayItemType::TYPE_FIXED_POSITION;
5211 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5212 return GetType() == DisplayItemType::TYPE_STICKY_POSITION;
5215 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5216 if (!mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
5217 return false;
5219 return mFrame->PresContext()->HasDynamicToolbar() ||
5220 // For tests on Android, this pref is set to simulate the dynamic
5221 // toolbar
5222 StaticPrefs::apz_fixed_margin_override_enabled();
5225 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5226 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5227 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5228 nsDisplayListBuilder* aDisplayListBuilder) {
5229 Maybe<wr::WrAnimationProperty> prop;
5230 bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() &&
5231 (IsScrollThumbLayer() || IsZoomingLayer() ||
5232 (IsFixedPositionLayer() && HasDynamicToolbar()) ||
5233 (IsStickyPositionLayer() && HasDynamicToolbar()) ||
5234 (IsRootScrollbarContainer() && HasDynamicToolbar()));
5236 if (needsProp) {
5237 // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5238 // to create and set an animation id. That way APZ can adjust the position/
5239 // zoom of this content asynchronously as needed.
5240 RefPtr<WebRenderAPZAnimationData> animationData =
5241 aManager->CommandBuilder()
5242 .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(this);
5243 mWrAnimationId = animationData->GetAnimationId();
5245 prop.emplace();
5246 prop->id = mWrAnimationId;
5247 prop->key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5248 wr::SpatialKeyKind::APZ);
5249 prop->effect_type = wr::WrAnimationType::Transform;
5252 wr::StackingContextParams params;
5253 params.animation = prop.ptrOr(nullptr);
5254 params.clip =
5255 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5256 if (IsScrollbarContainer()) {
5257 params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER;
5259 if (IsScrollThumbLayer()) {
5260 params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_THUMB;
5262 if (IsZoomingLayer() ||
5263 ((IsFixedPositionLayer() && HasDynamicToolbar()) ||
5264 (IsStickyPositionLayer() && HasDynamicToolbar()) ||
5265 (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5266 params.is_2d_scale_translation = true;
5267 params.should_snap = true;
5270 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5271 params);
5273 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
5274 aDisplayListBuilder);
5275 return true;
5278 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData,
5279 WebRenderLayerScrollData* aLayerData) {
5280 bool isRelevantToApz =
5281 (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5282 (IsFixedPositionLayer() && HasDynamicToolbar()) ||
5283 (IsStickyPositionLayer() && HasDynamicToolbar()));
5285 if (!isRelevantToApz) {
5286 return false;
5289 if (!aLayerData) {
5290 return true;
5293 if (IsZoomingLayer()) {
5294 aLayerData->SetZoomAnimationId(mWrAnimationId);
5295 return true;
5298 if (IsFixedPositionLayer() && HasDynamicToolbar()) {
5299 aLayerData->SetFixedPositionAnimationId(mWrAnimationId);
5300 return true;
5303 if (IsStickyPositionLayer() && HasDynamicToolbar()) {
5304 aLayerData->SetStickyPositionAnimationId(mWrAnimationId);
5305 return true;
5308 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5310 aLayerData->SetScrollbarData(mScrollbarData);
5312 if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5313 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5314 return true;
5317 if (IsScrollThumbLayer()) {
5318 aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5319 LayoutDeviceRect bounds = LayoutDeviceIntRect::FromAppUnits(
5320 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
5321 // We use a resolution of 1.0 because this is a WebRender codepath which
5322 // always uses containerless scrolling, and so resolution doesn't apply to
5323 // scrollbars.
5324 LayerIntRect layerBounds =
5325 RoundedOut(bounds * LayoutDeviceToLayerScale(1.0f));
5326 aLayerData->SetVisibleRegion(LayerIntRegion(layerBounds));
5328 return true;
5331 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) {
5332 aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
5333 (int)mFlags, mScrollbarData.mTargetViewId)
5334 .get();
5337 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
5338 nsIFrame* aFrame,
5339 nsSubDocumentFrame* aSubDocFrame,
5340 nsDisplayList* aList,
5341 nsDisplayOwnLayerFlags aFlags)
5342 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5343 aBuilder->CurrentActiveScrolledRoot(), aFlags),
5344 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5345 mShouldFlatten(false),
5346 mSubDocFrame(aSubDocFrame) {
5347 MOZ_COUNT_CTOR(nsDisplaySubDocument);
5349 if (mSubDocFrame && mSubDocFrame != mFrame) {
5350 mSubDocFrame->AddDisplayItem(this);
5354 nsDisplaySubDocument::~nsDisplaySubDocument() {
5355 MOZ_COUNT_DTOR(nsDisplaySubDocument);
5356 if (mSubDocFrame) {
5357 mSubDocFrame->RemoveDisplayItem(this);
5361 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const {
5362 return mSubDocFrame ? mSubDocFrame : mFrame;
5365 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) {
5366 if (aFrame == mSubDocFrame) {
5367 mSubDocFrame = nullptr;
5368 SetDeletedFrame();
5370 nsDisplayOwnLayer::RemoveFrame(aFrame);
5373 void nsDisplaySubDocument::Disown() {
5374 if (mFrame) {
5375 mFrame->RemoveDisplayItem(this);
5376 RemoveFrame(mFrame);
5378 if (mSubDocFrame) {
5379 mSubDocFrame->RemoveDisplayItem(this);
5380 RemoveFrame(mSubDocFrame);
5384 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder,
5385 nsIFrame* aFrame) {
5386 return aBuilder->IsPaintingToWindow() &&
5387 DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext());
5390 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder,
5391 bool* aSnap) const {
5392 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5394 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5395 usingDisplayPort) {
5396 *aSnap = false;
5397 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
5400 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
5403 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5404 bool* aSnap) const {
5405 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5407 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5408 usingDisplayPort) {
5409 *aSnap = false;
5410 return nsRegion();
5413 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
5416 /* static */
5417 nsDisplayFixedPosition* nsDisplayFixedPosition::CreateForFixedBackground(
5418 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5419 nsDisplayBackgroundImage* aImage, const uint16_t aIndex,
5420 const ActiveScrolledRoot* aScrollTargetASR) {
5421 nsDisplayList temp(aBuilder);
5422 temp.AppendToTop(aImage);
5424 if (aSecondaryFrame) {
5425 auto tableType = GetTableTypeFromFrame(aFrame);
5426 const uint16_t index = CalculateTablePerFrameKey(aIndex + 1, tableType);
5427 return MakeDisplayItemWithIndex<nsDisplayTableFixedPosition>(
5428 aBuilder, aSecondaryFrame, index, &temp, aFrame, aScrollTargetASR);
5431 return MakeDisplayItemWithIndex<nsDisplayFixedPosition>(
5432 aBuilder, aFrame, aIndex + 1, &temp, aScrollTargetASR);
5435 nsDisplayFixedPosition::nsDisplayFixedPosition(
5436 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5437 const ActiveScrolledRoot* aActiveScrolledRoot,
5438 const ActiveScrolledRoot* aScrollTargetASR)
5439 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5440 mScrollTargetASR(aScrollTargetASR),
5441 mIsFixedBackground(false) {
5442 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5445 nsDisplayFixedPosition::nsDisplayFixedPosition(
5446 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5447 const ActiveScrolledRoot* aScrollTargetASR)
5448 : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5449 aBuilder->CurrentActiveScrolledRoot()),
5450 // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5451 mScrollTargetASR(aScrollTargetASR),
5452 mIsFixedBackground(true) {
5453 MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5456 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() {
5457 if (mScrollTargetASR &&
5458 (mIsFixedBackground || !nsLayoutUtils::IsReallyFixedPos(mFrame))) {
5459 return mScrollTargetASR->GetViewId();
5461 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5464 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5465 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5466 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5467 nsDisplayListBuilder* aDisplayListBuilder) {
5468 SideBits sides = SideBits::eNone;
5469 if (!mIsFixedBackground) {
5470 sides = nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5473 // We install this RAII scrolltarget tracker so that any
5474 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5475 // share the same ASR as this item) use the correct scroll target. That way
5476 // attempts to scroll on those items will scroll the root scroll frame.
5477 wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker(
5478 aBuilder, GetActiveScrolledRoot(), GetScrollTargetId(), sides);
5479 return nsDisplayOwnLayer::CreateWebRenderCommands(
5480 aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
5483 bool nsDisplayFixedPosition::UpdateScrollData(
5484 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5485 if (aLayerData) {
5486 if (!mIsFixedBackground) {
5487 aLayerData->SetFixedPositionSides(
5488 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame));
5490 aLayerData->SetFixedPositionScrollContainerId(GetScrollTargetId());
5492 nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5493 return true;
5496 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream& aStream) {
5497 aStream << nsPrintfCString(
5498 " (containerASR %s) (scrolltarget %" PRIu64 ")",
5499 ActiveScrolledRoot::ToString(mScrollTargetASR).get(),
5500 GetScrollTargetId())
5501 .get();
5504 TableType GetTableTypeFromFrame(nsIFrame* aFrame) {
5505 if (aFrame->IsTableFrame()) {
5506 return TableType::Table;
5509 if (aFrame->IsTableColFrame()) {
5510 return TableType::TableCol;
5513 if (aFrame->IsTableColGroupFrame()) {
5514 return TableType::TableColGroup;
5517 if (aFrame->IsTableRowFrame()) {
5518 return TableType::TableRow;
5521 if (aFrame->IsTableRowGroupFrame()) {
5522 return TableType::TableRowGroup;
5525 if (aFrame->IsTableCellFrame()) {
5526 return TableType::TableCell;
5529 MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5530 return TableType::Table;
5533 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5534 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5535 nsIFrame* aAncestorFrame, const ActiveScrolledRoot* aScrollTargetASR)
5536 : nsDisplayFixedPosition(aBuilder, aFrame, aList, aScrollTargetASR),
5537 mAncestorFrame(aAncestorFrame) {
5538 if (aBuilder->IsRetainingDisplayList()) {
5539 mAncestorFrame->AddDisplayItem(this);
5543 nsDisplayStickyPosition::nsDisplayStickyPosition(
5544 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5545 const ActiveScrolledRoot* aActiveScrolledRoot,
5546 const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
5547 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5548 mContainerASR(aContainerASR),
5549 mClippedToDisplayPort(aClippedToDisplayPort) {
5550 MOZ_COUNT_CTOR(nsDisplayStickyPosition);
5553 // Returns the smallest distance from "0" to the range [min, max] where
5554 // min <= max. Despite the name, the return value is actually a 1-D vector,
5555 // and so may be negative if max < 0.
5556 static nscoord DistanceToRange(nscoord min, nscoord max) {
5557 MOZ_ASSERT(min <= max);
5558 if (max < 0) {
5559 return max;
5561 if (min > 0) {
5562 return min;
5564 MOZ_ASSERT(min <= 0 && max >= 0);
5565 return 0;
5568 // Returns the magnitude of the part of the range [min, max] that is greater
5569 // than zero. The return value is always non-negative.
5570 static nscoord PositivePart(nscoord min, nscoord max) {
5571 MOZ_ASSERT(min <= max);
5572 if (min >= 0) {
5573 return max - min;
5575 if (max > 0) {
5576 return max;
5578 return 0;
5581 // Returns the magnitude of the part of the range [min, max] that is less
5582 // than zero. The return value is always non-negative.
5583 static nscoord NegativePart(nscoord min, nscoord max) {
5584 MOZ_ASSERT(min <= max);
5585 if (max <= 0) {
5586 return max - min;
5588 if (min < 0) {
5589 return 0 - min;
5591 return 0;
5594 StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() {
5595 StickyScrollContainer* stickyScrollContainer =
5596 StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
5597 if (stickyScrollContainer) {
5598 // If there's no ASR for the scrollframe that this sticky item is attached
5599 // to, then don't create a WR sticky item for it either. Trying to do so
5600 // will end in sadness because WR will interpret some coordinates as
5601 // relative to the nearest enclosing scrollframe, which will correspond
5602 // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5603 // same as the scrollframe this sticky item is actually supposed to be
5604 // attached to, thus the sadness.
5605 // Not sending WR the sticky item is ok, because the enclosing scrollframe
5606 // will never be asynchronously scrolled. Instead we will always position
5607 // the sticky items correctly on the gecko side and WR will never need to
5608 // adjust their position itself.
5609 MOZ_ASSERT(
5610 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5611 if (!stickyScrollContainer->ScrollFrame()
5612 ->IsMaybeAsynchronouslyScrolled()) {
5613 stickyScrollContainer = nullptr;
5616 return stickyScrollContainer;
5619 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5620 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5621 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5622 nsDisplayListBuilder* aDisplayListBuilder) {
5623 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5625 Maybe<wr::SpaceAndClipChainHelper> saccHelper;
5627 if (stickyScrollContainer) {
5628 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5630 bool snap;
5631 nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
5633 Maybe<float> topMargin;
5634 Maybe<float> rightMargin;
5635 Maybe<float> bottomMargin;
5636 Maybe<float> leftMargin;
5637 wr::StickyOffsetBounds vBounds = {0.0, 0.0};
5638 wr::StickyOffsetBounds hBounds = {0.0, 0.0};
5639 nsPoint appliedOffset;
5641 nsRectAbsolute outer;
5642 nsRectAbsolute inner;
5643 stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5645 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
5646 nsPoint offset =
5647 scrollFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5649 // Adjust the scrollPort coordinates to be relative to the reference frame,
5650 // so that it is in the same space as everything else.
5651 nsRect scrollPort =
5652 stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
5653 scrollPort += offset;
5655 // The following computations make more sense upon understanding the
5656 // semantics of "inner" and "outer", which is explained in the comment on
5657 // SetStickyPositionData in Layers.h.
5659 if (outer.YMost() != inner.YMost()) {
5660 // Question: How far will itemBounds.y be from the top of the scrollport
5661 // when we have scrolled from the current scroll position of "0" to
5662 // reach the range [inner.YMost(), outer.YMost()] where the item gets
5663 // stuck?
5664 // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5665 // needs to be adjusted by the distance to the range, less any other
5666 // sticky ranges that fall between 0 and the range. If the distance is
5667 // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5668 // scrolling upwards (decreasing scroll offset) to reach that range,
5669 // which would increase itemBounds.y and make it farther away from the
5670 // top of the scrollport. So in that case the adjustment is -distance.
5671 // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5672 // we would be scrolling downwards, itemBounds.y would decrease, and we
5673 // again need to adjust by -distance. If we are already in the range
5674 // then no adjustment is needed and distance is 0 so again using
5675 // -distance works. If the distance is positive, and the item has both
5676 // top and bottom sticky ranges, then the bottom sticky range may fall
5677 // (entirely[1] or partly[2]) between the current scroll position.
5678 // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5679 // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5680 // In these cases, the item doesn't actually move for that part of the
5681 // distance, so we need to subtract out that bit, which can be computed
5682 // as the positive portion of the range [outer.Y(), inner.Y()].
5683 nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
5684 if (distance > 0) {
5685 distance -= PositivePart(outer.Y(), inner.Y());
5687 topMargin = Some(NSAppUnitsToFloatPixels(
5688 itemBounds.y - scrollPort.y - distance, auPerDevPixel));
5689 // Question: What is the maximum positive ("downward") offset that WR
5690 // will have to apply to this item in order to prevent the item from
5691 // visually moving?
5692 // Answer: Since the item is "sticky" in the range [inner.YMost(),
5693 // outer.YMost()], the maximum offset will be the size of the range, which
5694 // is outer.YMost() - inner.YMost().
5695 vBounds.max =
5696 NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
5697 // Question: how much of an offset has layout already applied to the item?
5698 // Answer: if we are
5699 // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5700 // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5701 // then layout has already applied some offset to the position of the
5702 // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5703 // and |outer.YMost() - inner.YMost()| in case (b).
5704 if (inner.YMost() < 0) {
5705 appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
5706 MOZ_ASSERT(appliedOffset.y > 0);
5709 if (outer.Y() != inner.Y()) {
5710 // Similar logic as in the previous section, but this time we care about
5711 // the distance from itemBounds.YMost() to scrollPort.YMost().
5712 nscoord distance = DistanceToRange(outer.Y(), inner.Y());
5713 if (distance < 0) {
5714 distance += NegativePart(inner.YMost(), outer.YMost());
5716 bottomMargin = Some(NSAppUnitsToFloatPixels(
5717 scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
5718 // And here WR will be moving the item upwards rather than downwards so
5719 // again things are inverted from the previous block.
5720 vBounds.min =
5721 NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
5722 // We can't have appliedOffset be both positive and negative, and the top
5723 // adjustment takes priority. So here we only update appliedOffset.y if
5724 // it wasn't set by the top-sticky case above.
5725 if (appliedOffset.y == 0 && inner.Y() > 0) {
5726 appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
5727 MOZ_ASSERT(appliedOffset.y < 0);
5730 // Same as above, but for the x-axis
5731 if (outer.XMost() != inner.XMost()) {
5732 nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
5733 if (distance > 0) {
5734 distance -= PositivePart(outer.X(), inner.X());
5736 leftMargin = Some(NSAppUnitsToFloatPixels(
5737 itemBounds.x - scrollPort.x - distance, auPerDevPixel));
5738 hBounds.max =
5739 NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
5740 if (inner.XMost() < 0) {
5741 appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
5742 MOZ_ASSERT(appliedOffset.x > 0);
5745 if (outer.X() != inner.X()) {
5746 nscoord distance = DistanceToRange(outer.X(), inner.X());
5747 if (distance < 0) {
5748 distance += NegativePart(inner.XMost(), outer.XMost());
5750 rightMargin = Some(NSAppUnitsToFloatPixels(
5751 scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
5752 hBounds.min =
5753 NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
5754 if (appliedOffset.x == 0 && inner.X() > 0) {
5755 appliedOffset.x = std::max(0, outer.X()) - inner.X();
5756 MOZ_ASSERT(appliedOffset.x < 0);
5760 LayoutDeviceRect bounds =
5761 LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
5762 wr::LayoutVector2D applied = {
5763 NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
5764 NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
5765 wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame(
5766 wr::ToLayoutRect(bounds), topMargin.ptrOr(nullptr),
5767 rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
5768 leftMargin.ptrOr(nullptr), vBounds, hBounds, applied,
5769 wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5770 wr::SpatialKeyKind::Sticky));
5772 saccHelper.emplace(aBuilder, spatialId);
5773 aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId);
5777 wr::StackingContextParams params;
5778 params.clip =
5779 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5780 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
5781 aBuilder, params);
5782 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc,
5783 aManager, aDisplayListBuilder);
5786 if (stickyScrollContainer) {
5787 aManager->CommandBuilder().PopOverrideForASR(mContainerASR);
5790 return true;
5793 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5794 StickyScrollContainer* aStickyScrollContainer, float aAppUnitsPerDevPixel,
5795 float aScaleX, float aScaleY, LayerRectAbsolute& aStickyOuter,
5796 LayerRectAbsolute& aStickyInner) {
5797 nsRectAbsolute outer;
5798 nsRectAbsolute inner;
5799 aStickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5800 aStickyOuter.SetBox(
5801 NSAppUnitsToFloatPixels(outer.X(), aAppUnitsPerDevPixel) * aScaleX,
5802 NSAppUnitsToFloatPixels(outer.Y(), aAppUnitsPerDevPixel) * aScaleY,
5803 NSAppUnitsToFloatPixels(outer.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5804 NSAppUnitsToFloatPixels(outer.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5805 aStickyInner.SetBox(
5806 NSAppUnitsToFloatPixels(inner.X(), aAppUnitsPerDevPixel) * aScaleX,
5807 NSAppUnitsToFloatPixels(inner.Y(), aAppUnitsPerDevPixel) * aScaleY,
5808 NSAppUnitsToFloatPixels(inner.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5809 NSAppUnitsToFloatPixels(inner.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5812 bool nsDisplayStickyPosition::UpdateScrollData(
5813 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5814 bool hasDynamicToolbar = HasDynamicToolbar();
5815 if (aLayerData && hasDynamicToolbar) {
5816 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5817 if (stickyScrollContainer) {
5818 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5819 float cumulativeResolution =
5820 mFrame->PresShell()->GetCumulativeResolution();
5821 LayerRectAbsolute stickyOuter;
5822 LayerRectAbsolute stickyInner;
5823 CalculateLayerScrollRanges(stickyScrollContainer, auPerDevPixel,
5824 cumulativeResolution, cumulativeResolution,
5825 stickyOuter, stickyInner);
5826 aLayerData->SetStickyScrollRangeOuter(stickyOuter);
5827 aLayerData->SetStickyScrollRangeInner(stickyInner);
5829 SideBits sides =
5830 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5831 aLayerData->SetFixedPositionSides(sides);
5833 ScrollableLayerGuid::ViewID scrollId =
5834 nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5835 ->GetScrolledFrame()
5836 ->GetContent());
5837 aLayerData->SetStickyPositionScrollContainerId(scrollId);
5840 // Return true if either there is a dynamic toolbar affecting this sticky
5841 // item or the OwnLayer base implementation returns true for some other
5842 // reason.
5843 bool ret = hasDynamicToolbar;
5844 ret |= nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5845 return ret;
5848 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5849 nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
5850 nsIFrame* aScrollFrame, const CompositorHitTestInfo& aHitInfo,
5851 const nsRect& aHitArea)
5852 : nsDisplayWrapList(aBuilder, aScrollFrame),
5853 mScrollFrame(aScrollFrame),
5854 mScrolledFrame(aScrolledFrame),
5855 mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5856 mHitInfo(aHitInfo),
5857 mHitArea(aHitArea) {
5858 #ifdef NS_BUILD_REFCNT_LOGGING
5859 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
5860 #endif
5863 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5864 nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) {
5865 ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
5866 mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(),
5867 ToReferenceFrame(), aLayerManager, mScrollParentId,
5868 mScrollFrame->GetSize(), false);
5869 metadata.GetMetrics().SetIsScrollInfoLayer(true);
5870 nsIScrollableFrame* scrollableFrame = mScrollFrame->GetScrollTargetFrame();
5871 if (scrollableFrame) {
5872 aBuilder->AddScrollFrameToNotify(scrollableFrame);
5875 return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
5878 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5879 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5880 if (aLayerData) {
5881 UniquePtr<ScrollMetadata> metadata =
5882 ComputeScrollMetadata(aData->GetBuilder(), aData->GetManager());
5883 MOZ_ASSERT(aData);
5884 MOZ_ASSERT(metadata);
5885 aLayerData->AppendScrollMetadata(*aData, *metadata);
5887 return true;
5890 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5891 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5892 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5893 nsDisplayListBuilder* aDisplayListBuilder) {
5894 ScrollableLayerGuid::ViewID scrollId =
5895 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame->GetContent());
5897 const LayoutDeviceRect devRect = LayoutDeviceRect::FromAppUnits(
5898 mHitArea, mScrollFrame->PresContext()->AppUnitsPerDevPixel());
5900 const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
5902 aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, mHitInfo,
5903 SideBits::eNone);
5905 return true;
5908 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) {
5909 aStream << " (scrollframe " << mScrollFrame << " scrolledFrame "
5910 << mScrolledFrame << ")";
5913 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5914 nsSubDocumentFrame* aSubDocFrame,
5915 nsDisplayList* aList, int32_t aAPD,
5916 int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags)
5917 : nsDisplaySubDocument(aBuilder, aFrame, aSubDocFrame, aList, aFlags),
5918 mAPD(aAPD),
5919 mParentAPD(aParentAPD) {
5920 MOZ_COUNT_CTOR(nsDisplayZoom);
5923 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder,
5924 bool* aSnap) const {
5925 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
5926 *aSnap = false;
5927 return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD);
5930 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5931 HitTestState* aState,
5932 nsTArray<nsIFrame*>* aOutFrames) {
5933 nsRect rect;
5934 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5935 // rect as well instead of possibly rounding the width or height to zero.
5936 if (aRect.width == 1 && aRect.height == 1) {
5937 rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD));
5938 rect.width = rect.height = 1;
5939 } else {
5940 rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
5942 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5945 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5946 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5947 const ActiveScrolledRoot* aActiveScrolledRoot, FrameMetrics::ViewID aViewID)
5948 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5949 mViewID(aViewID) {
5950 MOZ_COUNT_CTOR(nsDisplayAsyncZoom);
5953 #ifdef NS_BUILD_REFCNT_LOGGING
5954 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5955 MOZ_COUNT_DTOR(nsDisplayAsyncZoom);
5957 #endif
5959 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder* aBuilder,
5960 const nsRect& aRect, HitTestState* aState,
5961 nsTArray<nsIFrame*>* aOutFrames) {
5962 #ifdef DEBUG
5963 nsIScrollableFrame* scrollFrame = do_QueryFrame(mFrame);
5964 MOZ_ASSERT(scrollFrame && ViewportUtils::IsZoomedContentRoot(
5965 scrollFrame->GetScrolledFrame()));
5966 #endif
5967 nsRect rect = ViewportUtils::VisualToLayout(aRect, mFrame->PresShell());
5968 mList.HitTest(aBuilder, rect, aState, aOutFrames);
5971 bool nsDisplayAsyncZoom::UpdateScrollData(
5972 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5973 bool ret = nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5974 MOZ_ASSERT(ret);
5975 if (aLayerData) {
5976 aLayerData->SetAsyncZoomContainerId(mViewID);
5978 return ret;
5981 ///////////////////////////////////////////////////
5982 // nsDisplayTransform Implementation
5985 #ifndef DEBUG
5986 static_assert(sizeof(nsDisplayTransform) <= 512,
5987 "nsDisplayTransform has grown");
5988 #endif
5990 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5991 nsIFrame* aFrame, nsDisplayList* aList,
5992 const nsRect& aChildrenBuildingRect)
5993 : nsPaintedDisplayItem(aBuilder, aFrame),
5994 mChildren(aBuilder),
5995 mTransform(Some(Matrix4x4())),
5996 mChildrenBuildingRect(aChildrenBuildingRect),
5997 mPrerenderDecision(PrerenderDecision::No),
5998 mIsTransformSeparator(true),
5999 mHasTransformGetter(false),
6000 mHasAssociatedPerspective(false) {
6001 MOZ_COUNT_CTOR(nsDisplayTransform);
6002 MOZ_ASSERT(aFrame, "Must have a frame!");
6003 Init(aBuilder, aList);
6006 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
6007 nsIFrame* aFrame, nsDisplayList* aList,
6008 const nsRect& aChildrenBuildingRect,
6009 PrerenderDecision aPrerenderDecision)
6010 : nsPaintedDisplayItem(aBuilder, aFrame),
6011 mChildren(aBuilder),
6012 mChildrenBuildingRect(aChildrenBuildingRect),
6013 mPrerenderDecision(aPrerenderDecision),
6014 mIsTransformSeparator(false),
6015 mHasTransformGetter(false),
6016 mHasAssociatedPerspective(false) {
6017 MOZ_COUNT_CTOR(nsDisplayTransform);
6018 MOZ_ASSERT(aFrame, "Must have a frame!");
6019 SetReferenceFrameToAncestor(aBuilder);
6020 Init(aBuilder, aList);
6023 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
6024 nsIFrame* aFrame, nsDisplayList* aList,
6025 const nsRect& aChildrenBuildingRect,
6026 decltype(WithTransformGetter))
6027 : nsPaintedDisplayItem(aBuilder, aFrame),
6028 mChildren(aBuilder),
6029 mChildrenBuildingRect(aChildrenBuildingRect),
6030 mPrerenderDecision(PrerenderDecision::No),
6031 mIsTransformSeparator(false),
6032 mHasTransformGetter(true),
6033 mHasAssociatedPerspective(false) {
6034 MOZ_COUNT_CTOR(nsDisplayTransform);
6035 MOZ_ASSERT(aFrame, "Must have a frame!");
6036 MOZ_ASSERT(aFrame->GetTransformGetter());
6037 Init(aBuilder, aList);
6040 void nsDisplayTransform::SetReferenceFrameToAncestor(
6041 nsDisplayListBuilder* aBuilder) {
6042 if (mFrame == aBuilder->RootReferenceFrame()) {
6043 return;
6045 // We manually recompute mToReferenceFrame without going through the
6046 // builder, since this won't apply the 'additional offset'. Our
6047 // children will already be painting with that applied, and we don't
6048 // want to include it a second time in our transform. We don't recompute
6049 // our visible/building rects, since those should still include the additional
6050 // offset.
6051 // TODO: Are there are things computed using our ToReferenceFrame that should
6052 // have the additional offset applied? Should we instead just manually remove
6053 // the offset from our transform instead of this more general value?
6054 // Can we instead apply the additional offset to us and not our children, like
6055 // we do for all other offsets (and how reference frames are supposed to
6056 // work)?
6057 nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame);
6058 const nsIFrame* referenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
6059 mToReferenceFrame = mFrame->GetOffsetToCrossDoc(referenceFrame);
6062 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
6063 nsDisplayList* aChildren) {
6064 mChildren.AppendToTop(aChildren);
6065 UpdateBounds(aBuilder);
6068 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
6069 return false;
6072 /* Returns the delta specified by the transform-origin property.
6073 * This is a positive delta, meaning that it indicates the direction to move
6074 * to get from (0, 0) of the frame to the transform origin. This function is
6075 * called off the main thread.
6077 /* static */
6078 Point3D nsDisplayTransform::GetDeltaToTransformOrigin(
6079 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6080 float aAppUnitsPerPixel) {
6081 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6082 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6083 aFrame->Combines3DTransformWithAncestors(),
6084 "Shouldn't get a delta for an untransformed frame!");
6086 if (!aFrame->IsTransformed()) {
6087 return Point3D();
6090 /* For both of the coordinates, if the value of transform is a
6091 * percentage, it's relative to the size of the frame. Otherwise, if it's
6092 * a distance, it's already computed for us!
6094 const nsStyleDisplay* display = aFrame->StyleDisplay();
6096 const StyleTransformOrigin& transformOrigin = display->mTransformOrigin;
6097 CSSPoint origin = nsStyleTransformMatrix::Convert2DPosition(
6098 transformOrigin.horizontal, transformOrigin.vertical, aRefBox);
6100 if (aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
6101 // SVG frames (unlike other frames) have a reference box that can be (and
6102 // typically is) offset from the TopLeft() of the frame. We need to account
6103 // for that here.
6104 origin.x += CSSPixel::FromAppUnits(aRefBox.X());
6105 origin.y += CSSPixel::FromAppUnits(aRefBox.Y());
6108 float scale = AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel);
6109 float z = transformOrigin.depth._0;
6110 return Point3D(origin.x * scale, origin.y * scale, z * scale);
6113 /* static */
6114 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
6115 float aAppUnitsPerPixel,
6116 Matrix4x4& aOutMatrix) {
6117 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6118 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6119 aFrame->Combines3DTransformWithAncestors(),
6120 "Shouldn't get a delta for an untransformed frame!");
6121 MOZ_ASSERT(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
6123 if (!aFrame->IsTransformed()) {
6124 return false;
6127 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6128 // correctly yet (similar to the aBoundsOverride case for
6129 // GetResultingTransformMatrix)?
6130 nsIFrame* perspectiveFrame =
6131 aFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6132 if (!perspectiveFrame) {
6133 return false;
6136 /* Grab the values for perspective and perspective-origin (if present) */
6137 const nsStyleDisplay* perspectiveDisplay = perspectiveFrame->StyleDisplay();
6138 if (perspectiveDisplay->mChildPerspective.IsNone()) {
6139 return false;
6142 MOZ_ASSERT(perspectiveDisplay->mChildPerspective.IsLength());
6143 float perspective =
6144 perspectiveDisplay->mChildPerspective.AsLength().ToCSSPixels();
6145 perspective = std::max(1.0f, perspective);
6146 if (perspective < std::numeric_limits<Float>::epsilon()) {
6147 return true;
6150 TransformReferenceBox refBox(perspectiveFrame);
6152 Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition(
6153 perspectiveDisplay->mPerspectiveOrigin.horizontal,
6154 perspectiveDisplay->mPerspectiveOrigin.vertical, refBox,
6155 aAppUnitsPerPixel);
6157 /* GetOffsetTo computes the offset required to move from 0,0 in
6158 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6159 * this, it's faster to compute this way.
6161 nsPoint frameToPerspectiveOffset = -aFrame->GetOffsetTo(perspectiveFrame);
6162 Point frameToPerspectiveGfxOffset(
6163 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.x, aAppUnitsPerPixel),
6164 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.y, aAppUnitsPerPixel));
6166 /* Move the perspective origin to be relative to aFrame, instead of relative
6167 * to the containing block which is how it was specified in the style system.
6169 perspectiveOrigin += frameToPerspectiveGfxOffset;
6171 aOutMatrix._34 =
6172 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective),
6173 aAppUnitsPerPixel);
6175 aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
6176 return true;
6179 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6180 const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6181 float aAppUnitsPerPixel)
6182 : mFrame(aFrame),
6183 mTranslate(aFrame->StyleDisplay()->mTranslate),
6184 mRotate(aFrame->StyleDisplay()->mRotate),
6185 mScale(aFrame->StyleDisplay()->mScale),
6186 mTransform(aFrame->StyleDisplay()->mTransform),
6187 mMotion(MotionPathUtils::ResolveMotionPath(aFrame, aRefBox)),
6188 mToTransformOrigin(
6189 GetDeltaToTransformOrigin(aFrame, aRefBox, aAppUnitsPerPixel)) {}
6191 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6192 * translates from local coordinate space to transform coordinate space, then
6193 * hands it back.
6195 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6196 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6197 float aAppUnitsPerPixel) {
6198 return GetResultingTransformMatrixInternal(aProperties, aRefBox, nsPoint(),
6199 aAppUnitsPerPixel, 0);
6202 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6203 const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel,
6204 uint32_t aFlags) {
6205 TransformReferenceBox refBox(aFrame);
6206 FrameTransformProperties props(aFrame, refBox, aAppUnitsPerPixel);
6207 return GetResultingTransformMatrixInternal(props, refBox, aOrigin,
6208 aAppUnitsPerPixel, aFlags);
6211 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal(
6212 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6213 const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags) {
6214 const nsIFrame* frame = aProperties.mFrame;
6215 NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE),
6216 "Must have a frame to compute perspective!");
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, 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 (!mFrame->ChildrenHavePerspective()) {
6662 // If it has perspective, we create a new scroll data via the
6663 // UpdateScrollData call because that scenario is more complex. Otherwise
6664 // we can just stash the transform on the StackingContextHelper and
6665 // apply it to any scroll data that are created inside this
6666 // 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 (!mFrame->ChildrenHavePerspective()) {
6718 // This case is handled in CreateWebRenderCommands by stashing the transform
6719 // on the stacking context.
6720 return false;
6722 if (aLayerData) {
6723 auto matrix = GetTransform().GetMatrix();
6724 if (!mFrame->Combines3DTransformWithAncestors()) {
6725 matrix.ProjectTo2D();
6727 aLayerData->SetTransform(matrix);
6728 aLayerData->SetTransformIsPerspective(true);
6730 return true;
6733 bool nsDisplayTransform::ShouldSkipTransform(
6734 nsDisplayListBuilder* aBuilder) const {
6735 return (aBuilder->RootReferenceFrame() == mFrame) &&
6736 aBuilder->IsForGenerateGlyphMask();
6739 void nsDisplayTransform::Collect3DTransformLeaves(
6740 nsDisplayListBuilder* aBuilder, nsTArray<nsDisplayTransform*>& aLeaves) {
6741 if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6742 aLeaves.AppendElement(this);
6743 return;
6746 FlattenedDisplayListIterator iter(aBuilder, &mChildren);
6747 while (iter.HasNext()) {
6748 nsDisplayItem* item = iter.GetNextItem();
6749 if (item->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
6750 auto* perspective = static_cast<nsDisplayPerspective*>(item);
6751 if (!perspective->GetChildren()->GetTop()) {
6752 continue;
6754 item = perspective->GetChildren()->GetTop();
6756 if (item->GetType() != DisplayItemType::TYPE_TRANSFORM) {
6757 gfxCriticalError() << "Invalid child item within 3D transform of type: "
6758 << item->Name();
6759 continue;
6761 static_cast<nsDisplayTransform*>(item)->Collect3DTransformLeaves(aBuilder,
6762 aLeaves);
6766 static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
6767 const gfx::Polygon& aPolygon) {
6768 MOZ_ASSERT(!aPolygon.IsEmpty());
6770 RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
6771 const nsTArray<Point4D>& points = aPolygon.GetPoints();
6773 pathBuilder->MoveTo(points[0].As2DPoint());
6775 for (size_t i = 1; i < points.Length(); ++i) {
6776 pathBuilder->LineTo(points[i].As2DPoint());
6779 pathBuilder->Close();
6780 return pathBuilder->Finish();
6783 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6784 nsDisplayListBuilder* aBuilder, nsTArray<TransformPolygon>& aLeaves) {
6785 std::list<TransformPolygon> inputLayers;
6787 nsTArray<nsDisplayTransform*> leaves;
6788 Collect3DTransformLeaves(aBuilder, leaves);
6789 for (nsDisplayTransform* item : leaves) {
6790 auto bounds = LayoutDeviceRect::FromAppUnits(
6791 item->mChildBounds, item->mFrame->PresContext()->AppUnitsPerDevPixel());
6792 Matrix4x4 transform = item->GetAccumulatedPreserved3DTransform(aBuilder);
6794 if (!IsFrameVisible(item->mFrame, transform)) {
6795 continue;
6797 gfx::Polygon polygon =
6798 gfx::Polygon::FromRect(gfx::Rect(bounds.ToUnknownRect()));
6800 polygon.TransformToScreenSpace(transform);
6802 if (polygon.GetPoints().Length() >= 3) {
6803 inputLayers.push_back(TransformPolygon(item, std::move(polygon)));
6807 if (inputLayers.empty()) {
6808 return;
6811 BSPTree<nsDisplayTransform> tree(inputLayers);
6812 nsTArray<TransformPolygon> orderedLayers(tree.GetDrawOrder());
6814 for (TransformPolygon& polygon : orderedLayers) {
6815 Matrix4x4 inverse =
6816 polygon.data->GetAccumulatedPreserved3DTransform(aBuilder).Inverse();
6818 MOZ_ASSERT(polygon.geometry);
6819 polygon.geometry->TransformToLayerSpace(inverse);
6822 aLeaves = std::move(orderedLayers);
6825 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder,
6826 gfxContext* aCtx) {
6827 Paint(aBuilder, aCtx, Nothing());
6830 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
6831 const Maybe<gfx::Polygon>& aPolygon) {
6832 if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6833 MOZ_ASSERT(!aPolygon);
6834 nsTArray<TransformPolygon> leaves;
6835 CollectSorted3DTransformLeaves(aBuilder, leaves);
6836 for (TransformPolygon& item : leaves) {
6837 item.data->Paint(aBuilder, aCtx, item.geometry);
6839 return;
6842 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
6843 Matrix4x4 trans = ShouldSkipTransform(aBuilder)
6844 ? Matrix4x4()
6845 : GetAccumulatedPreserved3DTransform(aBuilder);
6846 if (!IsFrameVisible(mFrame, trans)) {
6847 return;
6850 Matrix trans2d;
6851 if (trans.CanDraw2D(&trans2d)) {
6852 aCtx->Multiply(ThebesMatrix(trans2d));
6854 if (aPolygon) {
6855 RefPtr<gfx::Path> path =
6856 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6857 aCtx->GetDrawTarget()->PushClip(path);
6860 GetChildren()->Paint(aBuilder, aCtx,
6861 mFrame->PresContext()->AppUnitsPerDevPixel());
6863 if (aPolygon) {
6864 aCtx->GetDrawTarget()->PopClip();
6866 return;
6869 // TODO: Implement 3d transform handling, including plane splitting and
6870 // sorting. See BasicCompositor.
6871 auto pixelBounds = LayoutDeviceRect::FromAppUnitsToOutside(
6872 mChildBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
6873 RefPtr<DrawTarget> untransformedDT =
6874 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6875 IntSize(pixelBounds.Width(), pixelBounds.Height()),
6876 SurfaceFormat::B8G8R8A8, true);
6877 if (!untransformedDT || !untransformedDT->IsValid()) {
6878 return;
6880 untransformedDT->SetTransform(
6881 Matrix::Translation(-Point(pixelBounds.X(), pixelBounds.Y())));
6883 RefPtr<gfxContext> groupTarget =
6884 gfxContext::CreatePreservingTransformOrNull(untransformedDT);
6886 if (aPolygon) {
6887 RefPtr<gfx::Path> path =
6888 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6889 aCtx->GetDrawTarget()->PushClip(path);
6892 GetChildren()->Paint(aBuilder, groupTarget,
6893 mFrame->PresContext()->AppUnitsPerDevPixel());
6895 if (aPolygon) {
6896 aCtx->GetDrawTarget()->PopClip();
6899 RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
6901 trans.PreTranslate(pixelBounds.X(), pixelBounds.Y(), 0);
6902 aCtx->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf, trans);
6905 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
6906 // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6907 // completely bypass the main thread for this animation, so it is always
6908 // worthwhile.
6909 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6910 // already involved so there is less to be gained.
6911 // Therefore we check that the *post-transform* bounds of this item are
6912 // big enough to justify an active layer.
6913 return EffectCompositor::HasAnimationsForCompositor(
6914 mFrame, DisplayItemType::TYPE_TRANSFORM) ||
6915 (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame));
6918 nsRect nsDisplayTransform::TransformUntransformedBounds(
6919 nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
6920 bool snap;
6921 const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
6922 // GetTransform always operates in dev pixels.
6923 const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
6924 return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
6925 factor);
6929 * Returns the bounds for this transform. The bounds are calculated during
6930 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6932 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
6933 bool* aSnap) const {
6934 *aSnap = false;
6935 return mBounds;
6938 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
6939 MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
6941 /* Some transforms can get empty bounds in 2D, but might get transformed again
6942 * and get non-empty bounds. A simple example of this would be a 180 degree
6943 * rotation getting applied twice.
6944 * We should not depend on transforming bounds level by level.
6946 * This function collects the bounds of this transform and stores it in
6947 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6948 * down and include the bounds of the child transforms.
6949 * The bounds are transformed with the accumulated transformation matrix up to
6950 * the 3D context root coordinate space.
6952 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6953 accTransform.Accumulate(GetTransform().GetMatrix());
6955 // Do not dive into another 3D context.
6956 if (!IsLeafOf3DContext()) {
6957 for (nsDisplayItem* i : *GetChildren()) {
6958 i->DoUpdateBoundsPreserves3D(aBuilder);
6962 /* The child transforms that extend 3D context further will have empty bounds,
6963 * so the untransformed bounds here is the bounds of all the non-preserve-3d
6964 * content under this transform.
6966 const nsRect rect = TransformUntransformedBounds(
6967 aBuilder, accTransform.GetCurrentTransform());
6968 aBuilder->AccumulateRect(rect);
6971 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6972 nsDisplayListBuilder* aBuilder) {
6973 MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
6974 IsTransformSeparator());
6975 // Updating is not going through to child 3D context.
6976 ComputeBounds(aBuilder);
6979 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
6980 UpdateUntransformedBounds(aBuilder);
6982 if (IsTransformSeparator()) {
6983 MOZ_ASSERT(GetTransform().IsIdentity());
6984 mBounds = mChildBounds;
6985 return;
6988 if (mFrame->Extend3DContext()) {
6989 if (!Combines3DTransformWithAncestors()) {
6990 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6991 // collect the bounds from the child transforms.
6992 UpdateBoundsFor3D(aBuilder);
6993 } else {
6994 // With nested 3D transforms, the 2D bounds might not be useful.
6995 mBounds = nsRect();
6998 return;
7001 MOZ_ASSERT(!mFrame->Extend3DContext());
7003 // We would like to avoid calculating 2D bounds here for nested 3D transforms,
7004 // but mix-blend-mode relies on having bounds set. See bug 1556956.
7006 // A stand-alone transform.
7007 mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
7010 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
7011 MOZ_ASSERT(mFrame->Extend3DContext() &&
7012 !mFrame->Combines3DTransformWithAncestors() &&
7013 !IsTransformSeparator());
7015 // Always start updating from an establisher of a 3D rendering context.
7016 nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
7017 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
7018 accTransform.StartRoot();
7019 ComputeBounds(aBuilder);
7020 mBounds = aBuilder->GetAccumulatedRect();
7023 void nsDisplayTransform::UpdateUntransformedBounds(
7024 nsDisplayListBuilder* aBuilder) {
7025 mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
7026 aBuilder, mActiveScrolledRoot);
7029 #ifdef DEBUG_HIT
7030 # include <time.h>
7031 #endif
7033 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
7034 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
7035 const nsRect& aRect, HitTestState* aState,
7036 nsTArray<nsIFrame*>* aOutFrames) {
7037 if (aState->mInPreserves3D) {
7038 GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
7039 return;
7042 /* Here's how this works:
7043 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
7044 * anything).
7045 * 2. Invert the matrix.
7046 * 3. Use it to transform the rect into the correct space.
7047 * 4. Pass that rect down through to the list's version of HitTest.
7049 // GetTransform always operates in dev pixels.
7050 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7051 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7053 if (!IsFrameVisible(mFrame, matrix)) {
7054 return;
7057 const bool oldHitOccludingItem = aState->mHitOccludingItem;
7059 /* We want to go from transformed-space to regular space.
7060 * Thus we have to invert the matrix, which normally does
7061 * the reverse operation (e.g. regular->transformed)
7064 /* Now, apply the transform and pass it down the channel. */
7065 matrix.Invert();
7066 nsRect resultingRect;
7067 // Magic width/height indicating we're hit testing a point, not a rect
7068 const bool testingPoint = aRect.width == 1 && aRect.height == 1;
7069 if (testingPoint) {
7070 Point4D point =
7071 matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
7072 NSAppUnitsToFloatPixels(aRect.y, factor)));
7073 if (!point.HasPositiveWCoord()) {
7074 return;
7077 Point point2d = point.As2DPoint();
7079 resultingRect =
7080 nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
7081 NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1);
7083 } else {
7084 Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
7085 NSAppUnitsToFloatPixels(aRect.y, factor),
7086 NSAppUnitsToFloatPixels(aRect.width, factor),
7087 NSAppUnitsToFloatPixels(aRect.height, factor));
7089 Rect childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds.x, factor),
7090 NSAppUnitsToFloatPixels(mChildBounds.y, factor),
7091 NSAppUnitsToFloatPixels(mChildBounds.width, factor),
7092 NSAppUnitsToFloatPixels(mChildBounds.height, factor));
7094 Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
7096 resultingRect =
7097 nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
7098 NSFloatPixelsToAppUnits(float(rect.Y()), factor),
7099 NSFloatPixelsToAppUnits(float(rect.Width()), factor),
7100 NSFloatPixelsToAppUnits(float(rect.Height()), factor));
7103 if (resultingRect.IsEmpty()) {
7104 return;
7107 #ifdef DEBUG_HIT
7108 printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
7109 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(),
7110 resultingRect.Y());
7111 uint32_t originalFrameCount = aOutFrames.Length();
7112 #endif
7114 GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
7116 if (aState->mHitOccludingItem && !testingPoint &&
7117 !mChildBounds.Contains(aRect)) {
7118 MOZ_ASSERT(aBuilder->HitTestIsForVisibility());
7119 // We're hit-testing a rect that's bigger than our child bounds, but
7120 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so
7121 // we can't stop hit-testing altogether.
7123 // FIXME(emilio): I think this means that theoretically we might include
7124 // some frames fully behind other transformed-but-opaque frames? Then again
7125 // that's our pre-existing behavior for other untransformed content that
7126 // doesn't fill the whole rect. To be fully correct I think we'd need proper
7127 // "known occluded region" tracking, but that might be overkill for our
7128 // purposes here.
7129 aState->mHitOccludingItem = oldHitOccludingItem;
7132 #ifdef DEBUG_HIT
7133 if (originalFrameCount != aOutFrames.Length())
7134 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7135 dynamic_cast<void*>(aOutFrames.ElementAt(0)));
7136 printf("=== end of hit test ===\n");
7137 #endif
7140 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
7141 const nsPoint& aPoint) {
7142 // GetTransform always operates in dev pixels.
7143 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7144 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7146 NS_ASSERTION(IsFrameVisible(mFrame, matrix),
7147 "We can't have hit a frame that isn't visible!");
7149 Matrix4x4 inverse = matrix;
7150 inverse.Invert();
7151 Point4D point =
7152 inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
7153 NSAppUnitsToFloatPixels(aPoint.y, factor)));
7155 Point point2d = point.As2DPoint();
7157 Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
7158 return transformed.z;
7161 /* The transform is opaque iff the transform consists solely of scales and
7162 * translations and if the underlying content is opaque. Thus if the transform
7163 * is of the form
7165 * |a c e|
7166 * |b d f|
7167 * |0 0 1|
7169 * We need b and c to be zero.
7171 * We also need to check whether the underlying opaque content completely fills
7172 * our visible rect. We use UntransformRect which expands to the axis-aligned
7173 * bounding rect, but that's OK since if
7174 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7175 * certainly contains the actual (non-axis-aligned) untransformed rect.
7177 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7178 bool* aSnap) const {
7179 *aSnap = false;
7181 nsRect untransformedVisible;
7182 if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
7183 return nsRegion();
7186 const Matrix4x4Flagged& matrix = GetTransform();
7187 Matrix matrix2d;
7188 if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
7189 return nsRegion();
7192 nsRegion result;
7194 bool tmpSnap;
7195 const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
7196 const nsRegion opaque =
7197 ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
7199 if (opaque.Contains(untransformedVisible)) {
7200 result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
7202 return result;
7205 nsRect nsDisplayTransform::GetComponentAlphaBounds(
7206 nsDisplayListBuilder* aBuilder) const {
7207 if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
7208 return nsRect();
7211 bool snap;
7212 return GetBounds(aBuilder, &snap);
7215 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7216 * the smallest rectangle (in app space) containing the transformed image of
7217 * that rectangle. That is, it takes the four corners of the rectangle,
7218 * transforms them according to the matrix associated with the specified frame,
7219 * then returns the smallest rectangle containing the four transformed points.
7221 * @param aUntransformedBounds The rectangle (in app units) to transform.
7222 * @param aFrame The frame whose transformation should be applied.
7223 * @param aOrigin The delta from the frame origin to the coordinate space origin
7224 * @return The smallest rectangle containing the image of the transformed
7225 * rectangle.
7227 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds,
7228 const nsIFrame* aFrame,
7229 TransformReferenceBox& aRefBox) {
7230 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7232 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7234 FrameTransformProperties props(aFrame, aRefBox, factor);
7235 return nsLayoutUtils::MatrixTransformRect(
7236 aUntransformedBounds,
7237 GetResultingTransformMatrixInternal(props, aRefBox, nsPoint(), factor,
7238 kTransformRectFlags),
7239 factor);
7242 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7243 const nsRect& aChildBounds,
7244 const nsIFrame* aFrame,
7245 nsRect* aOutRect) {
7246 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7248 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7249 Matrix4x4 transform = GetResultingTransformMatrix(aFrame, nsPoint(), factor,
7250 kTransformRectFlags);
7251 return UntransformRect(aTransformedBounds, aChildBounds, transform, factor,
7252 aOutRect);
7255 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7256 const nsRect& aChildBounds,
7257 const Matrix4x4& aMatrix,
7258 float aAppUnitsPerPixel,
7259 nsRect* aOutRect) {
7260 if (aMatrix.IsSingular()) {
7261 return false;
7264 RectDouble result(
7265 NSAppUnitsToFloatPixels(aTransformedBounds.x, aAppUnitsPerPixel),
7266 NSAppUnitsToFloatPixels(aTransformedBounds.y, aAppUnitsPerPixel),
7267 NSAppUnitsToFloatPixels(aTransformedBounds.width, aAppUnitsPerPixel),
7268 NSAppUnitsToFloatPixels(aTransformedBounds.height, aAppUnitsPerPixel));
7270 RectDouble childGfxBounds(
7271 NSAppUnitsToFloatPixels(aChildBounds.x, aAppUnitsPerPixel),
7272 NSAppUnitsToFloatPixels(aChildBounds.y, aAppUnitsPerPixel),
7273 NSAppUnitsToFloatPixels(aChildBounds.width, aAppUnitsPerPixel),
7274 NSAppUnitsToFloatPixels(aChildBounds.height, aAppUnitsPerPixel));
7276 result = aMatrix.Inverse().ProjectRectBounds(result, childGfxBounds);
7277 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result),
7278 aAppUnitsPerPixel);
7279 return true;
7282 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder,
7283 const nsRect& aRect,
7284 nsRect* aOutRect) const {
7285 if (GetTransform().IsSingular()) {
7286 return false;
7289 // GetTransform always operates in dev pixels.
7290 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7291 RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor),
7292 NSAppUnitsToFloatPixels(aRect.y, factor),
7293 NSAppUnitsToFloatPixels(aRect.width, factor),
7294 NSAppUnitsToFloatPixels(aRect.height, factor));
7296 bool snap;
7297 nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
7298 RectDouble childGfxBounds(
7299 NSAppUnitsToFloatPixels(childBounds.x, factor),
7300 NSAppUnitsToFloatPixels(childBounds.y, factor),
7301 NSAppUnitsToFloatPixels(childBounds.width, factor),
7302 NSAppUnitsToFloatPixels(childBounds.height, factor));
7304 /* We want to untransform the matrix, so invert the transformation first! */
7305 result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds);
7307 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
7309 return true;
7312 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) {
7313 aStream << GetTransform().GetMatrix();
7314 if (IsTransformSeparator()) {
7315 aStream << " transform-separator";
7317 if (IsLeafOf3DContext()) {
7318 aStream << " 3d-context-leaf";
7320 if (mFrame->Extend3DContext()) {
7321 aStream << " extends-3d-context";
7323 if (mFrame->Combines3DTransformWithAncestors()) {
7324 aStream << " combines-3d-with-ancestors";
7327 aStream << " prerender(";
7328 switch (mPrerenderDecision) {
7329 case PrerenderDecision::No:
7330 aStream << "no";
7331 break;
7332 case PrerenderDecision::Partial:
7333 aStream << "partial";
7334 break;
7335 case PrerenderDecision::Full:
7336 aStream << "full";
7337 break;
7339 aStream << ")";
7340 aStream << " childrenBuildingRect" << mChildrenBuildingRect;
7343 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
7344 nsIFrame* aFrame,
7345 nsDisplayList* aList)
7346 : nsPaintedDisplayItem(aBuilder, aFrame), mList(aBuilder) {
7347 mList.AppendToTop(aList);
7348 MOZ_ASSERT(mList.Length() == 1);
7349 MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
7352 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder,
7353 gfxContext* aCtx) {
7354 // Just directly recurse into children, since we'll include the persepctive
7355 // value in any nsDisplayTransform children.
7356 GetChildren()->Paint(aBuilder, aCtx,
7357 mFrame->PresContext()->AppUnitsPerDevPixel());
7360 nsRegion nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7361 bool* aSnap) const {
7362 if (!GetChildren()->GetTop()) {
7363 *aSnap = false;
7364 return nsRegion();
7367 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder, aSnap);
7370 bool nsDisplayPerspective::CreateWebRenderCommands(
7371 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7372 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7373 nsDisplayListBuilder* aDisplayListBuilder) {
7374 float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
7375 Matrix4x4 perspectiveMatrix;
7376 DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
7377 mFrame, appUnitsPerPixel, perspectiveMatrix);
7378 MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
7381 * ClipListToRange can remove our child after we were created.
7383 if (!GetChildren()->GetTop()) {
7384 return false;
7388 * The resulting matrix is still in the coordinate space of the transformed
7389 * frame. Append a translation to the reference frame coordinates.
7391 nsDisplayTransform* transform =
7392 static_cast<nsDisplayTransform*>(GetChildren()->GetTop());
7394 Point3D newOrigin =
7395 Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
7396 appUnitsPerPixel),
7397 NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
7398 appUnitsPerPixel),
7399 0.0f);
7400 Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
7402 perspectiveMatrix.PostTranslate(roundedOrigin);
7404 nsIFrame* perspectiveFrame =
7405 mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
7407 // Passing true here is always correct, since perspective always combines
7408 // transforms with the descendants. However that'd make WR do a lot of work
7409 // that it doesn't really need to do if there aren't other transforms forming
7410 // part of the 3D context.
7412 // WR knows how to treat perspective in that case, so the only thing we need
7413 // to do is to ensure we pass true when we're involved in a 3d context in any
7414 // other way via the transform-style property on either the transformed frame
7415 // or the perspective frame in order to not confuse WR's preserve-3d code in
7416 // very awful ways.
7417 bool preserve3D =
7418 mFrame->Extend3DContext() || perspectiveFrame->Extend3DContext();
7420 wr::StackingContextParams params;
7422 wr::WrTransformInfo transform_info;
7423 transform_info.transform = wr::ToLayoutTransform(perspectiveMatrix);
7424 transform_info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
7425 wr::SpatialKeyKind::Perspective);
7426 params.mTransformPtr = &transform_info;
7428 params.reference_frame_kind = wr::WrReferenceFrameKind::Perspective;
7429 params.prim_flags = !BackfaceIsHidden()
7430 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7431 : wr::PrimitiveFlags{0};
7432 params.SetPreserve3D(preserve3D);
7433 params.clip =
7434 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
7436 Maybe<uint64_t> scrollingRelativeTo;
7437 for (const auto* asr = GetActiveScrolledRoot(); asr; asr = asr->mParent) {
7438 // In OOP documents, the root scrollable frame of the in-process root
7439 // document is always active, so using IsAncestorFrameCrossDocInProcess
7440 // should be fine here.
7441 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7442 asr->mScrollableFrame->GetScrolledFrame(), perspectiveFrame)) {
7443 scrollingRelativeTo.emplace(asr->GetViewId());
7444 break;
7448 // We put the perspective reference frame wrapping the transformed frame,
7449 // even though there may be arbitrarily nested scroll frames in between.
7451 // We need to know how many ancestor scroll-frames are we nested in, in order
7452 // for the async scrolling code in WebRender to calculate the right
7453 // transformation for the perspective contents.
7454 params.scrolling_relative_to = scrollingRelativeTo.ptrOr(nullptr);
7456 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
7457 params);
7459 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7460 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
7462 return true;
7465 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
7466 nsTextFrame* aFrame)
7467 : nsPaintedDisplayItem(aBuilder, aFrame),
7468 mVisIStartEdge(0),
7469 mVisIEndEdge(0) {
7470 MOZ_COUNT_CTOR(nsDisplayText);
7471 mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7472 // Bug 748228
7473 mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
7474 mVisibleRect = aBuilder->GetVisibleRect() +
7475 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
7478 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager,
7479 nsDisplayListBuilder* aBuilder) const {
7480 auto* f = static_cast<nsTextFrame*>(mFrame);
7482 if (f->IsSelected()) {
7483 return false;
7486 const nsStyleText* textStyle = f->StyleText();
7487 if (textStyle->HasTextShadow()) {
7488 return false;
7491 nsTextFrame::TextDecorations decorations;
7492 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7493 decorations);
7494 return !decorations.HasDecorationLines();
7497 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
7498 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
7499 // We don't pass mVisibleRect here, since this can be called from within
7500 // the WebRender fallback painting path, and we don't want to issue
7501 // recorded commands that are dependent on the visible/building rect.
7502 RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
7505 bool nsDisplayText::CreateWebRenderCommands(
7506 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7507 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7508 nsDisplayListBuilder* aDisplayListBuilder) {
7509 auto* f = static_cast<nsTextFrame*>(mFrame);
7510 auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
7512 nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
7513 // Bug 748228
7514 bounds.Inflate(appUnitsPerDevPixel);
7516 if (bounds.IsEmpty()) {
7517 return true;
7520 // For large font sizes, punt to a blob image, to avoid the blurry rendering
7521 // that results from WR clamping the glyph size used for rasterization.
7523 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7525 // This is not strictly accurate, as final used font sizes might not be the
7526 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7527 // altering the used size of the font actually used).
7528 // It also fails to consider how transforms might affect the device-font-size
7529 // that webrender uses (and clamps).
7530 // But it should be near enough for practical purposes; the limitations just
7531 // mean we might sometimes end up with webrender still applying some bitmap
7532 // scaling, or bail out when we didn't really need to.
7533 constexpr float kWebRenderFontSizeLimit = 320.0;
7534 f->EnsureTextRun(nsTextFrame::eInflated);
7535 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7536 if (textRun &&
7537 textRun->GetFontGroup()->GetStyle()->size > kWebRenderFontSizeLimit) {
7538 return false;
7541 gfx::Point deviceOffset =
7542 LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
7543 .ToUnknownPoint();
7545 // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7546 // frames) let's us early reject a bunch of things, but it can produce
7547 // incorrect results for shadows, because they can translate things back into
7548 // view. Also if we're selected we might have some shadows from the
7549 // ::selected and ::inctive-selected pseudo-selectors. So don't do this
7550 // optimization if we have shadows or a selection.
7551 if (!(f->IsSelected() || f->StyleText()->HasTextShadow())) {
7552 nsRect visible = mVisibleRect;
7553 visible.Inflate(3 * appUnitsPerDevPixel);
7554 bounds = bounds.Intersect(visible);
7557 RefPtr<gfxContext> textDrawer = aBuilder.GetTextContext(
7558 aResources, aSc, aManager, this, bounds, deviceOffset);
7560 aBuilder.StartGroup(this);
7562 RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
7563 aBuilder.GetInheritedOpacity(), true);
7564 const bool result = textDrawer->GetTextDrawer()->Finish();
7566 if (result) {
7567 aBuilder.FinishGroup();
7568 } else {
7569 aBuilder.CancelGroup(true);
7572 return result;
7575 void nsDisplayText::RenderToContext(gfxContext* aCtx,
7576 nsDisplayListBuilder* aBuilder,
7577 const nsRect& aVisibleRect, float aOpacity,
7578 bool aIsRecording) {
7579 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7581 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7582 // antialiased pixels beyond the measured text extents.
7583 // This is temporary until we do this in the actual calculation of text
7584 // extents.
7585 auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
7586 LayoutDeviceRect extraVisible =
7587 LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D);
7588 extraVisible.Inflate(1);
7590 gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width,
7591 extraVisible.height);
7592 pixelVisible.Inflate(2);
7593 pixelVisible.RoundOut();
7595 bool willClip = !aBuilder->IsForGenerateGlyphMask() && !aIsRecording;
7596 if (willClip) {
7597 aCtx->Clip(pixelVisible);
7600 NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
7601 NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
7603 gfxContextMatrixAutoSaveRestore matrixSR;
7605 nsPoint framePt = ToReferenceFrame();
7606 if (f->Style()->IsTextCombined()) {
7607 float scaleFactor = nsTextFrame::GetTextCombineScaleFactor(f);
7608 if (scaleFactor != 1.0f) {
7609 if (auto* textDrawer = aCtx->GetTextDrawer()) {
7610 // WebRender doesn't support scaling text like this yet
7611 textDrawer->FoundUnsupportedFeature();
7612 return;
7614 matrixSR.SetContext(aCtx);
7615 // Setup matrix to compress text for text-combine-upright if
7616 // necessary. This is done here because we want selection be
7617 // compressed at the same time as text.
7618 gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
7619 gfxMatrix mat = aCtx->CurrentMatrixDouble()
7620 .PreTranslate(pt)
7621 .PreScale(scaleFactor, 1.0)
7622 .PreTranslate(-pt);
7623 aCtx->SetMatrixDouble(mat);
7626 nsTextFrame::PaintTextParams params(aCtx);
7627 params.framePt = gfx::Point(framePt.x, framePt.y);
7628 params.dirtyRect = extraVisible;
7630 if (aBuilder->IsForGenerateGlyphMask()) {
7631 params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
7632 } else {
7633 params.state = nsTextFrame::PaintTextParams::PaintText;
7636 f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
7637 f->IsSelected(), aOpacity);
7639 if (willClip) {
7640 aCtx->PopClip();
7644 // This could go to nsDisplayListInvalidation.h, but
7645 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7646 // would produce circular dependencies.
7647 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
7648 public:
7649 nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
7650 : nsDisplayItemGenericGeometry(aItem, aBuilder),
7651 mVisIStartEdge(aItem->VisIStartEdge()),
7652 mVisIEndEdge(aItem->VisIEndEdge()) {
7653 nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
7654 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7655 mDecorations);
7659 * We store the computed text decorations here since they are
7660 * computed using style data from parent frames. Any changes to these
7661 * styles will only invalidate the parent frame and not this frame.
7663 nsTextFrame::TextDecorations mDecorations;
7664 nscoord mVisIStartEdge;
7665 nscoord mVisIEndEdge;
7668 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry(
7669 nsDisplayListBuilder* aBuilder) {
7670 return new nsDisplayTextGeometry(this, aBuilder);
7673 void nsDisplayText::ComputeInvalidationRegion(
7674 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7675 nsRegion* aInvalidRegion) const {
7676 const nsDisplayTextGeometry* geometry =
7677 static_cast<const nsDisplayTextGeometry*>(aGeometry);
7678 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7680 nsTextFrame::TextDecorations decorations;
7681 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7682 decorations);
7684 bool snap;
7685 const nsRect& newRect = geometry->mBounds;
7686 nsRect oldRect = GetBounds(aBuilder, &snap);
7687 if (decorations != geometry->mDecorations ||
7688 mVisIStartEdge != geometry->mVisIStartEdge ||
7689 mVisIEndEdge != geometry->mVisIEndEdge ||
7690 !oldRect.IsEqualInterior(newRect) ||
7691 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
7692 aInvalidRegion->Or(oldRect, newRect);
7696 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) {
7697 #ifdef DEBUG
7698 aStream << " (\"";
7700 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7701 nsCString buf;
7702 f->ToCString(buf);
7704 aStream << buf.get() << "\")";
7705 #endif
7708 nsDisplayEffectsBase::nsDisplayEffectsBase(
7709 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7710 const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
7711 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
7712 aClearClipChain) {
7713 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7716 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
7717 nsIFrame* aFrame,
7718 nsDisplayList* aList)
7719 : nsDisplayWrapList(aBuilder, aFrame, aList) {
7720 MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7723 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7724 bool* aSnap) const {
7725 *aSnap = false;
7726 return nsRegion();
7729 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder* aBuilder,
7730 const nsRect& aRect, HitTestState* aState,
7731 nsTArray<nsIFrame*>* aOutFrames) {
7732 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
7733 if (SVGIntegrationUtils::HitTestFrameForEffects(
7734 mFrame, rectCenter - ToReferenceFrame())) {
7735 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
7739 gfxRect nsDisplayEffectsBase::BBoxInUserSpace() const {
7740 return SVGUtils::GetBBox(mFrame);
7743 gfxPoint nsDisplayEffectsBase::UserSpaceOffset() const {
7744 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
7747 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7748 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7749 nsRegion* aInvalidRegion) const {
7750 const auto* geometry =
7751 static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
7752 bool snap;
7753 nsRect bounds = GetBounds(aBuilder, &snap);
7754 if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
7755 geometry->mUserSpaceOffset != UserSpaceOffset() ||
7756 !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
7757 // Filter and mask output can depend on the location of the frame's user
7758 // space and on the frame's BBox. We need to invalidate if either of these
7759 // change relative to the reference frame.
7760 // Invalidations from our inactive layer manager are not enough to catch
7761 // some of these cases because filters can produce output even if there's
7762 // nothing in the filter input.
7763 aInvalidRegion->Or(bounds, geometry->mBounds);
7767 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7768 const nsIContent* content = mFrame->GetContent();
7769 bool hasSVGLayout = mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT);
7770 if (hasSVGLayout) {
7771 ISVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame);
7772 if (!svgFrame || !mFrame->GetContent()->IsSVGElement()) {
7773 NS_ASSERTION(false, "why?");
7774 return false;
7776 if (!static_cast<const SVGElement*>(content)->HasValidDimensions()) {
7777 return false; // The SVG spec says not to draw filters for this
7781 return true;
7784 using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams;
7786 static void ComputeMaskGeometry(PaintFramesParams& aParams) {
7787 // Properties are added lazily and may have been removed by a restyle, so
7788 // make sure all applicable ones are set again.
7789 nsIFrame* firstFrame =
7790 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
7792 const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
7794 nsTArray<SVGMaskFrame*> maskFrames;
7795 // XXX check return value?
7796 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
7798 if (maskFrames.Length() == 0) {
7799 return;
7802 gfxContext& ctx = aParams.ctx;
7803 nsIFrame* frame = aParams.frame;
7805 nsPoint offsetToUserSpace =
7806 nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame);
7808 auto cssToDevScale = frame->PresContext()->CSSToDevPixelScale();
7809 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
7811 gfxPoint devPixelOffsetToUserSpace =
7812 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, appUnitsPerDevPixel);
7814 gfxContextMatrixAutoSaveRestore matSR(&ctx);
7815 ctx.SetMatrixDouble(
7816 ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace));
7818 // Convert boaderArea and dirtyRect to user space.
7819 nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
7820 nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
7822 // Union all mask layer rectangles in user space.
7823 LayoutDeviceRect maskInUserSpace;
7824 for (size_t i = 0; i < maskFrames.Length(); i++) {
7825 SVGMaskFrame* maskFrame = maskFrames[i];
7826 LayoutDeviceRect currentMaskSurfaceRect;
7828 if (maskFrame) {
7829 auto rect = maskFrame->GetMaskArea(aParams.frame);
7830 currentMaskSurfaceRect =
7831 CSSRect::FromUnknownRect(ToRect(rect)) * cssToDevScale;
7832 } else {
7833 nsCSSRendering::ImageLayerClipState clipState;
7834 nsCSSRendering::GetImageLayerClip(
7835 svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(),
7836 userSpaceBorderArea, userSpaceDirtyRect, false, /* aWillPaintBorder */
7837 appUnitsPerDevPixel, &clipState);
7838 currentMaskSurfaceRect = LayoutDeviceRect::FromUnknownRect(
7839 ToRect(clipState.mDirtyRectInDevPx));
7842 maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
7845 if (!maskInUserSpace.IsEmpty()) {
7846 aParams.maskRect = Some(maskInUserSpace);
7847 } else {
7848 aParams.maskRect = Nothing();
7852 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7853 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7854 const ActiveScrolledRoot* aActiveScrolledRoot, bool aWrapsBackdropFilter)
7855 : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
7856 mWrapsBackdropFilter(aWrapsBackdropFilter) {
7857 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
7859 nsPresContext* presContext = mFrame->PresContext();
7860 uint32_t flags =
7861 aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
7862 const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
7863 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7864 const auto& layer = svgReset->mMask.mLayers[i];
7865 if (!layer.mImage.IsResolved()) {
7866 continue;
7868 const nsRect& borderArea = mFrame->GetRectRelativeToSelf();
7869 // NOTE(emilio): We only care about the dest rect so we don't bother
7870 // computing a clip.
7871 bool isTransformedFixed = false;
7872 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
7873 presContext, aFrame, flags, borderArea, borderArea, layer,
7874 &isTransformedFixed);
7875 mDestRects.AppendElement(state.mDestArea);
7879 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) {
7880 // Do not merge items for box-decoration-break:clone elements,
7881 // since each box should have its own mask in that case.
7882 if (aFrame->StyleBorder()->mBoxDecorationBreak ==
7883 StyleBoxDecorationBreak::Clone) {
7884 return false;
7887 // Do not merge if either frame has a mask. Continuation frames should apply
7888 // the mask independently (just like nsDisplayBackgroundImage).
7889 if (aFrame->StyleSVGReset()->HasMask()) {
7890 return false;
7893 return true;
7896 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem* aItem) const {
7897 // Items for the same content element should be merged into a single
7898 // compositing group.
7899 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
7900 !HasSameContent(aItem)) {
7901 return false;
7904 return CanMergeDisplayMaskFrame(mFrame) &&
7905 CanMergeDisplayMaskFrame(aItem->Frame());
7908 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7909 if (!ValidateSVGFrame()) {
7910 return false;
7913 nsIFrame* firstFrame =
7914 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
7916 return !(SVGObserverUtils::GetAndObserveClipPath(firstFrame, nullptr) ==
7917 SVGObserverUtils::eHasRefsSomeInvalid ||
7918 SVGObserverUtils::GetAndObserveMasks(firstFrame, nullptr) ==
7919 SVGObserverUtils::eHasRefsSomeInvalid);
7922 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder,
7923 gfxContext* aMaskContext,
7924 bool aHandleOpacity,
7925 bool* aMaskPainted) {
7926 MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
7928 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7929 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7930 SVGIntegrationUtils::PaintFramesParams params(*aMaskContext, mFrame, mBounds,
7931 borderArea, aBuilder,
7932 aHandleOpacity, imgParams);
7933 ComputeMaskGeometry(params);
7934 bool maskIsComplete = false;
7935 bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete);
7936 if (aMaskPainted) {
7937 *aMaskPainted = painted;
7940 nsDisplayMasksAndClipPathsGeometry::UpdateDrawResult(this, imgParams.result);
7942 return maskIsComplete &&
7943 (imgParams.result == ImgDrawResult::SUCCESS ||
7944 imgParams.result == ImgDrawResult::SUCCESS_NOT_COMPLETE ||
7945 imgParams.result == ImgDrawResult::WRONG_SIZE);
7948 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7949 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7950 nsRegion* aInvalidRegion) const {
7951 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
7952 aInvalidRegion);
7954 const auto* geometry =
7955 static_cast<const nsDisplayMasksAndClipPathsGeometry*>(aGeometry);
7956 bool snap;
7957 nsRect bounds = GetBounds(aBuilder, &snap);
7959 if (mDestRects.Length() != geometry->mDestRects.Length()) {
7960 aInvalidRegion->Or(bounds, geometry->mBounds);
7961 } else {
7962 for (size_t i = 0; i < mDestRects.Length(); i++) {
7963 if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) {
7964 aInvalidRegion->Or(bounds, geometry->mBounds);
7965 break;
7970 if (aBuilder->ShouldSyncDecodeImages() &&
7971 geometry->ShouldInvalidateToSyncDecodeImages()) {
7972 const nsStyleSVGReset* svgReset = mFrame->StyleSVGReset();
7973 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7974 const auto& image = svgReset->mMask.mLayers[i].mImage;
7975 if (image.IsImageRequestType()) {
7976 aInvalidRegion->Or(*aInvalidRegion, bounds);
7977 break;
7983 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7984 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
7985 const std::function<void()>& aPaintChildren) {
7986 // Clip the drawing target by mVisibleRect, which contains the visible
7987 // region of the target frame and its out-of-flow and inflow descendants.
7988 gfxContext* context = aCtx;
7990 Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx),
7991 mFrame->PresContext()->AppUnitsPerDevPixel());
7992 bounds.RoundOut();
7993 context->Clip(bounds);
7995 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7996 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7997 SVGIntegrationUtils::PaintFramesParams params(
7998 *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), borderArea, aBuilder, false,
7999 imgParams);
8001 ComputeMaskGeometry(params);
8003 SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren);
8005 context->PopClip();
8007 nsDisplayMasksAndClipPathsGeometry::UpdateDrawResult(this, imgParams.result);
8010 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder* aBuilder,
8011 gfxContext* aCtx) {
8012 if (!IsValidMask()) {
8013 return;
8015 PaintWithContentsPaintCallback(aBuilder, aCtx, [&] {
8016 GetChildren()->Paint(aBuilder, aCtx,
8017 mFrame->PresContext()->AppUnitsPerDevPixel());
8021 static Maybe<wr::WrClipId> CreateSimpleClipRegion(
8022 const nsDisplayMasksAndClipPaths& aDisplayItem,
8023 wr::DisplayListBuilder& aBuilder) {
8024 nsIFrame* frame = aDisplayItem.Frame();
8025 const auto* style = frame->StyleSVGReset();
8026 MOZ_ASSERT(style->HasClipPath() || style->HasMask());
8027 if (!SVGIntegrationUtils::UsingSimpleClipPathForFrame(frame)) {
8028 return Nothing();
8031 const auto& clipPath = style->mClipPath;
8032 const auto& shape = *clipPath.AsShape()._0;
8034 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8035 const nsRect refBox =
8036 nsLayoutUtils::ComputeGeometryBox(frame, clipPath.AsShape()._1);
8038 wr::WrClipId clipId{};
8040 switch (shape.tag) {
8041 case StyleBasicShape::Tag::Inset: {
8042 const nsRect insetRect = ShapeUtils::ComputeInsetRect(shape, refBox) +
8043 aDisplayItem.ToReferenceFrame();
8045 nscoord radii[8] = {0};
8047 if (ShapeUtils::ComputeInsetRadii(shape, refBox, radii)) {
8048 clipId = aBuilder.DefineRoundedRectClip(
8049 Nothing(),
8050 wr::ToComplexClipRegion(insetRect, radii, appUnitsPerDevPixel));
8051 } else {
8052 clipId = aBuilder.DefineRectClip(
8053 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8054 insetRect, appUnitsPerDevPixel)));
8057 break;
8059 case StyleBasicShape::Tag::Ellipse:
8060 case StyleBasicShape::Tag::Circle: {
8061 nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox);
8063 nsSize radii;
8064 if (shape.IsEllipse()) {
8065 radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox);
8066 } else {
8067 nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox);
8068 radii = {radius, radius};
8071 nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center -
8072 nsPoint(radii.width, radii.height),
8073 radii * 2);
8075 nscoord ellipseRadii[8];
8076 for (const auto corner : AllPhysicalHalfCorners()) {
8077 ellipseRadii[corner] =
8078 HalfCornerIsX(corner) ? radii.width : radii.height;
8081 clipId = aBuilder.DefineRoundedRectClip(
8082 Nothing(), wr::ToComplexClipRegion(ellipseRect, ellipseRadii,
8083 appUnitsPerDevPixel));
8085 break;
8087 default:
8088 // Please don't add more exceptions, try to find a way to define the clip
8089 // without using a mask image.
8091 // And if you _really really_ need to add an exception, add it to
8092 // SVGIntegrationUtils::UsingSimpleClipPathForFrame
8093 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8094 return Nothing();
8097 return Some(clipId);
8100 static void FillPolygonDataForDisplayItem(
8101 const nsDisplayMasksAndClipPaths& aDisplayItem,
8102 nsTArray<wr::LayoutPoint>& aPoints, wr::FillRule& aFillRule) {
8103 nsIFrame* frame = aDisplayItem.Frame();
8104 const auto* style = frame->StyleSVGReset();
8105 bool isPolygon = style->HasClipPath() && style->mClipPath.IsShape() &&
8106 style->mClipPath.AsShape()._0->IsPolygon();
8107 if (!isPolygon) {
8108 return;
8111 const auto& clipPath = style->mClipPath;
8112 const auto& shape = *clipPath.AsShape()._0;
8113 const nsRect refBox =
8114 nsLayoutUtils::ComputeGeometryBox(frame, clipPath.AsShape()._1);
8116 // We only fill polygon data for polygons that are below a complexity
8117 // limit.
8118 nsTArray<nsPoint> vertices =
8119 ShapeUtils::ComputePolygonVertices(shape, refBox);
8120 if (vertices.Length() > wr::POLYGON_CLIP_VERTEX_MAX) {
8121 return;
8124 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8126 for (size_t i = 0; i < vertices.Length(); ++i) {
8127 wr::LayoutPoint point = wr::ToLayoutPoint(
8128 LayoutDevicePoint::FromAppUnits(vertices[i], appUnitsPerDevPixel));
8129 aPoints.AppendElement(point);
8132 aFillRule = (shape.AsPolygon().fill == StyleFillRule::Nonzero)
8133 ? wr::FillRule::Nonzero
8134 : wr::FillRule::Evenodd;
8137 static Maybe<wr::WrClipId> CreateWRClipPathAndMasks(
8138 nsDisplayMasksAndClipPaths* aDisplayItem, const LayoutDeviceRect& aBounds,
8139 wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder,
8140 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8141 nsDisplayListBuilder* aDisplayListBuilder) {
8142 if (auto clip = CreateSimpleClipRegion(*aDisplayItem, aBuilder)) {
8143 return clip;
8146 Maybe<wr::ImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(
8147 aDisplayItem, aBuilder, aResources, aSc, aDisplayListBuilder, aBounds);
8148 if (!mask) {
8149 return Nothing();
8152 // We couldn't create a simple clip region, but before we create an image
8153 // mask clip, see if we can get a polygon clip to add to it.
8154 nsTArray<wr::LayoutPoint> points;
8155 wr::FillRule fillRule = wr::FillRule::Nonzero;
8156 FillPolygonDataForDisplayItem(*aDisplayItem, points, fillRule);
8158 wr::WrClipId clipId =
8159 aBuilder.DefineImageMaskClip(mask.ref(), points, fillRule);
8161 return Some(clipId);
8164 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8165 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8166 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8167 nsDisplayListBuilder* aDisplayListBuilder) {
8168 bool snap;
8169 auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8170 nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
8171 LayoutDeviceRect bounds =
8172 LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel);
8174 Maybe<wr::WrClipId> clip = CreateWRClipPathAndMasks(
8175 this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder);
8177 float oldOpacity = aBuilder.GetInheritedOpacity();
8179 Maybe<StackingContextHelper> layer;
8180 const StackingContextHelper* sc = &aSc;
8181 if (clip) {
8182 // Create a new stacking context to attach the mask to, ensuring the mask is
8183 // applied to the aggregate, and not the individual elements.
8185 // The stacking context shouldn't have any offset.
8186 bounds.MoveTo(0, 0);
8188 Maybe<float> opacity =
8189 (SVGIntegrationUtils::UsingSimpleClipPathForFrame(mFrame) &&
8190 aBuilder.GetInheritedOpacity() != 1.0f)
8191 ? Some(aBuilder.GetInheritedOpacity())
8192 : Nothing();
8194 wr::StackingContextParams params;
8195 params.clip = wr::WrStackingContextClip::ClipId(*clip);
8196 params.opacity = opacity.ptrOr(nullptr);
8197 if (mWrapsBackdropFilter) {
8198 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8200 layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params,
8201 bounds);
8202 sc = layer.ptr();
8205 aBuilder.SetInheritedOpacity(1.0f);
8206 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8207 aBuilder.SetInheritedClipChain(nullptr);
8208 CreateWebRenderCommandsNewClipListOption(aBuilder, aResources, *sc, aManager,
8209 aDisplayListBuilder, layer.isSome());
8210 aBuilder.SetInheritedOpacity(oldOpacity);
8211 aBuilder.SetInheritedClipChain(oldClipChain);
8213 return true;
8216 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8217 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
8218 if (const DisplayItemClip* clip =
8219 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
8220 return Some(clip->GetClipRect());
8222 // This item does not have a clip with respect to |aASR|. However, we
8223 // might still have finite bounds with respect to |aASR|. Check our
8224 // children.
8225 nsDisplayList* childList = GetSameCoordinateSystemChildren();
8226 if (childList) {
8227 return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR));
8229 #ifdef DEBUG
8230 MOZ_ASSERT(false, "item should have finite clip with respect to aASR");
8231 #endif
8232 return Nothing();
8235 #ifdef MOZ_DUMP_PAINTING
8236 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) {
8237 nsIFrame* firstFrame =
8238 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8239 bool first = true;
8240 aTo += " effects=(";
8241 SVGClipPathFrame* clipPathFrame;
8242 // XXX Check return value?
8243 SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
8244 if (clipPathFrame) {
8245 if (!first) {
8246 aTo += ", ";
8248 aTo += nsPrintfCString(
8249 "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
8250 first = false;
8251 } else if (mFrame->StyleSVGReset()->HasClipPath()) {
8252 if (!first) {
8253 aTo += ", ";
8255 aTo += "clip(basic-shape)";
8256 first = false;
8259 nsTArray<SVGMaskFrame*> masks;
8260 // XXX check return value?
8261 SVGObserverUtils::GetAndObserveMasks(firstFrame, &masks);
8262 if (!masks.IsEmpty() && masks[0]) {
8263 if (!first) {
8264 aTo += ", ";
8266 aTo += "mask";
8268 aTo += ")";
8270 #endif
8272 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8273 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8274 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8275 nsDisplayListBuilder* aDisplayListBuilder) {
8276 WrFiltersHolder wrFilters;
8277 Maybe<nsRect> filterClip;
8278 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
8279 auto filterChain = style.StyleEffects()->mBackdropFilters.AsSpan();
8280 bool initialized = true;
8281 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8282 wrFilters) &&
8283 !SVGIntegrationUtils::BuildWebRenderFilters(
8284 mFrame, filterChain, wrFilters, filterClip, initialized)) {
8285 return false;
8288 if (!initialized) {
8289 // draw nothing
8290 return true;
8293 nsCSSRendering::ImageLayerClipState clip;
8294 nsCSSRendering::GetImageLayerClip(
8295 style.StyleBackground()->BottomLayer(), mFrame, *style.StyleBorder(),
8296 mBackdropRect, mBackdropRect, false,
8297 mFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
8299 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
8300 mBackdropRect, mFrame->PresContext()->AppUnitsPerDevPixel());
8302 wr::ComplexClipRegion region =
8303 wr::ToComplexClipRegion(clip.mBGClipArea, clip.mRadii,
8304 mFrame->PresContext()->AppUnitsPerDevPixel());
8306 aBuilder.PushBackdropFilter(wr::ToLayoutRect(bounds), region,
8307 wrFilters.filters, wrFilters.filter_datas,
8308 !BackfaceIsHidden());
8310 wr::StackingContextParams params;
8311 params.clip =
8312 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8313 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8314 params);
8316 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8317 aDisplayListBuilder);
8318 return true;
8321 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder* aBuilder,
8322 gfxContext* aCtx) {
8323 // TODO: Implement backdrop filters
8324 GetChildren()->Paint(aBuilder, aCtx,
8325 mFrame->PresContext()->AppUnitsPerDevPixel());
8328 nsRect nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder* aBuilder,
8329 bool* aSnap) const {
8330 nsRect childBounds = nsDisplayWrapList::GetBounds(aBuilder, aSnap);
8332 *aSnap = false;
8334 return mBackdropRect.Union(childBounds);
8337 /* static */
8338 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder,
8339 nsIFrame* aFrame, nsDisplayList* aList,
8340 nsIFrame* aStyleFrame,
8341 bool aWrapsBackdropFilter)
8342 : nsDisplayEffectsBase(aBuilder, aFrame, aList),
8343 mStyle(aFrame == aStyleFrame ? nullptr : aStyleFrame->Style()),
8344 mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()),
8345 mWrapsBackdropFilter(aWrapsBackdropFilter) {
8346 MOZ_COUNT_CTOR(nsDisplayFilters);
8347 mVisibleRect = aBuilder->GetVisibleRect() +
8348 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
8351 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8352 PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) {
8353 GetChildren()->Paint(aBuilder, aContext,
8354 mFrame->PresContext()->AppUnitsPerDevPixel());
8358 void nsDisplayFilters::ComputeInvalidationRegion(
8359 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
8360 nsRegion* aInvalidRegion) const {
8361 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
8362 aInvalidRegion);
8364 const auto* geometry =
8365 static_cast<const nsDisplayFiltersGeometry*>(aGeometry);
8366 if (aBuilder->ShouldSyncDecodeImages() &&
8367 geometry->ShouldInvalidateToSyncDecodeImages()) {
8368 bool snap;
8369 nsRect bounds = GetBounds(aBuilder, &snap);
8370 aInvalidRegion->Or(*aInvalidRegion, bounds);
8374 void nsDisplayFilters::PaintWithContentsPaintCallback(
8375 nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
8376 const std::function<void(gfxContext* aContext)>& aPaintChildren) {
8377 MOZ_ASSERT(!mStyle, "Shouldn't get to this code path on the root");
8378 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
8379 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8380 SVGIntegrationUtils::PaintFramesParams params(
8381 *aCtx, mFrame, mVisibleRect, borderArea, aBuilder, false, imgParams);
8383 gfxPoint userSpaceToFrameSpaceOffset =
8384 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params);
8386 SVGIntegrationUtils::PaintFilter(
8387 params,
8388 [&](gfxContext& aContext, nsIFrame* aTarget, const gfxMatrix& aTransform,
8389 const nsIntRect* aDirtyRect, imgDrawingParams& aImgParams) {
8390 gfxContextMatrixAutoSaveRestore autoSR(&aContext);
8391 aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(
8392 -userSpaceToFrameSpaceOffset));
8393 aPaintChildren(&aContext);
8395 nsDisplayFiltersGeometry::UpdateDrawResult(this, imgParams.result);
8398 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8399 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame);
8402 bool nsDisplayFilters::CreateWebRenderCommands(
8403 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8404 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8405 nsDisplayListBuilder* aDisplayListBuilder) {
8406 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8408 WrFiltersHolder wrFilters;
8409 Maybe<nsRect> filterClip;
8410 bool initialized = true;
8411 auto filterChain = mStyle ? mStyle->StyleEffects()->mFilters.AsSpan()
8412 : mFrame->StyleEffects()->mFilters.AsSpan();
8413 if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8414 wrFilters) &&
8415 !SVGIntegrationUtils::BuildWebRenderFilters(
8416 mFrame, filterChain, wrFilters, filterClip, initialized)) {
8417 if (mStyle) {
8418 // TODO(bug 1769223): Support fallback filters in the root code-path.
8419 return true;
8421 return false;
8424 if (!initialized) {
8425 // draw nothing
8426 return true;
8429 wr::WrStackingContextClip clip{};
8430 if (filterClip) {
8431 auto devPxRect = LayoutDeviceRect::FromAppUnits(
8432 filterClip.value() + ToReferenceFrame(), auPerDevPixel);
8433 wr::WrClipId clipId =
8434 aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect));
8435 clip = wr::WrStackingContextClip::ClipId(clipId);
8436 } else {
8437 clip = wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8440 float opacity = aBuilder.GetInheritedOpacity();
8441 aBuilder.SetInheritedOpacity(1.0f);
8442 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8443 aBuilder.SetInheritedClipChain(nullptr);
8444 wr::StackingContextParams params;
8445 params.mFilters = std::move(wrFilters.filters);
8446 params.mFilterDatas = std::move(wrFilters.filter_datas);
8447 params.opacity = opacity != 1.0f ? &opacity : nullptr;
8448 params.clip = clip;
8449 if (mWrapsBackdropFilter) {
8450 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER;
8452 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8453 params);
8455 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
8456 aManager, aDisplayListBuilder);
8457 aBuilder.SetInheritedOpacity(opacity);
8458 aBuilder.SetInheritedClipChain(oldClipChain);
8460 return true;
8463 #ifdef MOZ_DUMP_PAINTING
8464 void nsDisplayFilters::PrintEffects(nsACString& aTo) {
8465 nsIFrame* firstFrame =
8466 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8467 bool first = true;
8468 aTo += " effects=(";
8469 // We may exist for a mix of CSS filter functions and/or references to SVG
8470 // filters. If we have invalid references to SVG filters then we paint
8471 // nothing, but otherwise we will apply one or more filters.
8472 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) !=
8473 SVGObserverUtils::eHasRefsSomeInvalid) {
8474 if (!first) {
8475 aTo += ", ";
8477 aTo += "filter";
8479 aTo += ")";
8481 #endif
8483 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder,
8484 nsIFrame* aFrame, nsDisplayList* aList)
8485 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8486 MOZ_COUNT_CTOR(nsDisplaySVGWrapper);
8489 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8490 return !aBuilder->GetWidgetLayerManager();
8493 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8494 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8495 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8496 nsDisplayListBuilder* aDisplayListBuilder) {
8497 return CreateWebRenderCommandsNewClipListOption(
8498 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8501 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder* aBuilder,
8502 nsIFrame* aFrame,
8503 nsDisplayList* aList)
8504 : nsDisplayWrapList(aBuilder, aFrame, aList) {
8505 MOZ_COUNT_CTOR(nsDisplayForeignObject);
8508 #ifdef NS_BUILD_REFCNT_LOGGING
8509 nsDisplayForeignObject::~nsDisplayForeignObject() {
8510 MOZ_COUNT_DTOR(nsDisplayForeignObject);
8512 #endif
8514 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8515 return !aBuilder->GetWidgetLayerManager();
8518 bool nsDisplayForeignObject::CreateWebRenderCommands(
8519 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8520 const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8521 nsDisplayListBuilder* aDisplayListBuilder) {
8522 AutoRestore<bool> restoreDoGrouping(aManager->CommandBuilder().mDoGrouping);
8523 aManager->CommandBuilder().mDoGrouping = false;
8524 return CreateWebRenderCommandsNewClipListOption(
8525 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8528 void nsDisplayLink::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8529 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8530 aCtx->GetDrawTarget()->Link(
8531 mLinkSpec.get(), NSRectToRect(GetPaintRect(aBuilder, aCtx), appPerDev));
8534 void nsDisplayDestination::Paint(nsDisplayListBuilder* aBuilder,
8535 gfxContext* aCtx) {
8536 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8537 aCtx->GetDrawTarget()->Destination(
8538 mDestinationName.get(),
8539 NSPointToPoint(GetPaintRect(aBuilder, aCtx).TopLeft(), appPerDev));
8542 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8543 nsDisplayList* aOutResultList, nsIContent* aContent) {
8544 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already
8545 // in content document order and SortByZOrder is a stable sort which
8546 // guarantees that boxes produced by the same element are placed together
8547 // in the sort. Consider a position:relative inline element that breaks
8548 // across lines and has absolutely positioned children; all the abs-pos
8549 // children should be z-ordered after all the boxes for the position:relative
8550 // element itself.
8551 PositionedDescendants()->SortByZOrder();
8553 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8554 // 1,2: backgrounds and borders
8555 aOutResultList->AppendToTop(BorderBackground());
8556 // 3: negative z-index children.
8557 for (auto* item : PositionedDescendants()->TakeItems()) {
8558 if (item->ZIndex() < 0) {
8559 aOutResultList->AppendToTop(item);
8560 } else {
8561 PositionedDescendants()->AppendToTop(item);
8565 // 4: block backgrounds
8566 aOutResultList->AppendToTop(BlockBorderBackgrounds());
8567 // 5: floats
8568 aOutResultList->AppendToTop(Floats());
8569 // 7: general content
8570 aOutResultList->AppendToTop(Content());
8571 // 7.5: outlines, in content tree order. We need to sort by content order
8572 // because an element with outline that breaks and has children with outline
8573 // might have placed child outline items between its own outline items.
8574 // The element's outline items need to all come before any child outline
8575 // items.
8576 if (aContent) {
8577 Outlines()->SortByContentOrder(aContent);
8579 aOutResultList->AppendToTop(Outlines());
8580 // 8, 9: non-negative z-index children
8581 aOutResultList->AppendToTop(PositionedDescendants());
8584 uint32_t PaintTelemetry::sPaintLevel = 0;
8586 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8587 // Don't record nested paints.
8588 if (sPaintLevel++ > 0) {
8589 return;
8592 mStart = TimeStamp::Now();
8595 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8596 MOZ_ASSERT(sPaintLevel != 0);
8597 if (--sPaintLevel > 0) {
8598 return;
8601 // If we're in multi-process mode, don't include paint times for the parent
8602 // process.
8603 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8604 return;
8607 double totalMs = (TimeStamp::Now() - mStart).ToMilliseconds();
8609 // Record the total time.
8610 Telemetry::Accumulate(Telemetry::CONTENT_PAINT_TIME,
8611 static_cast<uint32_t>(totalMs));
8614 static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
8615 if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
8616 return aFrame;
8619 if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
8620 !aFrame->GetPrevInFlow()) {
8621 return aFrame->GetPlaceholderFrame();
8624 return aFrame;
8627 static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
8628 nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
8629 MOZ_ASSERT(f);
8630 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
8633 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8634 nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
8635 const nsRect& aVisibleRect, const nsRect& aDirtyRect,
8636 const bool aIsTransformed)
8637 : mBuilder(aBuilder),
8638 mPrevFrame(aBuilder->mCurrentFrame),
8639 mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
8640 mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
8641 mPrevAdditionalOffset(aBuilder->mAdditionalOffset),
8642 mPrevVisibleRect(aBuilder->mVisibleRect),
8643 mPrevDirtyRect(aBuilder->mDirtyRect),
8644 mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo),
8645 mPrevAncestorHasApzAwareEventHandler(
8646 aBuilder->mAncestorHasApzAwareEventHandler),
8647 mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
8648 mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
8649 if (aIsTransformed) {
8650 aBuilder->mCurrentOffsetToReferenceFrame =
8651 aBuilder->AdditionalOffset().refOr(nsPoint());
8652 aBuilder->mCurrentReferenceFrame = aForChild;
8653 } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
8654 aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
8655 } else {
8656 aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
8657 aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
8660 // If aForChild is being visited from a frame other than it's ancestor frame,
8661 // mInInvalidSubtree will need to be recalculated the slow way.
8662 if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) {
8663 aBuilder->mInInvalidSubtree =
8664 aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
8665 } else {
8666 aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
8669 aBuilder->mCurrentFrame = aForChild;
8670 aBuilder->mVisibleRect = aVisibleRect;
8671 aBuilder->mDirtyRect =
8672 aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
8675 } // namespace mozilla