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  */
7 
8 /*
9  * structures that represent things to be painted (ordered in z-order),
10  * used during painting and hit testing
11  */
12 
13 #include "nsDisplayList.h"
14 
15 #include <stdint.h>
16 #include <algorithm>
17 #include <limits>
18 
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"
55 
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"
114 
115 namespace mozilla {
116 
117 using namespace dom;
118 using namespace gfx;
119 using namespace layout;
120 using namespace layers;
121 using namespace image;
122 
123 LazyLogModule sDisplayListLog("displaylist");
124 
125 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
AssertUniqueItem(nsDisplayItem * aItem)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;
132       }
133       MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
134     }
135   }
136 }
137 #endif
138 
ShouldBuildItemForEvents(const DisplayItemType aType)139 bool ShouldBuildItemForEvents(const DisplayItemType aType) {
140   return aType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO ||
141          (GetDisplayItemFlagsForType(aType) & TYPE_IS_CONTAINER);
142 }
143 
ItemTypeSupportsHitTesting(const DisplayItemType aType)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;
152   }
153 }
154 
InitializeHitTestInfo(nsDisplayListBuilder * aBuilder,nsPaintedDisplayItem * aItem,const DisplayItemType aType)155 void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder,
156                            nsPaintedDisplayItem* aItem,
157                            const DisplayItemType aType) {
158   if (ItemTypeSupportsHitTesting(aType)) {
159     aItem->InitializeHitTestInfo(aBuilder);
160   }
161 }
162 
163 /* static */
CreateASRForFrame(const ActiveScrolledRoot * aParent,nsIScrollableFrame * aScrollableFrame,bool aIsRetained)164 already_AddRefed<ActiveScrolledRoot> ActiveScrolledRoot::CreateASRForFrame(
165     const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame,
166     bool aIsRetained) {
167   nsIFrame* f = do_QueryFrame(aScrollableFrame);
168 
169   RefPtr<ActiveScrolledRoot> asr;
170   if (aIsRetained) {
171     asr = f->GetProperty(ActiveScrolledRootCache());
172   }
173 
174   if (!asr) {
175     asr = new ActiveScrolledRoot();
176 
177     if (aIsRetained) {
178       RefPtr<ActiveScrolledRoot> ref = asr;
179       f->SetProperty(ActiveScrolledRootCache(), ref.forget().take());
180     }
181   }
182   asr->mParent = aParent;
183   asr->mScrollableFrame = aScrollableFrame;
184   asr->mViewId = Nothing();
185   asr->mDepth = aParent ? aParent->mDepth + 1 : 1;
186   asr->mRetained = aIsRetained;
187 
188   return asr.forget();
189 }
190 
191 /* static */
IsAncestor(const ActiveScrolledRoot * aAncestor,const ActiveScrolledRoot * aDescendant)192 bool ActiveScrolledRoot::IsAncestor(const ActiveScrolledRoot* aAncestor,
193                                     const ActiveScrolledRoot* aDescendant) {
194   if (!aAncestor) {
195     // nullptr is the root
196     return true;
197   }
198   if (Depth(aAncestor) > Depth(aDescendant)) {
199     return false;
200   }
201   const ActiveScrolledRoot* asr = aDescendant;
202   while (asr) {
203     if (asr == aAncestor) {
204       return true;
205     }
206     asr = asr->mParent;
207   }
208   return false;
209 }
210 
211 /* static */
IsProperAncestor(const ActiveScrolledRoot * aAncestor,const ActiveScrolledRoot * aDescendant)212 bool ActiveScrolledRoot::IsProperAncestor(
213     const ActiveScrolledRoot* aAncestor,
214     const ActiveScrolledRoot* aDescendant) {
215   return aAncestor != aDescendant && IsAncestor(aAncestor, aDescendant);
216 }
217 
218 /* static */
ToString(const ActiveScrolledRoot * aActiveScrolledRoot)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(", ");
226     }
227   }
228   return std::move(str);
229 }
230 
ComputeViewId() const231 ScrollableLayerGuid::ViewID ActiveScrolledRoot::ComputeViewId() const {
232   nsIContent* content = mScrollableFrame->GetScrolledFrame()->GetContent();
233   return nsLayoutUtils::FindOrCreateIDFor(content);
234 }
235 
~ActiveScrolledRoot()236 ActiveScrolledRoot::~ActiveScrolledRoot() {
237   if (mScrollableFrame && mRetained) {
238     nsIFrame* f = do_QueryFrame(mScrollableFrame);
239     f->RemoveProperty(ActiveScrolledRootCache());
240   }
241 }
242 
AddAnimationsForWebRender(nsDisplayItem * aItem,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder,const Maybe<LayoutDevicePoint> & aPosition=Nothing ())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.
255     //
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;
260   }
261 
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());
271 
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;
283   }
284 
285   return animationsId;
286 }
287 
GenerateAndPushTextMask(nsIFrame * aFrame,gfxContext * aContext,const nsRect & aFillRect,nsDisplayListBuilder * aBuilder)288 static bool GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
289                                     const nsRect& aFillRect,
290                                     nsDisplayListBuilder* aBuilder) {
291   if (aBuilder->IsForGenerateGlyphMask()) {
292     return false;
293   }
294 
295   SVGObserverUtils::GetAndObserveBackgroundClip(aFrame);
296 
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.
302 
303   gfxContext* sourceCtx = aContext;
304   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
305       aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel());
306 
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;
313   }
314   RefPtr<gfxContext> maskCtx =
315       gfxContext::CreatePreservingTransformOrNull(maskDT);
316   MOZ_ASSERT(maskCtx);
317   maskCtx->Multiply(Matrix::Translation(bounds.TopLeft().ToUnknownPoint()));
318 
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);
323 
324   // Push the generated mask into aContext, so that the caller can pop and
325   // blend with it.
326 
327   Matrix currentMatrix = sourceCtx->CurrentMatrix();
328   Matrix invCurrentMatrix = currentMatrix;
329   invCurrentMatrix.Invert();
330 
331   RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
332   sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0,
333                                    maskSurface, invCurrentMatrix);
334 
335   return true;
336 }
337 
CreateShallowCopy(nsDisplayListBuilder * aBuilder)338 nsDisplayWrapper* nsDisplayWrapList::CreateShallowCopy(
339     nsDisplayListBuilder* aBuilder) {
340   const nsDisplayWrapList* wrappedItem = AsDisplayWrapList();
341   MOZ_ASSERT(wrappedItem);
342 
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);
349 
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;
354 }
355 
MergeItems(nsTArray<nsDisplayItem * > & aItems)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);
366 
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));
377   }
378 
379   merged->GetChildren()->AppendToTop(last->CreateShallowCopy(this));
380 
381   return merged;
382 }
383 
384 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
SetCurrentActiveScrolledRoot(const ActiveScrolledRoot * aActiveScrolledRoot)385     SetCurrentActiveScrolledRoot(
386         const ActiveScrolledRoot* aActiveScrolledRoot) {
387   MOZ_ASSERT(!mUsed);
388 
389   // Set the builder's mCurrentActiveScrolledRoot.
390   mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;
391 
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.
409 
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);
414 
415   // mCurrentContainerASR is adjusted so that it's still an ancestor of
416   // finiteBoundsASR.
417   mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
418       mBuilder->mCurrentContainerASR, finiteBoundsASR);
419 
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();
427     }
428   }
429 
430   mUsed = true;
431 }
432 
433 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
InsertScrollFrame(nsIScrollableFrame * aScrollableFrame)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;
441 
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;
451       }
452     }
453   }
454 
455   mUsed = true;
456 }
457 
AutoContainerASRTracker(nsDisplayListBuilder * aBuilder)458 nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
459     nsDisplayListBuilder* aBuilder)
460     : mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) {
461   mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot;
462 }
463 
CurrentPresContext()464 nsPresContext* nsDisplayListBuilder::CurrentPresContext() {
465   return CurrentPresShellState()->mPresShell->GetPresContext();
466 }
467 
468 /* static */
ComputeVisibleRectForFrame(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsRect & aVisibleRect,const nsRect & aDirtyRect,nsRect * aOutDirtyRect)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;
475 
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());
483 
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;
507         }
508       }
509     }
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)));
516     }
517   }
518 
519   *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
520   visible -= aFrame->GetPosition();
521 
522   nsRect overflowRect = aFrame->InkOverflowRect();
523 
524   if (aFrame->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
525                                      aFrame, DisplayItemType::TYPE_TRANSFORM)) {
526     /**
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.
530      */
531     overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
532   }
533 
534   visible.IntersectRect(visible, overflowRect);
535   aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect);
536 
537   return visible;
538 }
539 
Linkifier(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList)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;
549   }
550 
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);
571       }
572     }
573   };
574 
575   if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
576     if (elem->HasID()) {
577       maybeGenerateDest(nsGkAtoms::id);
578     }
579     if (elem->HasName()) {
580       maybeGenerateDest(nsGkAtoms::name);
581     }
582   }
583 
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;
588   }
589 
590   // Check if we have actually found a link.
591   nsCOMPtr<nsIURI> uri;
592   if (!elem->IsLink(getter_AddRefs(uri))) {
593     return;
594   }
595 
596   // Is it a local (in-page) destination?
597   bool hasRef, eqExRef;
598   nsIURI* docURI;
599   if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
600       NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef &&
601       (docURI = aFrame->PresContext()->Document()->GetDocumentURI()) &&
602       NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &eqExRef)) && eqExRef) {
603     if (NS_FAILED(uri->GetRef(aBuilder->mLinkSpec)) ||
604         aBuilder->mLinkSpec.IsEmpty()) {
605       return;
606     }
607     // The destination name is simply a string; we don't want URL-escaping
608     // applied to it.
609     NS_UnescapeURL(aBuilder->mLinkSpec);
610     // Mark the link spec as being an internal destination
611     aBuilder->mLinkSpec.Insert('#', 0);
612   } else {
613     if (NS_FAILED(uri->GetSpec(aBuilder->mLinkSpec)) ||
614         aBuilder->mLinkSpec.IsEmpty()) {
615       return;
616     }
617   }
618 
619   // Record that we need to reset the builder's state on destruction.
620   mBuilderToReset = aBuilder;
621 }
622 
MaybeAppendLink(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)623 void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
624     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
625   // Note that we may generate a link here even if the constructor bailed out
626   // without updating aBuilder->LinkSpec(), because it may have been set by
627   // an ancestor that was associated with a link element.
628   if (!aBuilder->mLinkSpec.IsEmpty()) {
629     auto* link = MakeDisplayItem<nsDisplayLink>(
630         aBuilder, aFrame, aBuilder->mLinkSpec.get(), aFrame->GetRect());
631     mList->AppendToTop(link);
632   }
633 }
634 
nsDisplayListBuilder(nsIFrame * aReferenceFrame,nsDisplayListBuilderMode aMode,bool aBuildCaret,bool aRetainingDisplayList)635 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
636                                            nsDisplayListBuilderMode aMode,
637                                            bool aBuildCaret,
638                                            bool aRetainingDisplayList)
639     : mReferenceFrame(aReferenceFrame),
640       mIgnoreScrollFrame(nullptr),
641       mCurrentActiveScrolledRoot(nullptr),
642       mCurrentContainerASR(nullptr),
643       mCurrentFrame(aReferenceFrame),
644       mCurrentReferenceFrame(aReferenceFrame),
645       mBuildingExtraPagesForPageNum(0),
646       mDirtyRect(-1, -1, -1, -1),
647       mGlassDisplayItem(nullptr),
648       mHasGlassItemDuringPartial(false),
649       mCaretFrame(nullptr),
650       mScrollInfoItemsForHoisting(nullptr),
651       mFirstClipChainToDestroy(nullptr),
652       mMode(aMode),
653       mTableBackgroundSet(nullptr),
654       mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
655       mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
656       mFilterASR(nullptr),
657       mContainsBlendMode(false),
658       mIsBuildingScrollbar(false),
659       mCurrentScrollbarWillHaveLayer(false),
660       mBuildCaret(aBuildCaret),
661       mRetainingDisplayList(aRetainingDisplayList),
662       mPartialUpdate(false),
663       mIgnoreSuppression(false),
664       mIncludeAllOutOfFlows(false),
665       mDescendIntoSubdocuments(true),
666       mSelectedFramesOnly(false),
667       mAllowMergingAndFlattening(true),
668       mInTransform(false),
669       mInEventsOnly(false),
670       mInFilter(false),
671       mInPageSequence(false),
672       mIsInChromePresContext(false),
673       mSyncDecodeImages(false),
674       mIsPaintingToWindow(false),
675       mUseHighQualityScaling(false),
676       mIsPaintingForWebRender(false),
677       mIsCompositingCheap(false),
678       mAncestorHasApzAwareEventHandler(false),
679       mHaveScrollableDisplayPort(false),
680       mWindowDraggingAllowed(false),
681       mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
682       mForceLayerForScrollParent(false),
683       mContainsNonMinimalDisplayPort(false),
684       mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
685       mBuildingInvisibleItems(false),
686       mIsBuilding(false),
687       mInInvalidSubtree(false),
688       mDisablePartialUpdates(false),
689       mPartialBuildFailed(false),
690       mIsInActiveDocShell(false),
691       mBuildAsyncZoomContainer(false),
692       mContainsBackdropFilter(false),
693       mIsRelativeToLayoutViewport(false),
694       mUseOverlayScrollbars(false),
695       mAlwaysLayerizeScrollbars(false) {
696   MOZ_COUNT_CTOR(nsDisplayListBuilder);
697 
698   mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting();
699 
700   ShouldRebuildDisplayListDueToPrefChange();
701 
702   mUseOverlayScrollbars =
703       !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
704 
705   mAlwaysLayerizeScrollbars =
706       StaticPrefs::layout_scrollbars_always_layerize_track();
707 
708   static_assert(
709       static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
710       "Check TYPE_MAX should not overflow");
711   mIsForContent = XRE_IsContentProcess();
712   mIsReusingStackingContextItems =
713       mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
714 }
715 
GetFocusedPresShell()716 static PresShell* GetFocusedPresShell() {
717   nsPIDOMWindowOuter* focusedWnd =
718       nsFocusManager::GetFocusManager()->GetFocusedWindow();
719   if (!focusedWnd) {
720     return nullptr;
721   }
722 
723   nsCOMPtr<nsIDocShell> focusedDocShell = focusedWnd->GetDocShell();
724   if (!focusedDocShell) {
725     return nullptr;
726   }
727 
728   return focusedDocShell->GetPresShell();
729 }
730 
BeginFrame()731 void nsDisplayListBuilder::BeginFrame() {
732   nsCSSRendering::BeginFrameTreesLocked();
733 
734   mIsPaintingToWindow = false;
735   mUseHighQualityScaling = false;
736   mIgnoreSuppression = false;
737   mInTransform = false;
738   mInFilter = false;
739   mSyncDecodeImages = false;
740 
741   if (!mBuildCaret) {
742     return;
743   }
744 
745   RefPtr<PresShell> presShell = GetFocusedPresShell();
746   if (presShell) {
747     RefPtr<nsCaret> caret = presShell->GetCaret();
748     mCaretFrame = caret->GetPaintGeometry(&mCaretRect);
749 
750     // The focused pres shell may not be in the document that we're
751     // painting, or be in a popup. Check if the display root for
752     // the caret matches the display root that we're painting, and
753     // only use it if it matches.
754     if (mCaretFrame &&
755         nsLayoutUtils::GetDisplayRootFrame(mCaretFrame) !=
756             nsLayoutUtils::GetDisplayRootFrame(mReferenceFrame)) {
757       mCaretFrame = nullptr;
758     }
759   }
760 }
761 
AddEffectUpdate(dom::RemoteBrowser * aBrowser,const dom::EffectsInfo & aUpdate)762 void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser* aBrowser,
763                                            const dom::EffectsInfo& aUpdate) {
764   dom::EffectsInfo update = aUpdate;
765   // For printing we create one display item for each page that an iframe
766   // appears on, the proper visible rect is the union of all the visible rects
767   // we get from each display item.
768   nsPresContext* pc =
769       mReferenceFrame ? mReferenceFrame->PresContext() : nullptr;
770   if (pc && (pc->Type() != nsPresContext::eContext_Galley)) {
771     Maybe<dom::EffectsInfo> existing = mEffectsUpdates.MaybeGet(aBrowser);
772     if (existing.isSome()) {
773       // Only the visible rect should differ, the scales should match.
774       MOZ_ASSERT(existing->mScaleX == aUpdate.mScaleX &&
775                  existing->mScaleY == aUpdate.mScaleY &&
776                  existing->mTransformToAncestorScale ==
777                      aUpdate.mTransformToAncestorScale);
778       update.mVisibleRect = update.mVisibleRect.Union(existing->mVisibleRect);
779     }
780   }
781   mEffectsUpdates.InsertOrUpdate(aBrowser, update);
782 }
783 
EndFrame()784 void nsDisplayListBuilder::EndFrame() {
785   NS_ASSERTION(!mInInvalidSubtree,
786                "Someone forgot to cleanup mInInvalidSubtree!");
787   mActiveScrolledRoots.Clear();
788   mEffectsUpdates.Clear();
789   FreeClipChains();
790   FreeTemporaryItems();
791   nsCSSRendering::EndFrameTreesLocked();
792   mCaretFrame = nullptr;
793 }
794 
MarkFrameForDisplay(nsIFrame * aFrame,const nsIFrame * aStopAtFrame)795 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
796                                                const nsIFrame* aStopAtFrame) {
797   mFramesMarkedForDisplay.AppendElement(aFrame);
798   for (nsIFrame* f = aFrame; f;
799        f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
800     if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
801       return;
802     }
803     f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
804     if (f == aStopAtFrame) {
805       // we've reached a frame that we know will be painted, so we can stop.
806       break;
807     }
808   }
809 }
810 
AddFrameMarkedForDisplayIfVisible(nsIFrame * aFrame)811 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) {
812   mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
813 }
814 
MarkFrameForDisplayIfVisible(nsIFrame * aFrame,const nsIFrame * aStopAtFrame)815 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
816     nsIFrame* aFrame, const nsIFrame* aStopAtFrame) {
817   AddFrameMarkedForDisplayIfVisible(aFrame);
818   for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
819     if (f->ForceDescendIntoIfVisible()) {
820       return;
821     }
822     f->SetForceDescendIntoIfVisible(true);
823     if (f == aStopAtFrame) {
824       // we've reached a frame that we know will be painted, so we can stop.
825       break;
826     }
827   }
828 }
829 
SetGlassDisplayItem(nsDisplayItem * aItem)830 void nsDisplayListBuilder::SetGlassDisplayItem(nsDisplayItem* aItem) {
831   // Web pages or extensions could trigger the "Multiple glass backgrounds
832   // found?" warning by using -moz-appearance:win-borderless-glass etc on their
833   // own elements (as long as they are DocElementBoxFrames, which is rare as
834   // each xul doc only gets one near the root). We only care about first one,
835   // since that will be the background of the root window.
836 
837   if (IsPartialUpdate()) {
838     if (aItem->Frame()->IsDocElementBoxFrame()) {
839 #ifdef DEBUG
840       if (mHasGlassItemDuringPartial) {
841         NS_WARNING("Multiple glass backgrounds found?");
842       } else
843 #endif
844           if (!mHasGlassItemDuringPartial) {
845         mHasGlassItemDuringPartial = true;
846         aItem->SetIsGlassItem();
847       }
848     }
849     return;
850   }
851 
852   if (aItem->Frame()->IsDocElementBoxFrame()) {
853 #ifdef DEBUG
854     if (mGlassDisplayItem) {
855       NS_WARNING("Multiple glass backgrounds found?");
856     } else
857 #endif
858         if (!mGlassDisplayItem) {
859       mGlassDisplayItem = aItem;
860       mGlassDisplayItem->SetIsGlassItem();
861     }
862   }
863 }
864 
NeedToForceTransparentSurfaceForItem(nsDisplayItem * aItem)865 bool nsDisplayListBuilder::NeedToForceTransparentSurfaceForItem(
866     nsDisplayItem* aItem) {
867   return aItem == mGlassDisplayItem;
868 }
869 
SetIsRelativeToLayoutViewport()870 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
871   mIsRelativeToLayoutViewport = true;
872   UpdateShouldBuildAsyncZoomContainer();
873 }
874 
UpdateShouldBuildAsyncZoomContainer()875 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
876   const Document* document = mReferenceFrame->PresContext()->Document();
877   mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
878                              !document->Fullscreen() &&
879                              nsLayoutUtils::AllowZoomingForDocument(document);
880 }
881 
882 // Certain prefs may cause display list items to be added or removed when they
883 // are toggled. In those cases, we need to fully rebuild the display list.
ShouldRebuildDisplayListDueToPrefChange()884 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
885   // If we transition between wrapping the RCD-RSF contents into an async
886   // zoom container vs. not, we need to rebuild the display list. This only
887   // happens when the zooming or container scrolling prefs are toggled
888   // (manually by the user, or during test setup).
889   bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer;
890   UpdateShouldBuildAsyncZoomContainer();
891 
892   bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars;
893   mUseOverlayScrollbars =
894       !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
895 
896   bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars;
897   mAlwaysLayerizeScrollbars =
898       StaticPrefs::layout_scrollbars_always_layerize_track();
899 
900   if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) {
901     return true;
902   }
903 
904   if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) {
905     return true;
906   }
907 
908   if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) {
909     return true;
910   }
911 
912   return false;
913 }
914 
AddScrollFrameToNotify(nsIScrollableFrame * aScrollFrame)915 void nsDisplayListBuilder::AddScrollFrameToNotify(
916     nsIScrollableFrame* aScrollFrame) {
917   mScrollFramesToNotify.insert(aScrollFrame);
918 }
919 
NotifyAndClearScrollFrames()920 void nsDisplayListBuilder::NotifyAndClearScrollFrames() {
921   for (const auto& it : mScrollFramesToNotify) {
922     it->NotifyApzTransaction();
923   }
924   mScrollFramesToNotify.clear();
925 }
926 
MarkOutOfFlowFrameForDisplay(nsIFrame * aDirtyFrame,nsIFrame * aFrame,const nsRect & aVisibleRect,const nsRect & aDirtyRect)927 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
928     nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect,
929     const nsRect& aDirtyRect) {
930   MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
931   nsRect dirty;
932   nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
933       this, aFrame, aVisibleRect, aDirtyRect, &dirty);
934   if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
935       visible.IsEmpty()) {
936     return false;
937   }
938 
939   // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
940   // frame, then it will also mark any outer frames to ensure that building
941   // reaches the dirty feame.
942   if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
943     MarkFrameForDisplay(aFrame, aDirtyFrame);
944   }
945 
946   return true;
947 }
948 
UnmarkFrameForDisplay(nsIFrame * aFrame,const nsIFrame * aStopAtFrame)949 static void UnmarkFrameForDisplay(nsIFrame* aFrame,
950                                   const nsIFrame* aStopAtFrame) {
951   for (nsIFrame* f = aFrame; f;
952        f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
953     if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
954       return;
955     }
956     f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
957     if (f == aStopAtFrame) {
958       // we've reached a frame that we know will be painted, so we can stop.
959       break;
960     }
961   }
962 }
963 
UnmarkFrameForDisplayIfVisible(nsIFrame * aFrame)964 static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
965   for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
966     if (!f->ForceDescendIntoIfVisible()) {
967       return;
968     }
969     f->SetForceDescendIntoIfVisible(false);
970   }
971 }
972 
~nsDisplayListBuilder()973 nsDisplayListBuilder::~nsDisplayListBuilder() {
974   NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
975                "All frames should have been unmarked");
976   NS_ASSERTION(mFramesWithOOFData.Length() == 0,
977                "All OOF data should have been removed");
978   NS_ASSERTION(mPresShellStates.Length() == 0,
979                "All presshells should have been exited");
980 
981   DisplayItemClipChain* c = mFirstClipChainToDestroy;
982   while (c) {
983     DisplayItemClipChain* next = c->mNextClipChainToDestroy;
984     c->DisplayItemClipChain::~DisplayItemClipChain();
985     c = next;
986   }
987 
988   MOZ_COUNT_DTOR(nsDisplayListBuilder);
989 }
990 
GetBackgroundPaintFlags()991 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
992   uint32_t flags = 0;
993   if (mSyncDecodeImages) {
994     flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
995   }
996   if (mIsPaintingToWindow) {
997     flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
998   }
999   if (mUseHighQualityScaling) {
1000     flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING;
1001   }
1002   return flags;
1003 }
1004 
1005 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
GetImageRendererFlags() const1006 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
1007   uint32_t flags = 0;
1008   if (mSyncDecodeImages) {
1009     flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
1010   }
1011   if (mIsPaintingToWindow) {
1012     flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
1013   }
1014   if (mUseHighQualityScaling) {
1015     flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
1016   }
1017   return flags;
1018 }
1019 
GetImageDecodeFlags() const1020 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
1021   uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
1022   if (mSyncDecodeImages) {
1023     flags |= imgIContainer::FLAG_SYNC_DECODE;
1024   } else {
1025     flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
1026   }
1027   if (mIsPaintingToWindow || mUseHighQualityScaling) {
1028     flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1029   }
1030   return flags;
1031 }
1032 
SubtractFromVisibleRegion(nsRegion * aVisibleRegion,const nsRegion & aRegion)1033 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
1034                                                      const nsRegion& aRegion) {
1035   if (aRegion.IsEmpty()) {
1036     return;
1037   }
1038 
1039   nsRegion tmp;
1040   tmp.Sub(*aVisibleRegion, aRegion);
1041   // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1042   // to its bounds either, which can be very bad (see bug 516740).
1043   // Do let aVisibleRegion get more complex if by doing so we reduce its
1044   // area by at least half.
1045   if (tmp.GetNumRects() <= 15 || tmp.Area() <= aVisibleRegion->Area() / 2) {
1046     *aVisibleRegion = tmp;
1047   }
1048 }
1049 
GetCaret()1050 nsCaret* nsDisplayListBuilder::GetCaret() {
1051   RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
1052   return caret;
1053 }
1054 
IncrementPresShellPaintCount(PresShell * aPresShell)1055 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) {
1056   if (mIsPaintingToWindow) {
1057     mReferenceFrame->AddPaintedPresShell(aPresShell);
1058     aPresShell->IncrementPaintCount();
1059   }
1060 }
1061 
EnterPresShell(const nsIFrame * aReferenceFrame,bool aPointerEventsNoneDoc)1062 void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
1063                                           bool aPointerEventsNoneDoc) {
1064   PresShellState* state = mPresShellStates.AppendElement();
1065   state->mPresShell = aReferenceFrame->PresShell();
1066   state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
1067   state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
1068 
1069   nsIScrollableFrame* sf = state->mPresShell->GetRootScrollFrameAsScrollable();
1070   if (sf && IsInSubdocument()) {
1071     // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1072     // that the canvas background color will be set correctly, and that only one
1073     // unscrollable item will be created.
1074     // This is done to avoid, for example, a case where only scrollbar frames
1075     // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1076     // and possibly end up with an extra nsDisplaySolidColor item.
1077     // We skip this for the root document, since we don't want to use
1078     // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
1079     // do it manually there.
1080     nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
1081     if (canvasFrame) {
1082       MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
1083     }
1084   }
1085 
1086 #ifdef DEBUG
1087   state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
1088                                   nsLayoutPhase::DisplayListBuilding);
1089 #endif
1090 
1091   state->mPresShell->UpdateCanvasBackground();
1092 
1093   bool buildCaret = mBuildCaret;
1094   if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
1095     state->mIsBackgroundOnly = false;
1096   } else {
1097     state->mIsBackgroundOnly = true;
1098     buildCaret = false;
1099   }
1100 
1101   bool pointerEventsNone = aPointerEventsNoneDoc;
1102   if (IsInSubdocument()) {
1103     pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
1104                              .mInsidePointerEventsNoneDoc;
1105   }
1106   state->mInsidePointerEventsNoneDoc = pointerEventsNone;
1107 
1108   state->mPresShellIgnoreScrollFrame =
1109       state->mPresShell->IgnoringViewportScrolling()
1110           ? state->mPresShell->GetRootScrollFrame()
1111           : nullptr;
1112 
1113   nsPresContext* pc = aReferenceFrame->PresContext();
1114   mIsInChromePresContext = pc->IsChrome();
1115   nsIDocShell* docShell = pc->GetDocShell();
1116 
1117   if (docShell) {
1118     docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1119   }
1120 
1121   state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);
1122 
1123   if (!buildCaret) {
1124     return;
1125   }
1126 
1127   // Caret frames add visual area to their frame, but we don't update the
1128   // overflow area. Use flags to make sure we build display items for that frame
1129   // instead.
1130   if (mCaretFrame && mCaretFrame->PresShell() == state->mPresShell) {
1131     MarkFrameForDisplay(mCaretFrame, aReferenceFrame);
1132   }
1133 }
1134 
1135 // A non-blank paint is a paint that does not just contain the canvas
1136 // background.
DisplayListIsNonBlank(nsDisplayList * aList)1137 static bool DisplayListIsNonBlank(nsDisplayList* aList) {
1138   for (nsDisplayItem* i : *aList) {
1139     switch (i->GetType()) {
1140       case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
1141       case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
1142       case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
1143         continue;
1144       case DisplayItemType::TYPE_SOLID_COLOR:
1145       case DisplayItemType::TYPE_BACKGROUND:
1146       case DisplayItemType::TYPE_BACKGROUND_COLOR:
1147         if (i->Frame()->IsCanvasFrame()) {
1148           continue;
1149         }
1150         return true;
1151       default:
1152         return true;
1153     }
1154   }
1155   return false;
1156 }
1157 
1158 // A contentful paint is a paint that does contains DOM content (text,
1159 // images, non-blank canvases, SVG): "First Contentful Paint entry
1160 // contains a DOMHighResTimeStamp reporting the time when the browser
1161 // first rendered any text, image (including background images),
1162 // non-white canvas or SVG. This excludes any content of iframes, but
1163 // includes text with pending webfonts. This is the first time users
1164 // could start consuming page content."
DisplayListIsContentful(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)1165 static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
1166                                     nsDisplayList* aList) {
1167   for (nsDisplayItem* i : *aList) {
1168     DisplayItemType type = i->GetType();
1169     nsDisplayList* children = i->GetChildren();
1170 
1171     switch (type) {
1172       case DisplayItemType::TYPE_SUBDOCUMENT:  // iframes are ignored
1173         break;
1174       // CANVASes check if they may have been modified (as a stand-in
1175       // actually tracking all modifications)
1176       default:
1177         if (i->IsContentful()) {
1178           bool dummy;
1179           nsRect bound = i->GetBounds(aBuilder, &dummy);
1180           if (!bound.IsEmpty()) {
1181             return true;
1182           }
1183         }
1184         if (children) {
1185           if (DisplayListIsContentful(aBuilder, children)) {
1186             return true;
1187           }
1188         }
1189         break;
1190     }
1191   }
1192   return false;
1193 }
1194 
LeavePresShell(const nsIFrame * aReferenceFrame,nsDisplayList * aPaintedContents)1195 void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
1196                                           nsDisplayList* aPaintedContents) {
1197   NS_ASSERTION(
1198       CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
1199       "Presshell mismatch");
1200 
1201   if (mIsPaintingToWindow && aPaintedContents) {
1202     nsPresContext* pc = aReferenceFrame->PresContext();
1203     if (!pc->HadNonBlankPaint()) {
1204       if (!CurrentPresShellState()->mIsBackgroundOnly &&
1205           DisplayListIsNonBlank(aPaintedContents)) {
1206         pc->NotifyNonBlankPaint();
1207       }
1208     }
1209     nsRootPresContext* rootPresContext = pc->GetRootPresContext();
1210     if (!pc->HadContentfulPaint() && rootPresContext) {
1211       if (!CurrentPresShellState()->mIsBackgroundOnly) {
1212         if (pc->HasEverBuiltInvisibleText() ||
1213             DisplayListIsContentful(this, aPaintedContents)) {
1214           pc->NotifyContentfulPaint();
1215         }
1216       }
1217     }
1218   }
1219 
1220   ResetMarkedFramesForDisplayList(aReferenceFrame);
1221   mPresShellStates.RemoveLastElement();
1222 
1223   if (!mPresShellStates.IsEmpty()) {
1224     nsPresContext* pc = CurrentPresContext();
1225     nsIDocShell* docShell = pc->GetDocShell();
1226     if (docShell) {
1227       docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1228     }
1229     mIsInChromePresContext = pc->IsChrome();
1230   } else {
1231     for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
1232       UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
1233     }
1234     mFramesMarkedForDisplayIfVisible.SetLength(0);
1235   }
1236 }
1237 
FreeClipChains()1238 void nsDisplayListBuilder::FreeClipChains() {
1239   // Iterate the clip chains from newest to oldest (forward
1240   // iteration), so that we destroy descendants first which
1241   // will drop the ref count on their ancestors.
1242   DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;
1243 
1244   while (*indirect) {
1245     if (!(*indirect)->mRefCount) {
1246       DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;
1247 
1248       mClipDeduplicator.erase(*indirect);
1249       (*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
1250       Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);
1251 
1252       *indirect = next;
1253     } else {
1254       indirect = &(*indirect)->mNextClipChainToDestroy;
1255     }
1256   }
1257 }
1258 
FreeTemporaryItems()1259 void nsDisplayListBuilder::FreeTemporaryItems() {
1260   for (nsDisplayItem* i : mTemporaryItems) {
1261     // Temporary display items are not added to the frames.
1262     MOZ_ASSERT(i->Frame());
1263     i->RemoveFrame(i->Frame());
1264     i->Destroy(this);
1265   }
1266 
1267   mTemporaryItems.Clear();
1268 }
1269 
ResetMarkedFramesForDisplayList(const nsIFrame * aReferenceFrame)1270 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1271     const nsIFrame* aReferenceFrame) {
1272   // Unmark and pop off the frames marked for display in this pres shell.
1273   uint32_t firstFrameForShell =
1274       CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1275   for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
1276        ++i) {
1277     UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
1278   }
1279   mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1280 
1281   firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
1282   for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
1283     mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
1284   }
1285   mFramesWithOOFData.SetLength(firstFrameForShell);
1286 }
1287 
ClearFixedBackgroundDisplayData()1288 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1289   CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
1290 }
1291 
MarkFramesForDisplayList(nsIFrame * aDirtyFrame,const nsFrameList & aFrames)1292 void nsDisplayListBuilder::MarkFramesForDisplayList(
1293     nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
1294   nsRect visibleRect = GetVisibleRect();
1295   nsRect dirtyRect = GetDirtyRect();
1296 
1297   // If we are entering content that is fixed to the RCD-RSF, we are
1298   // crossing the async zoom container boundary, and need to convert from
1299   // visual to layout coordinates.
1300   if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
1301     if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
1302         viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
1303       if (viewportFrame->PresShell()->GetRootScrollFrame()) {
1304 #ifdef DEBUG
1305         for (nsIFrame* f : aFrames) {
1306           MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
1307         }
1308 #endif
1309         visibleRect = ViewportUtils::VisualToLayout(visibleRect,
1310                                                     viewportFrame->PresShell());
1311         dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
1312                                                   viewportFrame->PresShell());
1313       }
1314 #ifdef DEBUG
1315       else {
1316         // This is an edge case that should only happen if we are in a
1317         // document with a XUL root element so that it does not have a root
1318         // scroll frame but it has fixed pos content and all of the frames in
1319         // aFrames are that fixed pos content.
1320         for (nsIFrame* f : aFrames) {
1321           MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
1322                      f->GetParent() == aDirtyFrame &&
1323                      f->StyleDisplay()->mPosition ==
1324                          StylePositionProperty::Fixed);
1325         }
1326         // There's no root scroll frame so there can't be any zooming or async
1327         // panning so we don't need to adjust the visible and dirty rects.
1328       }
1329 #endif
1330     }
1331   }
1332 
1333   bool markedFrames = false;
1334   for (nsIFrame* e : aFrames) {
1335     // Skip the AccessibleCaret frame when building no caret.
1336     if (!IsBuildingCaret()) {
1337       nsIContent* content = e->GetContent();
1338       if (content && content->IsInNativeAnonymousSubtree() &&
1339           content->IsElement()) {
1340         auto* classList = content->AsElement()->ClassList();
1341         if (classList->Contains(u"moz-accessiblecaret"_ns)) {
1342           continue;
1343         }
1344       }
1345     }
1346     if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
1347       markedFrames = true;
1348     }
1349   }
1350 
1351   if (markedFrames) {
1352     // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1353     // to objects on the stack, so we need to clone the chain.
1354     const DisplayItemClipChain* clipChain =
1355         CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1356     const DisplayItemClipChain* combinedClipChain =
1357         mClipState.GetCurrentCombinedClipChain(this);
1358     const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1359 
1360     OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
1361         clipChain, combinedClipChain, asr, visibleRect, dirtyRect);
1362     aDirtyFrame->SetProperty(
1363         nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
1364     mFramesWithOOFData.AppendElement(aDirtyFrame);
1365   }
1366 
1367   if (!aDirtyFrame->GetParent()) {
1368     // This is the viewport frame of aDirtyFrame's presshell.
1369     // Store the current display data so that it can be used for fixed
1370     // background images.
1371     NS_ASSERTION(
1372         CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
1373         "Presshell mismatch");
1374     MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
1375                "already traversed this presshell's root frame?");
1376 
1377     const DisplayItemClipChain* clipChain =
1378         CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1379     const DisplayItemClipChain* combinedClipChain =
1380         mClipState.GetCurrentCombinedClipChain(this);
1381     const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1382     CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
1383         clipChain, combinedClipChain, asr, GetVisibleRect(), GetDirtyRect());
1384   }
1385 }
1386 
1387 /**
1388  * Mark all preserve-3d children with
1389  * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1390  * nsIFrame::BuildDisplayListForChild() would visit them.  Also compute
1391  * dirty rect for preserve-3d children.
1392  *
1393  * @param aDirtyFrame is the frame to mark children extending context.
1394  */
MarkPreserve3DFramesForDisplayList(nsIFrame * aDirtyFrame)1395 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1396     nsIFrame* aDirtyFrame) {
1397   for (const auto& childList : aDirtyFrame->ChildLists()) {
1398     for (nsIFrame* child : childList.mList) {
1399       if (child->Combines3DTransformWithAncestors()) {
1400         MarkFrameForDisplay(child, aDirtyFrame);
1401       }
1402 
1403       if (child->IsBlockWrapper()) {
1404         // Mark preserve-3d frames inside the block wrapper.
1405         MarkPreserve3DFramesForDisplayList(child);
1406       }
1407     }
1408   }
1409 }
1410 
AllocateActiveScrolledRoot(const ActiveScrolledRoot * aParent,nsIScrollableFrame * aScrollableFrame)1411 ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1412     const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame) {
1413   RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
1414       aParent, aScrollableFrame, IsRetainingDisplayList());
1415   mActiveScrolledRoots.AppendElement(asr);
1416   return asr;
1417 }
1418 
AllocateDisplayItemClipChain(const DisplayItemClip & aClip,const ActiveScrolledRoot * aASR,const DisplayItemClipChain * aParent)1419 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1420     const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
1421     const DisplayItemClipChain* aParent) {
1422   MOZ_ASSERT(!(aParent && aParent->mOnStack));
1423   void* p = Allocate(sizeof(DisplayItemClipChain),
1424                      DisplayListArenaObjectId::CLIPCHAIN);
1425   DisplayItemClipChain* c = new (KnownNotNull, p)
1426       DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy);
1427 #ifdef DEBUG
1428   c->mOnStack = false;
1429 #endif
1430   auto result = mClipDeduplicator.insert(c);
1431   if (!result.second) {
1432     // An equivalent clip chain item was already created, so let's return that
1433     // instead. Destroy the one we just created.
1434     // Note that this can cause clip chains from different coordinate systems to
1435     // collapse into the same clip chain object, because clip chains do not keep
1436     // track of the reference frame that they were created in.
1437     c->DisplayItemClipChain::~DisplayItemClipChain();
1438     Destroy(DisplayListArenaObjectId::CLIPCHAIN, c);
1439     return *(result.first);
1440   }
1441   mFirstClipChainToDestroy = c;
1442   return c;
1443 }
1444 
1445 struct ClipChainItem {
1446   DisplayItemClip clip;
1447   const ActiveScrolledRoot* asr;
1448 };
1449 
FindCommonAncestorClipForIntersection(const DisplayItemClipChain * aOne,const DisplayItemClipChain * aTwo)1450 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
1451     const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
1452   for (const ActiveScrolledRoot* asr =
1453            ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
1454        asr; asr = asr->mParent) {
1455     if (aOne == aTwo) {
1456       return aOne;
1457     }
1458     if (aOne->mASR == asr) {
1459       aOne = aOne->mParent;
1460     }
1461     if (aTwo->mASR == asr) {
1462       aTwo = aTwo->mParent;
1463     }
1464     if (!aOne) {
1465       return aTwo;
1466     }
1467     if (!aTwo) {
1468       return aOne;
1469     }
1470   }
1471   return nullptr;
1472 }
1473 
CreateClipChainIntersection(const DisplayItemClipChain * aAncestor,const DisplayItemClipChain * aLeafClip1,const DisplayItemClipChain * aLeafClip2)1474 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1475     const DisplayItemClipChain* aAncestor,
1476     const DisplayItemClipChain* aLeafClip1,
1477     const DisplayItemClipChain* aLeafClip2) {
1478   AutoTArray<ClipChainItem, 8> intersectedClips;
1479 
1480   const DisplayItemClipChain* clip1 = aLeafClip1;
1481   const DisplayItemClipChain* clip2 = aLeafClip2;
1482 
1483   const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
1484       clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);
1485 
1486   // Build up the intersection from the leaf to the root and put it into
1487   // intersectedClips. The loop below will convert intersectedClips into an
1488   // actual DisplayItemClipChain.
1489   // (We need to do this in two passes because we need the parent clip in order
1490   // to create the DisplayItemClipChain object, but the parent clip has not
1491   // been created at that point.)
1492   while (!aAncestor || asr != aAncestor->mASR) {
1493     if (clip1 && clip1->mASR == asr) {
1494       if (clip2 && clip2->mASR == asr) {
1495         DisplayItemClip intersection = clip1->mClip;
1496         intersection.IntersectWith(clip2->mClip);
1497         intersectedClips.AppendElement(ClipChainItem{intersection, asr});
1498         clip2 = clip2->mParent;
1499       } else {
1500         intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
1501       }
1502       clip1 = clip1->mParent;
1503     } else if (clip2 && clip2->mASR == asr) {
1504       intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
1505       clip2 = clip2->mParent;
1506     }
1507     if (!asr) {
1508       MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
1509       break;
1510     }
1511     asr = asr->mParent;
1512   }
1513 
1514   // Convert intersectedClips into a DisplayItemClipChain.
1515   const DisplayItemClipChain* parentSC = aAncestor;
1516   for (auto& sc : Reversed(intersectedClips)) {
1517     parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
1518   }
1519   return parentSC;
1520 }
1521 
CreateClipChainIntersection(const DisplayItemClipChain * aLeafClip1,const DisplayItemClipChain * aLeafClip2)1522 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1523     const DisplayItemClipChain* aLeafClip1,
1524     const DisplayItemClipChain* aLeafClip2) {
1525   // aLeafClip2 might be a reference to a clip on the stack. We need to make
1526   // sure that CreateClipChainIntersection will allocate the actual intersected
1527   // clip in the builder's arena, so for the aLeafClip1 == nullptr case,
1528   // we supply nullptr as the common ancestor so that
1529   // CreateClipChainIntersection clones the whole chain.
1530   const DisplayItemClipChain* ancestorClip =
1531       aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
1532                  : nullptr;
1533 
1534   return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2);
1535 }
1536 
CopyWholeChain(const DisplayItemClipChain * aClipChain)1537 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
1538     const DisplayItemClipChain* aClipChain) {
1539   return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
1540 }
1541 
FindReferenceFrameFor(const nsIFrame * aFrame,nsPoint * aOffset) const1542 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
1543     const nsIFrame* aFrame, nsPoint* aOffset) const {
1544   auto MaybeApplyAdditionalOffset = [&]() {
1545     if (auto offset = AdditionalOffset()) {
1546       *aOffset += *offset;
1547     }
1548   };
1549 
1550   if (aFrame == mCurrentFrame) {
1551     if (aOffset) {
1552       *aOffset = mCurrentOffsetToReferenceFrame;
1553     }
1554     return mCurrentReferenceFrame;
1555   }
1556 
1557   for (const nsIFrame* f = aFrame; f;
1558        f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
1559     if (f == mReferenceFrame || f->IsTransformed()) {
1560       if (aOffset) {
1561         *aOffset = aFrame->GetOffsetToCrossDoc(f);
1562         MaybeApplyAdditionalOffset();
1563       }
1564       return f;
1565     }
1566   }
1567 
1568   if (aOffset) {
1569     *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1570     MaybeApplyAdditionalOffset();
1571   }
1572 
1573   return mReferenceFrame;
1574 }
1575 
1576 // Sticky frames are active if their nearest scrollable frame is also active.
IsStickyFrameActive(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)1577 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
1578                                 nsIFrame* aFrame) {
1579   MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
1580              StylePositionProperty::Sticky);
1581 
1582   StickyScrollContainer* stickyScrollContainer =
1583       StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
1584   return stickyScrollContainer &&
1585          stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
1586 }
1587 
IsAnimatedGeometryRoot(nsIFrame * aFrame,nsIFrame ** aParent)1588 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,
1589                                                   nsIFrame** aParent) {
1590   if (aFrame == mReferenceFrame) {
1591     return true;
1592   }
1593 
1594   if (!IsPaintingToWindow()) {
1595     if (aParent) {
1596       *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1597     }
1598     return false;
1599   }
1600 
1601   nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
1602   if (!parent) {
1603     return true;
1604   }
1605   *aParent = parent;
1606 
1607   if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky &&
1608       IsStickyFrameActive(this, aFrame)) {
1609     return true;
1610   }
1611 
1612   if (aFrame->IsTransformed()) {
1613     if (EffectCompositor::HasAnimationsForCompositor(
1614             aFrame, DisplayItemType::TYPE_TRANSFORM)) {
1615       return true;
1616     }
1617   }
1618 
1619   LayoutFrameType parentType = parent->Type();
1620   if (parentType == LayoutFrameType::Scroll ||
1621       parentType == LayoutFrameType::ListControl) {
1622     nsIScrollableFrame* sf = do_QueryFrame(parent);
1623     if (sf->GetScrolledFrame() == aFrame) {
1624       MOZ_ASSERT(!aFrame->IsTransformed());
1625       return sf->IsMaybeAsynchronouslyScrolled();
1626     }
1627   }
1628 
1629   return false;
1630 }
1631 
FindAnimatedGeometryRootFrameFor(nsIFrame * aFrame)1632 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1633     nsIFrame* aFrame) {
1634   MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1635       RootReferenceFrame(), aFrame));
1636   nsIFrame* cursor = aFrame;
1637   while (cursor != RootReferenceFrame()) {
1638     nsIFrame* next;
1639     if (IsAnimatedGeometryRoot(cursor, &next)) {
1640       return cursor;
1641     }
1642     cursor = next;
1643   }
1644   return cursor;
1645 }
1646 
ApplyAllClipNonRoundedIntersection(const DisplayItemClipChain * aClipChain,const nsRect & aRect)1647 static nsRect ApplyAllClipNonRoundedIntersection(
1648     const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
1649   nsRect result = aRect;
1650   while (aClipChain) {
1651     result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
1652     aClipChain = aClipChain->mParent;
1653   }
1654   return result;
1655 }
1656 
AdjustWindowDraggingRegion(nsIFrame * aFrame)1657 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
1658   if (!mWindowDraggingAllowed || !IsForPainting()) {
1659     return;
1660   }
1661 
1662   const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
1663   if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
1664     // This frame has the default value and doesn't influence the window
1665     // dragging region.
1666     return;
1667   }
1668 
1669   LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;
1670 
1671   // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1672   nsIFrame* referenceFrame =
1673       const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1674 
1675   if (IsInTransform()) {
1676     // Only support 2d rectilinear transforms. Transform support is needed for
1677     // the horizontal flip transform that's applied to the urlbar textbox in
1678     // RTL mode - it should be able to exclude itself from the draggable region.
1679     referenceFrameToRootReferenceFrame =
1680         ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
1681             nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
1682                                                   RelativeTo{mReferenceFrame})
1683                 .GetMatrix());
1684     Matrix referenceFrameToRootReferenceFrame2d;
1685     if (!referenceFrameToRootReferenceFrame.Is2D(
1686             &referenceFrameToRootReferenceFrame2d) ||
1687         !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1688       return;
1689     }
1690   } else {
1691     MOZ_ASSERT(referenceFrame == mReferenceFrame,
1692                "referenceFrameToRootReferenceFrame needs to be adjusted");
1693   }
1694 
1695   // We do some basic visibility checking on the frame's border box here.
1696   // We intersect it both with the current dirty rect and with the current
1697   // clip. Either one is just a conservative approximation on its own, but
1698   // their intersection luckily works well enough for our purposes, so that
1699   // we don't have to do full-blown visibility computations.
1700   // The most important case we need to handle is the scrolled-off tab:
1701   // If the tab bar overflows, tab parts that are clipped by the scrollbox
1702   // should not be allowed to interfere with the window dragging region. Using
1703   // just the current DisplayItemClip is not enough to cover this case
1704   // completely because clips are reset while building stacking context
1705   // contents, so for example we'd fail to clip frames that have a clip path
1706   // applied to them. But the current dirty rect doesn't get reset in that
1707   // case, so we use it to make this case work.
1708   nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
1709   borderBox += ToReferenceFrame(aFrame);
1710   const DisplayItemClipChain* clip =
1711       ClipState().GetCurrentCombinedClipChain(this);
1712   borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
1713   if (borderBox.IsEmpty()) {
1714     return;
1715   }
1716 
1717   LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
1718       borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1719 
1720   LayoutDeviceRect transformedDevPixelBorderBox =
1721       TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1722   transformedDevPixelBorderBox.Round();
1723   LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1724 
1725   if (!transformedDevPixelBorderBox.ToIntRect(
1726           &transformedDevPixelBorderBoxInt)) {
1727     return;
1728   }
1729 
1730   LayoutDeviceIntRegion& region =
1731       styleUI->mWindowDragging == StyleWindowDragging::Drag
1732           ? mWindowDraggingRegion
1733           : mWindowNoDraggingRegion;
1734 
1735   if (!IsRetainingDisplayList()) {
1736     region.OrWith(transformedDevPixelBorderBoxInt);
1737     return;
1738   }
1739 
1740   gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
1741   if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
1742     mRetainedWindowDraggingRegion.Add(aFrame, rect);
1743   } else {
1744     mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
1745   }
1746 }
1747 
GetWindowDraggingRegion() const1748 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
1749   LayoutDeviceIntRegion result;
1750   if (!IsRetainingDisplayList()) {
1751     result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
1752     return result;
1753   }
1754 
1755   LayoutDeviceIntRegion dragRegion =
1756       mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();
1757 
1758   LayoutDeviceIntRegion noDragRegion =
1759       mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();
1760 
1761   result.Sub(dragRegion, noDragRegion);
1762   return result;
1763 }
1764 
AddSizeOfExcludingThis(nsWindowSizes & aSizes) const1765 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1766   nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes);
1767   aSizes.mLayoutRetainedDisplayListSize +=
1768       aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get());
1769 }
1770 
AddSizeOfExcludingThis(nsWindowSizes & aSizes) const1771 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1772   mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList);
1773 
1774   size_t n = 0;
1775   MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
1776   n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1777   n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
1778   n += mEffectsUpdates.ShallowSizeOfExcludingThis(mallocSizeOf);
1779   n += mWindowExcludeGlassRegion.SizeOfExcludingThis(mallocSizeOf);
1780   n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1781   n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
1782   n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf);
1783   // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
1784 
1785   aSizes.mLayoutRetainedDisplayListSize += n;
1786 }
1787 
AddSizeOfExcludingThis(nsWindowSizes & aSizes) const1788 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
1789   for (nsDisplayItem* item : *this) {
1790     item->AddSizeOfExcludingThis(aSizes);
1791     if (RetainedDisplayList* children = item->GetChildren()) {
1792       children->AddSizeOfExcludingThis(aSizes);
1793     }
1794   }
1795 
1796   size_t n = 0;
1797 
1798   n += mDAG.mDirectPredecessorList.ShallowSizeOfExcludingThis(
1799       aSizes.mState.mMallocSizeOf);
1800   n += mDAG.mNodesInfo.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1801   n += mOldItems.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
1802 
1803   aSizes.mLayoutRetainedDisplayListSize += n;
1804 }
1805 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const1806 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis(
1807     MallocSizeOf aMallocSizeOf) const {
1808   size_t n = 0;
1809   n += mFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
1810   for (const auto& frame : mFrames) {
1811     const UniquePtr<WeakFrame>& weakFrame = frame.mWeakFrame;
1812     n += aMallocSizeOf(weakFrame.get());
1813   }
1814   n += mRects.ShallowSizeOfExcludingThis(aMallocSizeOf);
1815   return n;
1816 }
1817 
1818 /**
1819  * Removes modified frames and rects from this WeakFrameRegion.
1820  */
RemoveModifiedFramesAndRects()1821 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() {
1822   MOZ_ASSERT(mFrames.Length() == mRects.Length());
1823 
1824   uint32_t i = 0;
1825   uint32_t length = mFrames.Length();
1826 
1827   while (i < length) {
1828     auto& wrapper = mFrames[i];
1829 
1830     if (!wrapper.mWeakFrame->IsAlive() ||
1831         AnyContentAncestorModified(wrapper.mWeakFrame->GetFrame())) {
1832       // To avoid multiple O(n) shifts in the array, move the last element of
1833       // the array to the current position and decrease the array length.
1834       mFrameSet.Remove(wrapper.mFrame);
1835       mFrames[i] = std::move(mFrames[length - 1]);
1836       mRects[i] = std::move(mRects[length - 1]);
1837       length--;
1838     } else {
1839       i++;
1840     }
1841   }
1842 
1843   mFrames.TruncateLength(length);
1844   mRects.TruncateLength(length);
1845 }
1846 
RemoveModifiedWindowRegions()1847 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1848   mRetainedWindowDraggingRegion.RemoveModifiedFramesAndRects();
1849   mRetainedWindowNoDraggingRegion.RemoveModifiedFramesAndRects();
1850   mWindowExcludeGlassRegion.RemoveModifiedFramesAndRects();
1851   mRetainedWindowOpaqueRegion.RemoveModifiedFramesAndRects();
1852 
1853   mHasGlassItemDuringPartial = false;
1854 }
1855 
ClearRetainedWindowRegions()1856 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1857   mRetainedWindowDraggingRegion.Clear();
1858   mRetainedWindowNoDraggingRegion.Clear();
1859   mWindowExcludeGlassRegion.Clear();
1860   mRetainedWindowOpaqueRegion.Clear();
1861 
1862   mGlassDisplayItem = nullptr;
1863 }
1864 
1865 const uint32_t gWillChangeAreaMultiplier = 3;
GetLayerizationCost(const nsSize & aSize)1866 static uint32_t GetLayerizationCost(const nsSize& aSize) {
1867   // There's significant overhead for each layer created from Gecko
1868   // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1869   // Therefore we set a minimum cost threshold of a 64x64 area.
1870   const int minBudgetCost = 64 * 64;
1871 
1872   const uint32_t budgetCost = std::max(
1873       minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
1874                          nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
1875 
1876   return budgetCost;
1877 }
1878 
AddToWillChangeBudget(nsIFrame * aFrame,const nsSize & aSize)1879 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
1880                                                  const nsSize& aSize) {
1881   MOZ_ASSERT(IsForPainting());
1882 
1883   if (aFrame->MayHaveWillChangeBudget()) {
1884     // The frame is already in the will-change budget.
1885     return true;
1886   }
1887 
1888   const nsPresContext* presContext = aFrame->PresContext();
1889   const nsRect area = presContext->GetVisibleArea();
1890   const uint32_t budgetLimit =
1891       nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1892       nsPresContext::AppUnitsToIntCSSPixels(area.height);
1893   const uint32_t cost = GetLayerizationCost(aSize);
1894 
1895   DocumentWillChangeBudget& documentBudget =
1896       mDocumentWillChangeBudgets.LookupOrInsert(presContext);
1897 
1898   const bool onBudget =
1899       (documentBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
1900 
1901   if (onBudget) {
1902     documentBudget += cost;
1903     mFrameWillChangeBudgets.InsertOrUpdate(
1904         aFrame, FrameWillChangeBudget(presContext, cost));
1905     aFrame->SetMayHaveWillChangeBudget(true);
1906   }
1907 
1908   return onBudget;
1909 }
1910 
IsInWillChangeBudget(nsIFrame * aFrame,const nsSize & aSize)1911 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
1912                                                 const nsSize& aSize) {
1913   if (!IsForPainting()) {
1914     // If this nsDisplayListBuilder is not for painting, the layerization should
1915     // not matter. Do the simple thing and return false.
1916     return false;
1917   }
1918 
1919   const bool onBudget = AddToWillChangeBudget(aFrame, aSize);
1920   if (onBudget) {
1921     return true;
1922   }
1923 
1924   auto* pc = aFrame->PresContext();
1925   auto* doc = pc->Document();
1926   if (!doc->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget)) {
1927     AutoTArray<nsString, 2> params;
1928     params.AppendElement()->AppendInt(gWillChangeAreaMultiplier);
1929 
1930     nsRect area = pc->GetVisibleArea();
1931     uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1932                            nsPresContext::AppUnitsToIntCSSPixels(area.height);
1933     params.AppendElement()->AppendInt(budgetLimit);
1934 
1935     doc->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget, false, params);
1936   }
1937 
1938   return false;
1939 }
1940 
ClearWillChangeBudgetStatus(nsIFrame * aFrame)1941 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame* aFrame) {
1942   MOZ_ASSERT(IsForPainting());
1943 
1944   if (!aFrame->MayHaveWillChangeBudget()) {
1945     return;
1946   }
1947 
1948   aFrame->SetMayHaveWillChangeBudget(false);
1949   RemoveFromWillChangeBudgets(aFrame);
1950 }
1951 
RemoveFromWillChangeBudgets(const nsIFrame * aFrame)1952 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame* aFrame) {
1953   if (auto entry = mFrameWillChangeBudgets.Lookup(aFrame)) {
1954     const FrameWillChangeBudget& frameBudget = entry.Data();
1955 
1956     auto documentBudget =
1957         mDocumentWillChangeBudgets.Lookup(frameBudget.mPresContext);
1958 
1959     if (documentBudget) {
1960       *documentBudget -= frameBudget.mUsage;
1961     }
1962 
1963     entry.Remove();
1964   }
1965 }
1966 
ClearWillChangeBudgets()1967 void nsDisplayListBuilder::ClearWillChangeBudgets() {
1968   mFrameWillChangeBudgets.Clear();
1969   mDocumentWillChangeBudgets.Clear();
1970 }
1971 
EnterSVGEffectsContents(nsIFrame * aEffectsFrame,nsDisplayList * aHoistedItemsStorage)1972 void nsDisplayListBuilder::EnterSVGEffectsContents(
1973     nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) {
1974   MOZ_ASSERT(aHoistedItemsStorage);
1975   if (mSVGEffectsFrames.IsEmpty()) {
1976     MOZ_ASSERT(!mScrollInfoItemsForHoisting);
1977     mScrollInfoItemsForHoisting = aHoistedItemsStorage;
1978   }
1979   mSVGEffectsFrames.AppendElement(aEffectsFrame);
1980 }
1981 
ExitSVGEffectsContents()1982 void nsDisplayListBuilder::ExitSVGEffectsContents() {
1983   MOZ_ASSERT(!mSVGEffectsFrames.IsEmpty());
1984   mSVGEffectsFrames.RemoveLastElement();
1985   MOZ_ASSERT(mScrollInfoItemsForHoisting);
1986   if (mSVGEffectsFrames.IsEmpty()) {
1987     mScrollInfoItemsForHoisting = nullptr;
1988   }
1989 }
1990 
ShouldBuildScrollInfoItemsForHoisting() const1991 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const {
1992   /*
1993    * Note: if changing the conditions under which scroll info layers
1994    * are created, make a corresponding change to
1995    * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
1996    */
1997   for (nsIFrame* frame : mSVGEffectsFrames) {
1998     if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame)) {
1999       return true;
2000     }
2001   }
2002   return false;
2003 }
2004 
AppendNewScrollInfoItemForHoisting(nsDisplayScrollInfoLayer * aScrollInfoItem)2005 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
2006     nsDisplayScrollInfoLayer* aScrollInfoItem) {
2007   MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2008   MOZ_ASSERT(mScrollInfoItemsForHoisting);
2009   mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
2010 }
2011 
BuildCompositorHitTestInfoIfNeeded(nsIFrame * aFrame,nsDisplayList * aList)2012 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2013     nsIFrame* aFrame, nsDisplayList* aList) {
2014   MOZ_ASSERT(aFrame);
2015   MOZ_ASSERT(aList);
2016 
2017   if (!BuildCompositorHitTestInfo()) {
2018     return;
2019   }
2020 
2021   const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
2022   if (info != CompositorHitTestInvisibleToHit) {
2023     aList->AppendNewToTop<nsDisplayCompositorHitTestInfo>(this, aFrame);
2024   }
2025 }
2026 
AddReusableDisplayItem(nsDisplayItem * aItem)2027 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem* aItem) {
2028   mReuseableItems.Insert(aItem);
2029 }
2030 
RemoveReusedDisplayItem(nsDisplayItem * aItem)2031 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem* aItem) {
2032   MOZ_ASSERT(aItem->IsReusedItem());
2033   mReuseableItems.Remove(aItem);
2034 }
2035 
ClearReuseableDisplayItems()2036 void nsDisplayListBuilder::ClearReuseableDisplayItems() {
2037   const size_t total = mReuseableItems.Count();
2038 
2039   size_t reused = 0;
2040   for (auto* item : mReuseableItems) {
2041     if (item->IsReusedItem()) {
2042       reused++;
2043       item->SetReusable();
2044     } else {
2045       item->Destroy(this);
2046     }
2047   }
2048 
2049   DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
2050   mReuseableItems.Clear();
2051 }
2052 
ReuseDisplayItem(nsDisplayItem * aItem)2053 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
2054   const auto* previous = mCurrentContainerASR;
2055   const auto* asr = aItem->GetActiveScrolledRoot();
2056   mCurrentContainerASR =
2057       ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
2058 
2059   if (previous != mCurrentContainerASR) {
2060     DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
2061             mCurrentContainerASR);
2062   }
2063 
2064   aItem->SetReusedItem();
2065 }
2066 
MoveTo(const nsDisplayListSet & aDestination) const2067 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
2068   aDestination.BorderBackground()->AppendToTop(BorderBackground());
2069   aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2070   aDestination.Floats()->AppendToTop(Floats());
2071   aDestination.Content()->AppendToTop(Content());
2072   aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
2073   aDestination.Outlines()->AppendToTop(Outlines());
2074 }
2075 
GetClippedBounds(nsDisplayListBuilder * aBuilder) const2076 nsRect nsDisplayList::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2077   nsRect bounds;
2078   for (nsDisplayItem* i : *this) {
2079     bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
2080   }
2081   return bounds;
2082 }
2083 
GetClippedBoundsWithRespectToASR(nsDisplayListBuilder * aBuilder,const ActiveScrolledRoot * aASR,nsRect * aBuildingRect) const2084 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR(
2085     nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
2086     nsRect* aBuildingRect) const {
2087   nsRect bounds;
2088   for (nsDisplayItem* i : *this) {
2089     nsRect r = i->GetClippedBounds(aBuilder);
2090     if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
2091       if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) {
2092         r = clip.ref();
2093       }
2094     }
2095     if (aBuildingRect) {
2096       aBuildingRect->UnionRect(*aBuildingRect, i->GetBuildingRect());
2097     }
2098     bounds.UnionRect(bounds, r);
2099   }
2100   return bounds;
2101 }
2102 
GetBuildingRect() const2103 nsRect nsDisplayList::GetBuildingRect() const {
2104   nsRect result;
2105   for (nsDisplayItem* i : *this) {
2106     result.UnionRect(result, i->GetBuildingRect());
2107   }
2108   return result;
2109 }
2110 
TriggerPendingAnimations(Document & aDoc,const TimeStamp & aReadyTime)2111 static void TriggerPendingAnimations(Document& aDoc,
2112                                      const TimeStamp& aReadyTime) {
2113   MOZ_ASSERT(!aReadyTime.IsNull(),
2114              "Animation ready time is not set. Perhaps we're using a layer"
2115              " manager that doesn't update it");
2116   if (PendingAnimationTracker* tracker = aDoc.GetPendingAnimationTracker()) {
2117     PresShell* presShell = aDoc.GetPresShell();
2118     // If paint-suppression is in effect then we haven't finished painting
2119     // this document yet so we shouldn't start animations
2120     if (!presShell || !presShell->IsPaintingSuppressed()) {
2121       tracker->TriggerPendingAnimationsOnNextTick(aReadyTime);
2122     }
2123   }
2124   auto recurse = [&aReadyTime](Document& aDoc) {
2125     TriggerPendingAnimations(aDoc, aReadyTime);
2126     return CallState::Continue;
2127   };
2128   aDoc.EnumerateSubDocuments(recurse);
2129 }
2130 
GetWidgetWindowRenderer(nsView ** aView)2131 WindowRenderer* nsDisplayListBuilder::GetWidgetWindowRenderer(nsView** aView) {
2132   if (aView) {
2133     *aView = RootReferenceFrame()->GetView();
2134   }
2135   if (RootReferenceFrame() !=
2136       nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2137     return nullptr;
2138   }
2139   nsIWidget* window = RootReferenceFrame()->GetNearestWidget();
2140   if (window) {
2141     return window->GetWindowRenderer();
2142   }
2143   return nullptr;
2144 }
2145 
GetWidgetLayerManager(nsView ** aView)2146 WebRenderLayerManager* nsDisplayListBuilder::GetWidgetLayerManager(
2147     nsView** aView) {
2148   WindowRenderer* renderer = GetWidgetWindowRenderer();
2149   if (renderer) {
2150     return renderer->AsWebRender();
2151   }
2152   return nullptr;
2153 }
2154 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,int32_t aAppUnitsPerDevPixel)2155 void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2156                           int32_t aAppUnitsPerDevPixel) {
2157   FlattenedDisplayListIterator iter(aBuilder, this);
2158   while (iter.HasNext()) {
2159     nsPaintedDisplayItem* item = iter.GetNextItem()->AsPaintedDisplayItem();
2160     if (!item) {
2161       continue;
2162     }
2163 
2164     nsRegion visible(item->GetClippedBounds(aBuilder));
2165     visible.And(visible, item->GetPaintRect(aBuilder, aCtx));
2166     if (visible.IsEmpty()) {
2167       continue;
2168     }
2169     nsRect buildingRect = item->GetBuildingRect();
2170     item->SetBuildingRect(visible.GetBounds());
2171 
2172     DisplayItemClip currentClip = item->GetClip();
2173     if (currentClip.HasClip()) {
2174       aCtx->Save();
2175       if (currentClip.IsRectClippedByRoundedCorner(visible.GetBounds())) {
2176         currentClip.ApplyTo(aCtx, aAppUnitsPerDevPixel);
2177       } else {
2178         currentClip.ApplyRectTo(aCtx, aAppUnitsPerDevPixel);
2179       }
2180     }
2181     aCtx->NewPath();
2182 
2183     item->Paint(aBuilder, aCtx);
2184 
2185     if (currentClip.HasClip()) {
2186       aCtx->Restore();
2187     }
2188     item->SetBuildingRect(buildingRect);
2189   }
2190 }
2191 
2192 /**
2193  * We paint by executing a layer manager transaction, constructing a
2194  * single layer representing the display list, and then making it the
2195  * root of the layer manager, drawing into the PaintedLayers.
2196  */
PaintRoot(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,uint32_t aFlags,Maybe<double> aDisplayListBuildTime)2197 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
2198                               uint32_t aFlags,
2199                               Maybe<double> aDisplayListBuildTime) {
2200   AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS);
2201 
2202   RefPtr<WebRenderLayerManager> layerManager;
2203   WindowRenderer* renderer = nullptr;
2204   bool widgetTransaction = false;
2205   bool doBeginTransaction = true;
2206   nsView* view = nullptr;
2207   if (aFlags & PAINT_USE_WIDGET_LAYERS) {
2208     renderer = aBuilder->GetWidgetWindowRenderer(&view);
2209     if (renderer) {
2210       // The fallback renderer doesn't retain any content, so it's
2211       // not meaningful to use it when drawing to an external context.
2212       if (aCtx && renderer->AsFallback()) {
2213         MOZ_ASSERT(!(aFlags & PAINT_EXISTING_TRANSACTION));
2214         renderer = nullptr;
2215       } else {
2216         layerManager = renderer->AsWebRender();
2217         doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
2218         widgetTransaction = true;
2219       }
2220     }
2221   }
2222 
2223   nsIFrame* frame = aBuilder->RootReferenceFrame();
2224   nsPresContext* presContext = frame->PresContext();
2225   PresShell* presShell = presContext->PresShell();
2226   Document* document = presShell->GetDocument();
2227 
2228   ScopeExit g([&]() {
2229     // For layers-free mode, we check the invalidation state bits in the
2230     // EndTransaction. So we clear the invalidation state bits after
2231     // EndTransaction.
2232     if (widgetTransaction ||
2233         // SVG-as-an-image docs don't paint as part of the retained layer tree,
2234         // but they still need the invalidation state bits cleared in order for
2235         // invalidation for CSS/SMIL animation to work properly.
2236         (document && document->IsBeingUsedAsImage())) {
2237       DL_LOGD("Clearing invalidation state bits");
2238       frame->ClearInvalidationStateBits();
2239     }
2240   });
2241 
2242   if (!renderer) {
2243     if (!aCtx) {
2244       NS_WARNING("Nowhere to paint into");
2245       return;
2246     }
2247     bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(false);
2248     Paint(aBuilder, aCtx, presContext->AppUnitsPerDevPixel());
2249 
2250     aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2251     return;
2252   }
2253 
2254   if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) {
2255     MOZ_ASSERT(layerManager);
2256     if (doBeginTransaction) {
2257       if (aCtx) {
2258         if (!layerManager->BeginTransactionWithTarget(aCtx, nsCString())) {
2259           return;
2260         }
2261       } else {
2262         if (!layerManager->BeginTransaction(nsCString())) {
2263           return;
2264         }
2265       }
2266     }
2267 
2268     bool prevIsCompositingCheap = aBuilder->SetIsCompositingCheap(true);
2269     layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
2270 
2271     bool sent = false;
2272     if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
2273       sent = layerManager->EndEmptyTransaction();
2274     }
2275 
2276     if (!sent) {
2277       auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
2278 
2279       nsIDocShell* docShell = presContext->GetDocShell();
2280       WrFiltersHolder wrFilters;
2281       gfx::Matrix5x4* colorMatrix =
2282           nsDocShell::Cast(docShell)->GetColorMatrix();
2283       if (colorMatrix) {
2284         wrFilters.filters.AppendElement(
2285             wr::FilterOp::ColorMatrix(colorMatrix->components));
2286       }
2287 
2288       wrManager->EndTransactionWithoutLayer(this, aBuilder,
2289                                             std::move(wrFilters), nullptr,
2290                                             aDisplayListBuildTime.valueOr(0.0));
2291     }
2292 
2293     aBuilder->SetIsCompositingCheap(prevIsCompositingCheap);
2294     if (document && widgetTransaction) {
2295       TriggerPendingAnimations(*document,
2296                                layerManager->GetAnimationReadyTime());
2297     }
2298 
2299     if (presContext->RefreshDriver()->HasScheduleFlush()) {
2300       presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2301                                       frame->GetRect());
2302     }
2303 
2304     return;
2305   }
2306 
2307   FallbackRenderer* fallback = renderer->AsFallback();
2308   MOZ_ASSERT(fallback);
2309 
2310   if (doBeginTransaction) {
2311     MOZ_ASSERT(!aCtx);
2312     if (!fallback->BeginTransaction()) {
2313       return;
2314     }
2315   }
2316 
2317   bool temp = aBuilder->SetIsCompositingCheap(renderer->IsCompositingCheap());
2318   fallback->EndTransactionWithList(aBuilder, this,
2319                                    presContext->AppUnitsPerDevPixel(),
2320                                    WindowRenderer::END_DEFAULT);
2321 
2322   aBuilder->SetIsCompositingCheap(temp);
2323 
2324   if (document && widgetTransaction) {
2325     TriggerPendingAnimations(*document, renderer->GetAnimationReadyTime());
2326   }
2327 }
2328 
DeleteAll(nsDisplayListBuilder * aBuilder)2329 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
2330   for (auto* item : TakeItems()) {
2331     item->Destroy(aBuilder);
2332   }
2333 }
2334 
IsFrameReceivingPointerEvents(nsIFrame * aFrame)2335 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) {
2336   return aFrame->Style()->PointerEvents() != StylePointerEvents::None;
2337 }
2338 
2339 // A list of frames, and their z depth. Used for sorting
2340 // the results of hit testing.
2341 struct FramesWithDepth {
FramesWithDepthmozilla::FramesWithDepth2342   explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {}
2343 
operator <mozilla::FramesWithDepth2344   bool operator<(const FramesWithDepth& aOther) const {
2345     if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
2346       // We want to sort so that the shallowest item (highest depth value) is
2347       // first
2348       return mDepth > aOther.mDepth;
2349     }
2350     return this < &aOther;
2351   }
operator ==mozilla::FramesWithDepth2352   bool operator==(const FramesWithDepth& aOther) const {
2353     return this == &aOther;
2354   }
2355 
2356   float mDepth;
2357   nsTArray<nsIFrame*> mFrames;
2358 };
2359 
2360 // Sort the frames by depth and then moves all the contained frames to the
2361 // destination
FlushFramesArray(nsTArray<FramesWithDepth> & aSource,nsTArray<nsIFrame * > * aDest)2362 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource,
2363                              nsTArray<nsIFrame*>* aDest) {
2364   if (aSource.IsEmpty()) {
2365     return;
2366   }
2367   aSource.Sort();
2368   uint32_t length = aSource.Length();
2369   for (uint32_t i = 0; i < length; i++) {
2370     aDest->AppendElements(std::move(aSource[i].mFrames));
2371   }
2372   aSource.Clear();
2373 }
2374 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,nsDisplayItem::HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames) const2375 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2376                             nsDisplayItem::HitTestState* aState,
2377                             nsTArray<nsIFrame*>* aOutFrames) const {
2378   nsDisplayItem* item;
2379 
2380   if (aState->mInPreserves3D) {
2381     // Collect leaves of the current 3D rendering context.
2382     for (nsDisplayItem* item : *this) {
2383       auto itemType = item->GetType();
2384       if (itemType != DisplayItemType::TYPE_TRANSFORM ||
2385           !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2386         item->HitTest(aBuilder, aRect, aState, aOutFrames);
2387       } else {
2388         // One of leaves in the current 3D rendering context.
2389         aState->mItemBuffer.AppendElement(item);
2390       }
2391     }
2392     return;
2393   }
2394 
2395   int32_t itemBufferStart = aState->mItemBuffer.Length();
2396   for (nsDisplayItem* item : *this) {
2397     aState->mItemBuffer.AppendElement(item);
2398   }
2399 
2400   AutoTArray<FramesWithDepth, 16> temp;
2401   for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart;
2402        --i) {
2403     // Pop element off the end of the buffer. We want to shorten the buffer
2404     // so that recursive calls to HitTest have more buffer space.
2405     item = aState->mItemBuffer[i];
2406     aState->mItemBuffer.SetLength(i);
2407 
2408     bool snap;
2409     nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
2410     auto itemType = item->GetType();
2411     bool same3DContext =
2412         (itemType == DisplayItemType::TYPE_TRANSFORM &&
2413          static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
2414         (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
2415          item->Frame()->Extend3DContext());
2416     if (same3DContext &&
2417         (itemType != DisplayItemType::TYPE_TRANSFORM ||
2418          !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
2419       if (!item->GetClip().MayIntersect(aRect)) {
2420         continue;
2421       }
2422       AutoTArray<nsIFrame*, 1> neverUsed;
2423       // Start gethering leaves of the 3D rendering context, and
2424       // append leaves at the end of mItemBuffer.  Leaves are
2425       // processed at following iterations.
2426       aState->mInPreserves3D = true;
2427       item->HitTest(aBuilder, aRect, aState, &neverUsed);
2428       aState->mInPreserves3D = false;
2429       i = aState->mItemBuffer.Length();
2430       continue;
2431     }
2432     if (same3DContext || item->GetClip().MayIntersect(r)) {
2433       AutoTArray<nsIFrame*, 16> outFrames;
2434       item->HitTest(aBuilder, aRect, aState, &outFrames);
2435 
2436       // For 3d transforms with preserve-3d we add hit frames into the temp list
2437       // so we can sort them later, otherwise we add them directly to the output
2438       // list.
2439       nsTArray<nsIFrame*>* writeFrames = aOutFrames;
2440       if (item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
2441           static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2442         if (outFrames.Length()) {
2443           nsDisplayTransform* transform =
2444               static_cast<nsDisplayTransform*>(item);
2445           nsPoint point = aRect.TopLeft();
2446           // A 1x1 rect means a point, otherwise use the center of the rect
2447           if (aRect.width != 1 || aRect.height != 1) {
2448             point = aRect.Center();
2449           }
2450           temp.AppendElement(
2451               FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
2452           writeFrames = &temp[temp.Length() - 1].mFrames;
2453         }
2454       } else {
2455         // We may have just finished a run of consecutive preserve-3d
2456         // transforms, so flush these into the destination array before
2457         // processing our frame list.
2458         FlushFramesArray(temp, aOutFrames);
2459       }
2460 
2461       for (uint32_t j = 0; j < outFrames.Length(); j++) {
2462         nsIFrame* f = outFrames.ElementAt(j);
2463         // Filter out some frames depending on the type of hittest
2464         // we are doing. For visibility tests, pass through all frames.
2465         // For pointer tests, only pass through frames that are styled
2466         // to receive pointer events.
2467         if (aBuilder->HitTestIsForVisibility() ||
2468             IsFrameReceivingPointerEvents(f)) {
2469           writeFrames->AppendElement(f);
2470         }
2471       }
2472 
2473       if (aBuilder->HitTestIsForVisibility()) {
2474         aState->mHitOccludingItem = [&] {
2475           if (aState->mHitOccludingItem) {
2476             // We already hit something before.
2477             return true;
2478           }
2479           if (aState->mCurrentOpacity == 1.0f &&
2480               item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) {
2481             // An opaque item always occludes everything. Note that we need to
2482             // check wrapping opacity and such as well.
2483             return true;
2484           }
2485           float threshold = aBuilder->VisibilityThreshold();
2486           if (threshold == 1.0f) {
2487             return false;
2488           }
2489           float itemOpacity = [&] {
2490             switch (item->GetType()) {
2491               case DisplayItemType::TYPE_OPACITY:
2492                 return static_cast<nsDisplayOpacity*>(item)->GetOpacity();
2493               case DisplayItemType::TYPE_BACKGROUND_COLOR:
2494                 return static_cast<nsDisplayBackgroundColor*>(item)
2495                     ->GetOpacity();
2496               default:
2497                 // Be conservative and assume it won't occlude other items.
2498                 return 0.0f;
2499             }
2500           }();
2501           return itemOpacity * aState->mCurrentOpacity >= threshold;
2502         }();
2503 
2504         if (aState->mHitOccludingItem) {
2505           // We're exiting early, so pop the remaining items off the buffer.
2506           aState->mItemBuffer.TruncateLength(itemBufferStart);
2507           break;
2508         }
2509       }
2510     }
2511   }
2512   // Clear any remaining preserve-3d transforms.
2513   FlushFramesArray(temp, aOutFrames);
2514   NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
2515                "How did we forget to pop some elements?");
2516 }
2517 
FindContentInDocument(nsDisplayItem * aItem,Document * aDoc)2518 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, Document* aDoc) {
2519   nsIFrame* f = aItem->Frame();
2520   while (f) {
2521     nsPresContext* pc = f->PresContext();
2522     if (pc->Document() == aDoc) {
2523       return f->GetContent();
2524     }
2525     f = nsLayoutUtils::GetCrossDocParentFrameInProcess(
2526         pc->PresShell()->GetRootFrame());
2527   }
2528   return nullptr;
2529 }
2530 
2531 struct ZSortItem {
2532   nsDisplayItem* item;
2533   int32_t zIndex;
2534 
ZSortItemmozilla::ZSortItem2535   explicit ZSortItem(nsDisplayItem* aItem)
2536       : item(aItem), zIndex(aItem->ZIndex()) {}
2537 
operator nsDisplayItem*mozilla::ZSortItem2538   operator nsDisplayItem*() { return item; }
2539 };
2540 
2541 struct ZOrderComparator {
operator ()mozilla::ZOrderComparator2542   bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const {
2543     // Note that we can't just take the difference of the two
2544     // z-indices here, because that might overflow a 32-bit int.
2545     return aLeft.zIndex < aRight.zIndex;
2546   }
2547 };
2548 
SortByZOrder()2549 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); }
2550 
2551 struct ContentComparator {
2552   nsIContent* mCommonAncestor;
2553 
ContentComparatormozilla::ContentComparator2554   explicit ContentComparator(nsIContent* aCommonAncestor)
2555       : mCommonAncestor(aCommonAncestor) {}
2556 
operator ()mozilla::ContentComparator2557   bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const {
2558     // It's possible that the nsIContent for aItem1 or aItem2 is in a
2559     // subdocument of commonAncestor, because display items for subdocuments
2560     // have been mixed into the same list. Ensure that we're looking at content
2561     // in commonAncestor's document.
2562     Document* commonAncestorDoc = mCommonAncestor->OwnerDoc();
2563     nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc);
2564     nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc);
2565     if (!content1 || !content2) {
2566       NS_ERROR("Document trees are mixed up!");
2567       // Something weird going on
2568       return true;
2569     }
2570     return nsLayoutUtils::CompareTreePosition(content1, content2,
2571                                               mCommonAncestor) < 0;
2572   }
2573 };
2574 
SortByContentOrder(nsIContent * aCommonAncestor)2575 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) {
2576   Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor));
2577 }
2578 
2579 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
2580 static_assert(sizeof(nsDisplayItem) <= 176, "nsDisplayItem has grown");
2581 #endif
2582 
nsDisplayItem(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)2583 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2584     : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
2585 
nsDisplayItem(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const ActiveScrolledRoot * aActiveScrolledRoot)2586 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2587                              const ActiveScrolledRoot* aActiveScrolledRoot)
2588     : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) {
2589   MOZ_COUNT_CTOR(nsDisplayItem);
2590   MOZ_ASSERT(mFrame);
2591   if (aBuilder->IsRetainingDisplayList()) {
2592     mFrame->AddDisplayItem(this);
2593   }
2594 
2595   aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
2596   NS_ASSERTION(
2597       aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
2598       "visible rect not set");
2599 
2600   mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2601 
2602   // The visible rect is for mCurrentFrame, so we have to use
2603   // mCurrentOffsetToReferenceFrame
2604   nsRect visible = aBuilder->GetVisibleRect() +
2605                    aBuilder->GetCurrentFrameOffsetToReferenceFrame();
2606   SetBuildingRect(visible);
2607 
2608   const nsStyleDisplay* disp = mFrame->StyleDisplay();
2609   if (mFrame->BackfaceIsHidden(disp)) {
2610     mItemFlags += ItemFlag::BackfaceHidden;
2611   }
2612   if (mFrame->Combines3DTransformWithAncestors()) {
2613     mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
2614   }
2615 }
2616 
SetDeletedFrame()2617 void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
2618 
HasDeletedFrame() const2619 bool nsDisplayItem::HasDeletedFrame() const {
2620   bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) ||
2621                 (GetType() == DisplayItemType::TYPE_REMOTE &&
2622                  !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader());
2623   MOZ_ASSERT(retval || mFrame);
2624   return retval;
2625 }
2626 
2627 /* static */
ForceActiveLayers()2628 bool nsDisplayItem::ForceActiveLayers() {
2629   return StaticPrefs::layers_force_active();
2630 }
2631 
ZIndex() const2632 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); }
2633 
SetClipChain(const DisplayItemClipChain * aClipChain,bool aStore)2634 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
2635                                  bool aStore) {
2636   mClipChain = aClipChain;
2637 }
2638 
GetClipWithRespectToASR(nsDisplayListBuilder * aBuilder,const ActiveScrolledRoot * aASR) const2639 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
2640     nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2641   if (const DisplayItemClip* clip =
2642           DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
2643     return Some(clip->GetClipRect());
2644   }
2645 #ifdef DEBUG
2646   MOZ_ASSERT(false, "item should have finite clip with respect to aASR");
2647 #endif
2648   return Nothing();
2649 }
2650 
GetClip() const2651 const DisplayItemClip& nsDisplayItem::GetClip() const {
2652   const DisplayItemClip* clip =
2653       DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot);
2654   return clip ? *clip : DisplayItemClip::NoClip();
2655 }
2656 
IntersectClip(nsDisplayListBuilder * aBuilder,const DisplayItemClipChain * aOther,bool aStore)2657 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
2658                                   const DisplayItemClipChain* aOther,
2659                                   bool aStore) {
2660   if (!aOther || mClipChain == aOther) {
2661     return;
2662   }
2663 
2664   // aOther might be a reference to a clip on the stack. We need to make sure
2665   // that CreateClipChainIntersection will allocate the actual intersected
2666   // clip in the builder's arena, so for the mClipChain == nullptr case,
2667   // we supply nullptr as the common ancestor so that
2668   // CreateClipChainIntersection clones the whole chain.
2669   const DisplayItemClipChain* ancestorClip =
2670       mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther)
2671                  : nullptr;
2672 
2673   SetClipChain(
2674       aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther),
2675       aStore);
2676 }
2677 
GetClippedBounds(nsDisplayListBuilder * aBuilder) const2678 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
2679   bool snap;
2680   nsRect r = GetBounds(aBuilder, &snap);
2681   return GetClip().ApplyNonRoundedIntersection(r);
2682 }
2683 
nsDisplayContainer(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const ActiveScrolledRoot * aActiveScrolledRoot,nsDisplayList * aList)2684 nsDisplayContainer::nsDisplayContainer(
2685     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2686     const ActiveScrolledRoot* aActiveScrolledRoot, nsDisplayList* aList)
2687     : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
2688       mChildren(aBuilder) {
2689   MOZ_COUNT_CTOR(nsDisplayContainer);
2690   mChildren.AppendToTop(aList);
2691   UpdateBounds(aBuilder);
2692 
2693   // Clear and store the clip chain set by nsDisplayItem constructor.
2694   nsDisplayItem::SetClipChain(nullptr, true);
2695 }
2696 
GetPaintRect(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)2697 nsRect nsDisplayItem::GetPaintRect(nsDisplayListBuilder* aBuilder,
2698                                    gfxContext* aCtx) {
2699   bool dummy;
2700   nsRect result = GetBounds(aBuilder, &dummy);
2701   if (aCtx) {
2702     result.IntersectRect(result,
2703                          nsLayoutUtils::RoundGfxRectToAppRect(
2704                              aCtx->GetClipExtents(),
2705                              mFrame->PresContext()->AppUnitsPerDevPixel()));
2706   }
2707   return result;
2708 }
2709 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)2710 bool nsDisplayContainer::CreateWebRenderCommands(
2711     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2712     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2713     nsDisplayListBuilder* aDisplayListBuilder) {
2714   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
2715       GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
2716       false);
2717   return true;
2718 }
2719 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const2720 nsRect nsDisplayContainer::GetBounds(nsDisplayListBuilder* aBuilder,
2721                                      bool* aSnap) const {
2722   *aSnap = false;
2723   return mBounds;
2724 }
2725 
GetComponentAlphaBounds(nsDisplayListBuilder * aBuilder) const2726 nsRect nsDisplayContainer::GetComponentAlphaBounds(
2727     nsDisplayListBuilder* aBuilder) const {
2728   return mChildren.GetComponentAlphaBounds(aBuilder);
2729 }
2730 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,const nsRect & aListBounds)2731 static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2732                                 nsDisplayList* aList,
2733                                 const nsRect& aListBounds) {
2734   return aList->GetOpaqueRegion(aBuilder);
2735 }
2736 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const2737 nsRegion nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2738                                              bool* aSnap) const {
2739   return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
2740                                     GetBounds(aBuilder, aSnap));
2741 }
2742 
GetClipWithRespectToASR(nsDisplayListBuilder * aBuilder,const ActiveScrolledRoot * aASR) const2743 Maybe<nsRect> nsDisplayContainer::GetClipWithRespectToASR(
2744     nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
2745   // Our children should have finite bounds with respect to |aASR|.
2746   if (aASR == mActiveScrolledRoot) {
2747     return Some(mBounds);
2748   }
2749 
2750   return Some(mChildren.GetClippedBoundsWithRespectToASR(aBuilder, aASR));
2751 }
2752 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)2753 void nsDisplayContainer::HitTest(nsDisplayListBuilder* aBuilder,
2754                                  const nsRect& aRect, HitTestState* aState,
2755                                  nsTArray<nsIFrame*>* aOutFrames) {
2756   mChildren.HitTest(aBuilder, aRect, aState, aOutFrames);
2757 }
2758 
UpdateBounds(nsDisplayListBuilder * aBuilder)2759 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder* aBuilder) {
2760   // Container item bounds are expected to be clipped.
2761   mBounds =
2762       mChildren.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
2763 }
2764 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const2765 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder,
2766                                       bool* aSnap) const {
2767   *aSnap = true;
2768   return mBounds;
2769 }
2770 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)2771 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
2772                                 gfxContext* aCtx) {
2773   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2774   DrawTarget* drawTarget = aCtx->GetDrawTarget();
2775   Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx),
2776                                   appUnitsPerDevPixel, *drawTarget);
2777   drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
2778 }
2779 
WriteDebugInfo(std::stringstream & aStream)2780 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) {
2781   aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
2782           << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
2783           << ")";
2784 }
2785 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)2786 bool nsDisplaySolidColor::CreateWebRenderCommands(
2787     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2788     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2789     nsDisplayListBuilder* aDisplayListBuilder) {
2790   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
2791       mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
2792   wr::LayoutRect r = wr::ToLayoutRect(bounds);
2793   aBuilder.PushRect(r, r, !BackfaceIsHidden(), false,
2794                     wr::ToColorF(ToDeviceColor(mColor)));
2795 
2796   return true;
2797 }
2798 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const2799 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
2800                                             bool* aSnap) const {
2801   *aSnap = true;
2802   return mRegion.GetBounds();
2803 }
2804 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)2805 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
2806                                       gfxContext* aCtx) {
2807   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2808   DrawTarget* drawTarget = aCtx->GetDrawTarget();
2809   ColorPattern color(ToDeviceColor(mColor));
2810   for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2811     Rect rect =
2812         NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
2813     drawTarget->FillRect(rect, color);
2814   }
2815 }
2816 
WriteDebugInfo(std::stringstream & aStream)2817 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) {
2818   aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255)
2819           << "," << int(mColor.b * 255) << "," << mColor.a << ")";
2820 }
2821 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)2822 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
2823     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
2824     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
2825     nsDisplayListBuilder* aDisplayListBuilder) {
2826   for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
2827     nsRect rect = iter.Get();
2828     LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
2829         rect, mFrame->PresContext()->AppUnitsPerDevPixel());
2830     wr::LayoutRect r = wr::ToLayoutRect(layerRects);
2831     aBuilder.PushRect(r, r, !BackfaceIsHidden(), false,
2832                       wr::ToColorF(ToDeviceColor(mColor)));
2833   }
2834 
2835   return true;
2836 }
2837 
RegisterThemeGeometry(nsDisplayListBuilder * aBuilder,nsDisplayItem * aItem,nsIFrame * aFrame,nsITheme::ThemeGeometryType aType)2838 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder,
2839                                   nsDisplayItem* aItem, nsIFrame* aFrame,
2840                                   nsITheme::ThemeGeometryType aType) {
2841   if (aBuilder->IsInChromeDocumentOrPopup()) {
2842     nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2843     bool preservesAxisAlignedRectangles = false;
2844     nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(
2845         aFrame, aFrame->GetRectRelativeToSelf(), displayRoot,
2846         &preservesAxisAlignedRectangles);
2847     if (preservesAxisAlignedRectangles) {
2848       aBuilder->RegisterThemeGeometry(
2849           aType, aItem,
2850           LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels(
2851               aFrame->PresContext()->AppUnitsPerDevPixel())));
2852     }
2853   }
2854 }
2855 
2856 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
2857 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
GetViewportRectRelativeToReferenceFrame(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)2858 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
2859     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
2860   nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
2861   nsRect rootRect = rootFrame->GetRectRelativeToSelf();
2862   if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) ==
2863       nsLayoutUtils::TRANSFORM_SUCCEEDED) {
2864     return Some(rootRect + aBuilder->ToReferenceFrame(aFrame));
2865   }
2866   return Nothing();
2867 }
2868 
2869 /* static */ nsDisplayBackgroundImage::InitData
GetInitData(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,uint16_t aLayer,const nsRect & aBackgroundRect,ComputedStyle * aBackgroundStyle)2870 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder,
2871                                       nsIFrame* aFrame, uint16_t aLayer,
2872                                       const nsRect& aBackgroundRect,
2873                                       ComputedStyle* aBackgroundStyle) {
2874   nsPresContext* presContext = aFrame->PresContext();
2875   uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2876   const nsStyleImageLayers::Layer& layer =
2877       aBackgroundStyle->StyleBackground()->mImage.mLayers[aLayer];
2878 
2879   bool isTransformedFixed;
2880   nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
2881       presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer,
2882       &isTransformedFixed);
2883 
2884   // background-attachment:fixed is treated as background-attachment:scroll
2885   // if it's affected by a transform.
2886   // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
2887   bool shouldTreatAsFixed =
2888       layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2889       !isTransformedFixed;
2890 
2891   bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsNone();
2892   bool isRasterImage = state.mImageRenderer.IsRasterImage();
2893   nsCOMPtr<imgIContainer> image;
2894   if (isRasterImage) {
2895     image = state.mImageRenderer.GetImage();
2896   }
2897   return InitData{aBuilder,        aBackgroundStyle, image,
2898                   aBackgroundRect, state.mFillArea,  state.mDestArea,
2899                   aLayer,          isRasterImage,    shouldFixToViewport};
2900 }
2901 
nsDisplayBackgroundImage(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const InitData & aInitData,nsIFrame * aFrameForBounds)2902 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
2903     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aInitData,
2904     nsIFrame* aFrameForBounds)
2905     : nsPaintedDisplayItem(aBuilder, aFrame),
2906       mBackgroundStyle(aInitData.backgroundStyle),
2907       mImage(aInitData.image),
2908       mDependentFrame(nullptr),
2909       mBackgroundRect(aInitData.backgroundRect),
2910       mFillRect(aInitData.fillArea),
2911       mDestRect(aInitData.destArea),
2912       mLayer(aInitData.layer),
2913       mIsRasterImage(aInitData.isRasterImage),
2914       mShouldFixToViewport(aInitData.shouldFixToViewport) {
2915   MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
2916 #ifdef DEBUG
2917   if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) {
2918     // If this changes, then you also need to adjust css::ImageLoader to
2919     // invalidate mFrame as needed.
2920     MOZ_ASSERT(mFrame->IsCanvasFrame() ||
2921                mFrame->IsFrameOfType(nsIFrame::eTablePart));
2922   }
2923 #endif
2924 
2925   mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
2926   if (mShouldFixToViewport) {
2927     // Expand the item's visible rect to cover the entire bounds, limited to the
2928     // viewport rect. This is necessary because the background's clip can move
2929     // asynchronously.
2930     if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
2931             aInitData.builder, mFrame)) {
2932       SetBuildingRect(mBounds.Intersect(*viewportRect));
2933     }
2934   }
2935 }
2936 
~nsDisplayBackgroundImage()2937 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
2938   MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
2939   if (mDependentFrame) {
2940     mDependentFrame->RemoveDisplayItem(this);
2941   }
2942 }
2943 
GetBackgroundComputedStyleFrame(nsIFrame * aFrame)2944 static nsIFrame* GetBackgroundComputedStyleFrame(nsIFrame* aFrame) {
2945   nsIFrame* f;
2946   if (!nsCSSRendering::FindBackgroundFrame(aFrame, &f)) {
2947     // We don't want to bail out if moz-appearance is set on a root
2948     // node. If it has a parent content node, bail because it's not
2949     // a root, other wise keep going in order to let the theme stuff
2950     // draw the background. The canvas really should be drawing the
2951     // bg, but there's no way to hook that up via css.
2952     if (!aFrame->StyleDisplay()->HasAppearance()) {
2953       return nullptr;
2954     }
2955 
2956     nsIContent* content = aFrame->GetContent();
2957     if (!content || content->GetParent()) {
2958       return nullptr;
2959     }
2960 
2961     f = aFrame;
2962   }
2963   return f;
2964 }
2965 
SetBackgroundClipRegion(DisplayListClipState::AutoSaveRestore & aClipState,nsIFrame * aFrame,const nsStyleImageLayers::Layer & aLayer,const nsRect & aBackgroundRect,bool aWillPaintBorder)2966 static void SetBackgroundClipRegion(
2967     DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame,
2968     const nsStyleImageLayers::Layer& aLayer, const nsRect& aBackgroundRect,
2969     bool aWillPaintBorder) {
2970   nsCSSRendering::ImageLayerClipState clip;
2971   nsCSSRendering::GetImageLayerClip(
2972       aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect,
2973       aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
2974 
2975   if (clip.mHasAdditionalBGClipArea) {
2976     aClipState.ClipContentDescendants(
2977         clip.mAdditionalBGClipArea, clip.mBGClipArea,
2978         clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2979   } else {
2980     aClipState.ClipContentDescendants(
2981         clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2982   }
2983 }
2984 
2985 /**
2986  * This is used for the find bar highlighter overlay. It's only accessible
2987  * through the AnonymousContent API, so it's not exposed to general web pages.
2988  */
SpecialCutoutRegionCase(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsRect & aBackgroundRect,nsDisplayList * aList,nscolor aColor)2989 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
2990                                     nsIFrame* aFrame,
2991                                     const nsRect& aBackgroundRect,
2992                                     nsDisplayList* aList, nscolor aColor) {
2993   nsIContent* content = aFrame->GetContent();
2994   if (!content) {
2995     return false;
2996   }
2997 
2998   void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
2999   if (!cutoutRegion) {
3000     return false;
3001   }
3002 
3003   if (NS_GET_A(aColor) == 0) {
3004     return true;
3005   }
3006 
3007   nsRegion region;
3008   region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
3009   region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
3010   aList->AppendNewToTop<nsDisplaySolidColorRegion>(aBuilder, aFrame, region,
3011                                                    aColor);
3012 
3013   return true;
3014 }
3015 
3016 enum class TableType : uint8_t {
3017   Table,
3018   TableCol,
3019   TableColGroup,
3020   TableRow,
3021   TableRowGroup,
3022   TableCell,
3023 
3024   MAX,
3025 };
3026 
3027 enum class TableTypeBits : uint8_t { Count = 3 };
3028 
3029 static_assert(static_cast<uint8_t>(TableType::MAX) <
3030                   (1 << (static_cast<uint8_t>(TableTypeBits::Count) + 1)),
3031               "TableType cannot fit with TableTypeBits::Count");
3032 TableType GetTableTypeFromFrame(nsIFrame* aFrame);
3033 
CalculateTablePerFrameKey(const uint16_t aIndex,const TableType aType)3034 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex,
3035                                           const TableType aType) {
3036   const uint32_t key = (aIndex << static_cast<uint8_t>(TableTypeBits::Count)) |
3037                        static_cast<uint8_t>(aType);
3038 
3039   return static_cast<uint16_t>(key);
3040 }
3041 
CreateBackgroundImage(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsIFrame * aSecondaryFrame,const nsDisplayBackgroundImage::InitData & aBgData)3042 static nsDisplayBackgroundImage* CreateBackgroundImage(
3043     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3044     const nsDisplayBackgroundImage::InitData& aBgData) {
3045   const auto index = aBgData.layer;
3046 
3047   if (aSecondaryFrame) {
3048     const auto tableType = GetTableTypeFromFrame(aFrame);
3049     const uint16_t tableItemIndex = CalculateTablePerFrameKey(index, tableType);
3050 
3051     return MakeDisplayItemWithIndex<nsDisplayTableBackgroundImage>(
3052         aBuilder, aSecondaryFrame, tableItemIndex, aBgData, aFrame);
3053   }
3054 
3055   return MakeDisplayItemWithIndex<nsDisplayBackgroundImage>(aBuilder, aFrame,
3056                                                             index, aBgData);
3057 }
3058 
CreateThemedBackground(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsIFrame * aSecondaryFrame,nsRect & aBgRect)3059 static nsDisplayThemedBackground* CreateThemedBackground(
3060     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3061     nsRect& aBgRect) {
3062   if (aSecondaryFrame) {
3063     const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3064     return MakeDisplayItemWithIndex<nsDisplayTableThemedBackground>(
3065         aBuilder, aSecondaryFrame, index, aBgRect, aFrame);
3066   }
3067 
3068   return MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, aBgRect);
3069 }
3070 
CreateBackgroundColor(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsIFrame * aSecondaryFrame,nsRect & aBgRect,ComputedStyle * aBgSC,nscolor aColor)3071 static nsDisplayBackgroundColor* CreateBackgroundColor(
3072     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
3073     nsRect& aBgRect, ComputedStyle* aBgSC, nscolor aColor) {
3074   if (aSecondaryFrame) {
3075     const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
3076     return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>(
3077         aBuilder, aSecondaryFrame, index, aBgRect, aBgSC, aColor, aFrame);
3078   }
3079 
3080   return MakeDisplayItem<nsDisplayBackgroundColor>(aBuilder, aFrame, aBgRect,
3081                                                    aBgSC, aColor);
3082 }
3083 
3084 /*static*/
AppendBackgroundItemsToTop(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsRect & aBackgroundRect,nsDisplayList * aList,bool aAllowWillPaintBorderOptimization,ComputedStyle * aComputedStyle,const nsRect & aBackgroundOriginRect,nsIFrame * aSecondaryReferenceFrame,Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> * aAutoBuildingDisplayList)3085 AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3086     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3087     const nsRect& aBackgroundRect, nsDisplayList* aList,
3088     bool aAllowWillPaintBorderOptimization, ComputedStyle* aComputedStyle,
3089     const nsRect& aBackgroundOriginRect, nsIFrame* aSecondaryReferenceFrame,
3090     Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
3091         aAutoBuildingDisplayList) {
3092   ComputedStyle* bgSC = aComputedStyle;
3093   const nsStyleBackground* bg = nullptr;
3094   nsRect bgRect = aBackgroundRect;
3095   nsRect bgOriginRect = bgRect;
3096   if (!aBackgroundOriginRect.IsEmpty()) {
3097     bgOriginRect = aBackgroundOriginRect;
3098   }
3099   nsPresContext* presContext = aFrame->PresContext();
3100   bool isThemed = aFrame->IsThemed();
3101   nsIFrame* dependentFrame = nullptr;
3102   if (!isThemed) {
3103     if (!bgSC) {
3104       dependentFrame = GetBackgroundComputedStyleFrame(aFrame);
3105       if (dependentFrame) {
3106         bgSC = dependentFrame->Style();
3107         if (dependentFrame == aFrame) {
3108           dependentFrame = nullptr;
3109         }
3110       }
3111     }
3112     if (bgSC) {
3113       bg = bgSC->StyleBackground();
3114     }
3115   }
3116 
3117   bool drawBackgroundColor = false;
3118   // XUL root frames need special handling for now even though they return true
3119   // from nsCSSRendering::IsCanvasFrame they rely on us painting the background
3120   // image from here, see bug 1665476.
3121   bool drawBackgroundImage =
3122       aFrame->IsXULRootFrame() && aFrame->ComputeShouldPaintBackground().mImage;
3123   nscolor color = NS_RGBA(0, 0, 0, 0);
3124   if (!nsCSSRendering::IsCanvasFrame(aFrame) && bg) {
3125     color = nsCSSRendering::DetermineBackgroundColor(
3126         presContext, bgSC, aFrame, drawBackgroundImage, drawBackgroundColor);
3127   }
3128 
3129   if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
3130                               color)) {
3131     return AppendedBackgroundType::None;
3132   }
3133 
3134   const nsStyleBorder* borderStyle = aFrame->StyleBorder();
3135   const nsStyleEffects* effectsStyle = aFrame->StyleEffects();
3136   bool hasInsetShadow = effectsStyle->HasBoxShadowWithInset(true);
3137   bool willPaintBorder = aAllowWillPaintBorderOptimization && !isThemed &&
3138                          !hasInsetShadow && borderStyle->HasBorder();
3139 
3140   // An auxiliary list is necessary in case we have background blending; if that
3141   // is the case, background items need to be wrapped by a blend container to
3142   // isolate blending to the background
3143   nsDisplayList bgItemList(aBuilder);
3144   // Even if we don't actually have a background color to paint, we may still
3145   // need to create an item for hit testing and we still need to create an item
3146   // for background-color animations.
3147   if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) ||
3148       aBuilder->IsForEventDelivery() ||
3149       EffectCompositor::HasAnimationsForCompositor(
3150           aFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3151     if (aAutoBuildingDisplayList && !*aAutoBuildingDisplayList) {
3152       nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3153       aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3154                                         aBuilder->GetVisibleRect() + offset,
3155                                         aBuilder->GetDirtyRect() + offset);
3156     }
3157     Maybe<DisplayListClipState::AutoSaveRestore> clipState;
3158     nsRect bgColorRect = bgRect;
3159     if (bg && !aBuilder->IsForEventDelivery()) {
3160       // Disable the will-paint-border optimization for background
3161       // colors with no border-radius. Enabling it for background colors
3162       // doesn't help much (there are no tiling issues) and clipping the
3163       // background breaks detection of the element's border-box being
3164       // opaque. For nonzero border-radius we still need it because we
3165       // want to inset the background if possible to avoid antialiasing
3166       // artifacts along the rounded corners.
3167       bool useWillPaintBorderOptimization =
3168           willPaintBorder &&
3169           nsLayoutUtils::HasNonZeroCorner(borderStyle->mBorderRadius);
3170 
3171       nsCSSRendering::ImageLayerClipState clip;
3172       nsCSSRendering::GetImageLayerClip(
3173           bg->BottomLayer(), aFrame, *aFrame->StyleBorder(), bgRect, bgRect,
3174           useWillPaintBorderOptimization,
3175           aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3176 
3177       bgColorRect = bgColorRect.Intersect(clip.mBGClipArea);
3178       if (clip.mHasAdditionalBGClipArea) {
3179         bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea);
3180       }
3181       if (clip.mHasRoundedCorners) {
3182         clipState.emplace(aBuilder);
3183         clipState->ClipContentDescendants(clip.mBGClipArea, clip.mRadii);
3184       }
3185     }
3186 
3187     nsDisplayBackgroundColor* bgItem = CreateBackgroundColor(
3188         aBuilder, aFrame, aSecondaryReferenceFrame, bgColorRect, bgSC,
3189         drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0));
3190 
3191     if (bgItem) {
3192       bgItem->SetDependentFrame(aBuilder, dependentFrame);
3193       bgItemList.AppendToTop(bgItem);
3194     }
3195   }
3196 
3197   if (isThemed) {
3198     nsDisplayThemedBackground* bgItem = CreateThemedBackground(
3199         aBuilder, aFrame, aSecondaryReferenceFrame, bgRect);
3200 
3201     if (bgItem) {
3202       bgItem->Init(aBuilder);
3203       bgItemList.AppendToTop(bgItem);
3204     }
3205 
3206     if (!bgItemList.IsEmpty()) {
3207       aList->AppendToTop(&bgItemList);
3208       return AppendedBackgroundType::ThemedBackground;
3209     }
3210 
3211     return AppendedBackgroundType::None;
3212   }
3213 
3214   if (!bg || !drawBackgroundImage) {
3215     if (!bgItemList.IsEmpty()) {
3216       aList->AppendToTop(&bgItemList);
3217       return AppendedBackgroundType::Background;
3218     }
3219 
3220     return AppendedBackgroundType::None;
3221   }
3222 
3223   const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
3224 
3225   bool needBlendContainer = false;
3226 
3227   // Passing bg == nullptr in this macro will result in one iteration with
3228   // i = 0.
3229   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
3230     if (bg->mImage.mLayers[i].mImage.IsNone()) {
3231       continue;
3232     }
3233 
3234     if (aAutoBuildingDisplayList && !*aAutoBuildingDisplayList) {
3235       nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame);
3236       aAutoBuildingDisplayList->emplace(aBuilder, aFrame,
3237                                         aBuilder->GetVisibleRect() + offset,
3238                                         aBuilder->GetDirtyRect() + offset);
3239     }
3240 
3241     if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3242       needBlendContainer = true;
3243     }
3244 
3245     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3246     if (!aBuilder->IsForEventDelivery()) {
3247       const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
3248       SetBackgroundClipRegion(clipState, aFrame, layer, bgRect,
3249                               willPaintBorder);
3250     }
3251 
3252     nsDisplayList thisItemList(aBuilder);
3253     nsDisplayBackgroundImage::InitData bgData =
3254         nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect,
3255                                               bgSC);
3256 
3257     if (bgData.shouldFixToViewport) {
3258       auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
3259       nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3260           aBuilder, aFrame, aBuilder->GetVisibleRect(),
3261           aBuilder->GetDirtyRect());
3262 
3263       nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
3264           aBuilder);
3265       if (displayData) {
3266         asrSetter.SetCurrentActiveScrolledRoot(
3267             displayData->mContainingBlockActiveScrolledRoot);
3268         if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
3269           // Override the dirty rect on the builder to be the dirty rect of
3270           // the viewport.
3271           // displayData->mDirtyRect is relative to the presshell's viewport
3272           // frame (the root frame), and we need it to be relative to aFrame.
3273           nsIFrame* rootFrame =
3274               aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
3275           // There cannot be any transforms between aFrame and rootFrame
3276           // because then bgData.shouldFixToViewport would have been false.
3277           nsRect visibleRect =
3278               displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame);
3279           aBuilder->SetVisibleRect(visibleRect);
3280           nsRect dirtyRect =
3281               displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
3282           aBuilder->SetDirtyRect(dirtyRect);
3283         }
3284       }
3285 
3286       nsDisplayBackgroundImage* bgItem = nullptr;
3287       {
3288         // The clip is captured by the nsDisplayFixedPosition, so clear the
3289         // clip for the nsDisplayBackgroundImage inside.
3290         DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
3291         bgImageClip.Clear();
3292         bgItem = CreateBackgroundImage(aBuilder, aFrame,
3293                                        aSecondaryReferenceFrame, bgData);
3294       }
3295       if (bgItem) {
3296         bgItem->SetDependentFrame(aBuilder, dependentFrame);
3297 
3298         thisItemList.AppendToTop(
3299             nsDisplayFixedPosition::CreateForFixedBackground(
3300                 aBuilder, aFrame, aSecondaryReferenceFrame, bgItem, i, asr));
3301       }
3302     } else {  // bgData.shouldFixToViewport == false
3303       nsDisplayBackgroundImage* bgItem = CreateBackgroundImage(
3304           aBuilder, aFrame, aSecondaryReferenceFrame, bgData);
3305       if (bgItem) {
3306         bgItem->SetDependentFrame(aBuilder, dependentFrame);
3307         thisItemList.AppendToTop(bgItem);
3308       }
3309     }
3310 
3311     if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) {
3312       DisplayListClipState::AutoSaveRestore blendClip(aBuilder);
3313       // asr is scrolled. Even if we wrap a fixed background layer, that's
3314       // fine, because the item will have a scrolled clip that limits the
3315       // item with respect to asr.
3316       if (aSecondaryReferenceFrame) {
3317         const auto tableType = GetTableTypeFromFrame(aFrame);
3318         const uint16_t index = CalculateTablePerFrameKey(i + 1, tableType);
3319 
3320         thisItemList.AppendNewToTopWithIndex<nsDisplayTableBlendMode>(
3321             aBuilder, aSecondaryReferenceFrame, index, &thisItemList,
3322             bg->mImage.mLayers[i].mBlendMode, asr, aFrame, true);
3323       } else {
3324         thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
3325             aBuilder, aFrame, i + 1, &thisItemList,
3326             bg->mImage.mLayers[i].mBlendMode, asr, true);
3327       }
3328     }
3329     bgItemList.AppendToTop(&thisItemList);
3330   }
3331 
3332   if (needBlendContainer) {
3333     DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder);
3334 
3335     bgItemList.AppendToTop(
3336         nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3337             aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr));
3338   }
3339 
3340   if (!bgItemList.IsEmpty()) {
3341     aList->AppendToTop(&bgItemList);
3342     return AppendedBackgroundType::Background;
3343   }
3344 
3345   return AppendedBackgroundType::None;
3346 }
3347 
3348 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3349 // intersects aRect.  Assumes that the unrounded border has already
3350 // been checked for intersection.
RoundedBorderIntersectsRect(nsIFrame * aFrame,const nsPoint & aFrameToReferenceFrame,const nsRect & aTestRect)3351 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame,
3352                                         const nsPoint& aFrameToReferenceFrame,
3353                                         const nsRect& aTestRect) {
3354   if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize())
3355            .Intersects(aTestRect)) {
3356     return false;
3357   }
3358 
3359   nscoord radii[8];
3360   return !aFrame->GetBorderRadii(radii) ||
3361          nsLayoutUtils::RoundedRectIntersectsRect(
3362              nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii,
3363              aTestRect);
3364 }
3365 
3366 // Returns TRUE if aContainedRect is guaranteed to be contained in
3367 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3368 // handled conservatively by returning FALSE in some situations where
3369 // a more thorough analysis could return TRUE.
3370 //
3371 // See also RoundedRectIntersectsRect.
RoundedRectContainsRect(const nsRect & aRoundedRect,const nscoord aRadii[8],const nsRect & aContainedRect)3372 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
3373                                     const nscoord aRadii[8],
3374                                     const nsRect& aContainedRect) {
3375   nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii,
3376                                                          aContainedRect);
3377   return rgn.Contains(aContainedRect);
3378 }
3379 
CanApplyOpacity(WebRenderLayerManager * aManager,nsDisplayListBuilder * aBuilder) const3380 bool nsDisplayBackgroundImage::CanApplyOpacity(
3381     WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3382   // Bug 1752919: WebRender does not properly handle opacity flattening for
3383   // images larger than 4096 dest pixels.
3384   static const nscoord WR_NSCOORD_LIMIT =
3385       NSIntPixelsToAppUnits(4096, AppUnitsPerCSSPixel());
3386   if MOZ_UNLIKELY (mDestRect.width > WR_NSCOORD_LIMIT ||
3387                    mDestRect.height > WR_NSCOORD_LIMIT) {
3388     return false;
3389   }
3390 
3391   return CanBuildWebRenderDisplayItems(aManager, aBuilder);
3392 }
3393 
CanBuildWebRenderDisplayItems(WebRenderLayerManager * aManager,nsDisplayListBuilder * aBuilder) const3394 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3395     WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3396   return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip !=
3397              StyleGeometryBox::Text &&
3398          nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3399              aManager, *StyleFrame()->PresContext(), StyleFrame(),
3400              mBackgroundStyle->StyleBackground(), mLayer,
3401              aBuilder->GetBackgroundPaintFlags());
3402 }
3403 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)3404 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3405     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3406     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3407     nsDisplayListBuilder* aDisplayListBuilder) {
3408   if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(),
3409                                      aDisplayListBuilder)) {
3410     return false;
3411   }
3412 
3413   uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
3414   bool dummy;
3415   nsCSSRendering::PaintBGParams params =
3416       nsCSSRendering::PaintBGParams::ForSingleLayer(
3417           *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy),
3418           mBackgroundRect, StyleFrame(), paintFlags, mLayer,
3419           CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity());
3420   params.bgClipRect = &mBounds;
3421   ImgDrawResult result =
3422       nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3423           params, aBuilder, aResources, aSc, aManager, this);
3424   if (result == ImgDrawResult::NOT_SUPPORTED) {
3425     return false;
3426   }
3427 
3428   nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
3429   return true;
3430 }
3431 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)3432 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
3433                                        const nsRect& aRect,
3434                                        HitTestState* aState,
3435                                        nsTArray<nsIFrame*>* aOutFrames) {
3436   if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3437     aOutFrames->AppendElement(mFrame);
3438   }
3439 }
3440 
GetInsideClipRect(const nsDisplayItem * aItem,StyleGeometryBox aClip,const nsRect & aRect,const nsRect & aBackgroundRect)3441 static nsRect GetInsideClipRect(const nsDisplayItem* aItem,
3442                                 StyleGeometryBox aClip, const nsRect& aRect,
3443                                 const nsRect& aBackgroundRect) {
3444   if (aRect.IsEmpty()) {
3445     return {};
3446   }
3447 
3448   nsIFrame* frame = aItem->Frame();
3449 
3450   nsRect clipRect = aBackgroundRect;
3451   if (frame->IsCanvasFrame()) {
3452     nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3453     clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
3454   } else if (aClip == StyleGeometryBox::PaddingBox ||
3455              aClip == StyleGeometryBox::ContentBox) {
3456     nsMargin border = frame->GetUsedBorder();
3457     if (aClip == StyleGeometryBox::ContentBox) {
3458       border += frame->GetUsedPadding();
3459     }
3460     border.ApplySkipSides(frame->GetSkipSides());
3461     clipRect.Deflate(border);
3462   }
3463 
3464   return clipRect.Intersect(aRect);
3465 }
3466 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const3467 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
3468     nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3469   nsRegion result;
3470   *aSnap = false;
3471 
3472   if (!mBackgroundStyle) {
3473     return result;
3474   }
3475 
3476   *aSnap = true;
3477 
3478   // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
3479   // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
3480   // which expects frames to be sent to it in content order, not reverse
3481   // content order which we'll produce here.
3482   // Of course, if there's only one frame in the flow, it doesn't matter.
3483   if (mFrame->StyleBorder()->mBoxDecorationBreak ==
3484           StyleBoxDecorationBreak::Clone ||
3485       (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
3486     const nsStyleImageLayers::Layer& layer =
3487         mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3488     if (layer.mImage.IsOpaque() && layer.mBlendMode == StyleBlend::Normal &&
3489         layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
3490         layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
3491         layer.mClip != StyleGeometryBox::Text) {
3492       result = GetInsideClipRect(this, layer.mClip, mBounds, mBackgroundRect);
3493     }
3494   }
3495 
3496   return result;
3497 }
3498 
IsUniform(nsDisplayListBuilder * aBuilder) const3499 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform(
3500     nsDisplayListBuilder* aBuilder) const {
3501   if (!mBackgroundStyle) {
3502     return Some(NS_RGBA(0, 0, 0, 0));
3503   }
3504   return Nothing();
3505 }
3506 
GetPositioningArea() const3507 nsRect nsDisplayBackgroundImage::GetPositioningArea() const {
3508   if (!mBackgroundStyle) {
3509     return nsRect();
3510   }
3511   nsIFrame* attachedToFrame;
3512   bool transformedFixed;
3513   return nsCSSRendering::ComputeImageLayerPositioningArea(
3514              mFrame->PresContext(), mFrame, mBackgroundRect,
3515              mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer],
3516              &attachedToFrame, &transformedFixed) +
3517          ToReferenceFrame();
3518 }
3519 
RenderingMightDependOnPositioningAreaSizeChange() const3520 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
3521     const {
3522   if (!mBackgroundStyle) {
3523     return false;
3524   }
3525 
3526   nscoord radii[8];
3527   if (mFrame->GetBorderRadii(radii)) {
3528     // A change in the size of the positioning area might change the position
3529     // of the rounded corners.
3530     return true;
3531   }
3532 
3533   const nsStyleImageLayers::Layer& layer =
3534       mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3535   return layer.RenderingMightDependOnPositioningAreaSizeChange();
3536 }
3537 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)3538 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
3539                                      gfxContext* aCtx) {
3540   PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &mBounds);
3541 }
3542 
PaintInternal(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,const nsRect & aBounds,nsRect * aClipRect)3543 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
3544                                              gfxContext* aCtx,
3545                                              const nsRect& aBounds,
3546                                              nsRect* aClipRect) {
3547   gfxContext* ctx = aCtx;
3548   StyleGeometryBox clip =
3549       mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip;
3550 
3551   if (clip == StyleGeometryBox::Text) {
3552     if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect,
3553                                  aBuilder)) {
3554       return;
3555     }
3556   }
3557 
3558   nsCSSRendering::PaintBGParams params =
3559       nsCSSRendering::PaintBGParams::ForSingleLayer(
3560           *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
3561           aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER,
3562           1.0f);
3563   params.bgClipRect = aClipRect;
3564   ImgDrawResult result = nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
3565 
3566   if (clip == StyleGeometryBox::Text) {
3567     ctx->PopGroupAndBlend();
3568   }
3569 
3570   nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
3571 }
3572 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const3573 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
3574     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3575     nsRegion* aInvalidRegion) const {
3576   if (!mBackgroundStyle) {
3577     return;
3578   }
3579 
3580   const auto* geometry =
3581       static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
3582 
3583   bool snap;
3584   nsRect bounds = GetBounds(aBuilder, &snap);
3585   nsRect positioningArea = GetPositioningArea();
3586   if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
3587       (positioningArea.Size() != geometry->mPositioningArea.Size() &&
3588        RenderingMightDependOnPositioningAreaSizeChange())) {
3589     // Positioning area changed in a way that could cause everything to change,
3590     // so invalidate everything (both old and new painting areas).
3591     aInvalidRegion->Or(bounds, geometry->mBounds);
3592     return;
3593   }
3594   if (!mDestRect.IsEqualInterior(geometry->mDestRect)) {
3595     // Dest area changed in a way that could cause everything to change,
3596     // so invalidate everything (both old and new painting areas).
3597     aInvalidRegion->Or(bounds, geometry->mBounds);
3598     return;
3599   }
3600   if (aBuilder->ShouldSyncDecodeImages()) {
3601     const auto& image =
3602         mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mImage;
3603     if (image.IsImageRequestType() &&
3604         geometry->ShouldInvalidateToSyncDecodeImages()) {
3605       aInvalidRegion->Or(*aInvalidRegion, bounds);
3606     }
3607   }
3608   if (!bounds.IsEqualInterior(geometry->mBounds)) {
3609     // Positioning area is unchanged, so invalidate just the change in the
3610     // painting area.
3611     aInvalidRegion->Xor(bounds, geometry->mBounds);
3612   }
3613 }
3614 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const3615 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder,
3616                                            bool* aSnap) const {
3617   *aSnap = true;
3618   return mBounds;
3619 }
3620 
GetBoundsInternal(nsDisplayListBuilder * aBuilder,nsIFrame * aFrameForBounds)3621 nsRect nsDisplayBackgroundImage::GetBoundsInternal(
3622     nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) {
3623   // This allows nsDisplayTableBackgroundImage to change the frame used for
3624   // bounds calculation.
3625   nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame;
3626 
3627   nsPresContext* presContext = frame->PresContext();
3628 
3629   if (!mBackgroundStyle) {
3630     return nsRect();
3631   }
3632 
3633   nsRect clipRect = mBackgroundRect;
3634   if (frame->IsCanvasFrame()) {
3635     nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
3636     clipRect = canvasFrame->CanvasArea() + ToReferenceFrame();
3637   }
3638   const nsStyleImageLayers::Layer& layer =
3639       mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
3640   return nsCSSRendering::GetBackgroundLayerRect(
3641       presContext, frame, mBackgroundRect, clipRect, layer,
3642       aBuilder->GetBackgroundPaintFlags());
3643 }
3644 
nsDisplayTableBackgroundImage(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const InitData & aData,nsIFrame * aCellFrame)3645 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
3646     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aData,
3647     nsIFrame* aCellFrame)
3648     : nsDisplayBackgroundImage(aBuilder, aFrame, aData, aCellFrame),
3649       mStyleFrame(aCellFrame) {
3650   if (aBuilder->IsRetainingDisplayList()) {
3651     mStyleFrame->AddDisplayItem(this);
3652   }
3653 }
3654 
~nsDisplayTableBackgroundImage()3655 nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage() {
3656   if (mStyleFrame) {
3657     mStyleFrame->RemoveDisplayItem(this);
3658   }
3659 }
3660 
IsInvalid(nsRect & aRect) const3661 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const {
3662   bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
3663   aRect += ToReferenceFrame();
3664   return result;
3665 }
3666 
nsDisplayThemedBackground(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsRect & aBackgroundRect)3667 nsDisplayThemedBackground::nsDisplayThemedBackground(
3668     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3669     const nsRect& aBackgroundRect)
3670     : nsPaintedDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) {
3671   MOZ_COUNT_CTOR(nsDisplayThemedBackground);
3672 }
3673 
Init(nsDisplayListBuilder * aBuilder)3674 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) {
3675   const nsStyleDisplay* disp = StyleFrame()->StyleDisplay();
3676   mAppearance = disp->EffectiveAppearance();
3677   StyleFrame()->IsThemed(disp, &mThemeTransparency);
3678 
3679   // Perform necessary RegisterThemeGeometry
3680   nsITheme* theme = StyleFrame()->PresContext()->Theme();
3681   nsITheme::ThemeGeometryType type =
3682       theme->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance);
3683   if (type != nsITheme::eThemeGeometryTypeUnknown) {
3684     RegisterThemeGeometry(aBuilder, this, StyleFrame(), type);
3685   }
3686 
3687   if (mAppearance == StyleAppearance::MozWinBorderlessGlass ||
3688       mAppearance == StyleAppearance::MozWinGlass) {
3689     aBuilder->SetGlassDisplayItem(this);
3690   }
3691 
3692   mBounds = GetBoundsInternal();
3693 }
3694 
WriteDebugInfo(std::stringstream & aStream)3695 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) {
3696   aStream << " (themed, appearance:" << (int)mAppearance << ")";
3697 }
3698 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)3699 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
3700                                         const nsRect& aRect,
3701                                         HitTestState* aState,
3702                                         nsTArray<nsIFrame*>* aOutFrames) {
3703   // Assume that any point in our background rect is a hit.
3704   if (mBackgroundRect.Intersects(aRect)) {
3705     aOutFrames->AppendElement(mFrame);
3706   }
3707 }
3708 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const3709 nsRegion nsDisplayThemedBackground::GetOpaqueRegion(
3710     nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3711   nsRegion result;
3712   *aSnap = false;
3713 
3714   if (mThemeTransparency == nsITheme::eOpaque) {
3715     *aSnap = true;
3716     result = mBackgroundRect;
3717   }
3718   return result;
3719 }
3720 
IsUniform(nsDisplayListBuilder * aBuilder) const3721 Maybe<nscolor> nsDisplayThemedBackground::IsUniform(
3722     nsDisplayListBuilder* aBuilder) const {
3723   if (mAppearance == StyleAppearance::MozWinBorderlessGlass ||
3724       mAppearance == StyleAppearance::MozWinGlass) {
3725     return Some(NS_RGBA(0, 0, 0, 0));
3726   }
3727   return Nothing();
3728 }
3729 
GetPositioningArea() const3730 nsRect nsDisplayThemedBackground::GetPositioningArea() const {
3731   return mBackgroundRect;
3732 }
3733 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)3734 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
3735                                       gfxContext* aCtx) {
3736   PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), nullptr);
3737 }
3738 
PaintInternal(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,const nsRect & aBounds,nsRect * aClipRect)3739 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
3740                                               gfxContext* aCtx,
3741                                               const nsRect& aBounds,
3742                                               nsRect* aClipRect) {
3743   // XXXzw this ignores aClipRect.
3744   nsPresContext* presContext = StyleFrame()->PresContext();
3745   nsITheme* theme = presContext->Theme();
3746   nsRect drawing(mBackgroundRect);
3747   theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(),
3748                            mAppearance, &drawing);
3749   drawing.IntersectRect(drawing, aBounds);
3750   theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect,
3751                               drawing);
3752 }
3753 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)3754 bool nsDisplayThemedBackground::CreateWebRenderCommands(
3755     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3756     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3757     nsDisplayListBuilder* aDisplayListBuilder) {
3758   nsITheme* theme = StyleFrame()->PresContext()->Theme();
3759   return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc,
3760                                                  aManager, StyleFrame(),
3761                                                  mAppearance, mBackgroundRect);
3762 }
3763 
IsWindowActive() const3764 bool nsDisplayThemedBackground::IsWindowActive() const {
3765   EventStates docState = mFrame->GetContent()->OwnerDoc()->GetDocumentState();
3766   return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
3767 }
3768 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const3769 void nsDisplayThemedBackground::ComputeInvalidationRegion(
3770     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
3771     nsRegion* aInvalidRegion) const {
3772   const auto* geometry =
3773       static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
3774 
3775   bool snap;
3776   nsRect bounds = GetBounds(aBuilder, &snap);
3777   nsRect positioningArea = GetPositioningArea();
3778   if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
3779     // Invalidate everything (both old and new painting areas).
3780     aInvalidRegion->Or(bounds, geometry->mBounds);
3781     return;
3782   }
3783   if (!bounds.IsEqualInterior(geometry->mBounds)) {
3784     // Positioning area is unchanged, so invalidate just the change in the
3785     // painting area.
3786     aInvalidRegion->Xor(bounds, geometry->mBounds);
3787   }
3788   nsITheme* theme = StyleFrame()->PresContext()->Theme();
3789   if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
3790       IsWindowActive() != geometry->mWindowIsActive) {
3791     aInvalidRegion->Or(*aInvalidRegion, bounds);
3792   }
3793 }
3794 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const3795 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder,
3796                                             bool* aSnap) const {
3797   *aSnap = true;
3798   return mBounds;
3799 }
3800 
GetBoundsInternal()3801 nsRect nsDisplayThemedBackground::GetBoundsInternal() {
3802   nsPresContext* presContext = mFrame->PresContext();
3803 
3804   nsRect r = mBackgroundRect - ToReferenceFrame();
3805   presContext->Theme()->GetWidgetOverflow(
3806       presContext->DeviceContext(), mFrame,
3807       mFrame->StyleDisplay()->EffectiveAppearance(), &r);
3808   return r + ToReferenceFrame();
3809 }
3810 
3811 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)3812 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder,
3813                                  gfxContext* aCtx) {
3814   mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(),
3815                                   mFrame, ToReferenceFrame(), mColor);
3816 }
3817 #endif
3818 
CanApplyOpacity(WebRenderLayerManager * aManager,nsDisplayListBuilder * aBuilder) const3819 bool nsDisplayBackgroundColor::CanApplyOpacity(
3820     WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const {
3821   // Don't apply opacity if the background color is animated since the color is
3822   // going to be changed on the compositor.
3823   return !EffectCompositor::HasAnimationsForCompositor(
3824       mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR);
3825 }
3826 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)3827 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
3828     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3829     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
3830     nsDisplayListBuilder* aDisplayListBuilder) {
3831   gfx::sRGBColor color = mColor;
3832   color.a *= aBuilder.GetInheritedOpacity();
3833 
3834   if (color == sRGBColor() &&
3835       !EffectCompositor::HasAnimationsForCompositor(
3836           mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3837     return true;
3838   }
3839 
3840   if (HasBackgroundClipText()) {
3841     return false;
3842   }
3843 
3844   uint64_t animationsId = 0;
3845   // We don't support background-color animations on table elements yet.
3846   if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
3847     animationsId =
3848         AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
3849   }
3850 
3851   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
3852       mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3853   wr::LayoutRect r = wr::ToLayoutRect(bounds);
3854 
3855   if (animationsId) {
3856     wr::WrAnimationProperty prop{
3857         wr::WrAnimationType::BackgroundColor,
3858         animationsId,
3859     };
3860     aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(),
3861                                    wr::ToColorF(ToDeviceColor(color)), &prop);
3862   } else {
3863     aBuilder.StartGroup(this);
3864     aBuilder.PushRect(r, r, !BackfaceIsHidden(), false,
3865                       wr::ToColorF(ToDeviceColor(color)));
3866     aBuilder.FinishGroup();
3867   }
3868 
3869   return true;
3870 }
3871 
PaintWithClip(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,const DisplayItemClip & aClip)3872 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder,
3873                                              gfxContext* aCtx,
3874                                              const DisplayItemClip& aClip) {
3875   MOZ_ASSERT(!HasBackgroundClipText());
3876 
3877   if (mColor == sRGBColor()) {
3878     return;
3879   }
3880 
3881   nsRect fillRect = mBackgroundRect;
3882   if (aClip.HasClip()) {
3883     fillRect.IntersectRect(fillRect, aClip.GetClipRect());
3884   }
3885 
3886   DrawTarget* dt = aCtx->GetDrawTarget();
3887   int32_t A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
3888   Rect bounds = ToRect(nsLayoutUtils::RectToGfxRect(fillRect, A2D));
3889   MaybeSnapToDevicePixels(bounds, *dt);
3890   ColorPattern fill(ToDeviceColor(mColor));
3891 
3892   if (aClip.GetRoundedRectCount()) {
3893     MOZ_ASSERT(aClip.GetRoundedRectCount() == 1);
3894 
3895     AutoTArray<DisplayItemClip::RoundedRect, 1> roundedRect;
3896     aClip.AppendRoundedRects(&roundedRect);
3897 
3898     bool pushedClip = false;
3899     if (!fillRect.Contains(roundedRect[0].mRect)) {
3900       dt->PushClipRect(bounds);
3901       pushedClip = true;
3902     }
3903 
3904     RectCornerRadii pixelRadii;
3905     nsCSSRendering::ComputePixelRadii(roundedRect[0].mRadii, A2D, &pixelRadii);
3906     dt->FillRoundedRect(
3907         RoundedRect(NSRectToSnappedRect(roundedRect[0].mRect, A2D, *dt),
3908                     pixelRadii),
3909         fill);
3910     if (pushedClip) {
3911       dt->PopClip();
3912     }
3913   } else {
3914     dt->FillRect(bounds, fill);
3915   }
3916 }
3917 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)3918 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
3919                                      gfxContext* aCtx) {
3920   if (mColor == sRGBColor()) {
3921     return;
3922   }
3923 
3924 #if 0
3925   // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
3926   // results in a precision induced rounding issue that makes the rect one
3927   // pixel shorter in rare cases. Disabled in favor of the old code for now.
3928   // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
3929   // reproduce the bug.
3930   //
3931   // TODO:
3932   // This new path does not include support for background-clip:text; need to
3933   // be fixed if/when we switch to this new code path.
3934 
3935   DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
3936 
3937   Rect rect = NSRectToSnappedRect(mBackgroundRect,
3938                                   mFrame->PresContext()->AppUnitsPerDevPixel(),
3939                                   aDrawTarget);
3940   ColorPattern color(ToDeviceColor(mColor));
3941   aDrawTarget.FillRect(rect, color);
3942 #else
3943   gfxContext* ctx = aCtx;
3944   gfxRect bounds = nsLayoutUtils::RectToGfxRect(
3945       mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3946 
3947   if (HasBackgroundClipText()) {
3948     if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
3949       return;
3950     }
3951 
3952     ctx->SetColor(mColor);
3953     ctx->NewPath();
3954     ctx->SnappedRectangle(bounds);
3955     ctx->Fill();
3956     ctx->PopGroupAndBlend();
3957     return;
3958   }
3959 
3960   ctx->SetColor(mColor);
3961   ctx->NewPath();
3962   ctx->SnappedRectangle(bounds);
3963   ctx->Fill();
3964 #endif
3965 }
3966 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const3967 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion(
3968     nsDisplayListBuilder* aBuilder, bool* aSnap) const {
3969   *aSnap = false;
3970 
3971   if (mColor.a != 1 ||
3972       // Even if the current alpha channel is 1, we treat this item as if it's
3973       // non-opaque if there is a background-color animation since the animation
3974       // might change the alpha channel.
3975       EffectCompositor::HasAnimationsForCompositor(
3976           mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
3977     return nsRegion();
3978   }
3979 
3980   if (!mHasStyle || HasBackgroundClipText()) {
3981     return nsRegion();
3982   }
3983 
3984   *aSnap = true;
3985   return GetInsideClipRect(this, mBottomLayerClip, mBackgroundRect,
3986                            mBackgroundRect);
3987 }
3988 
IsUniform(nsDisplayListBuilder * aBuilder) const3989 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform(
3990     nsDisplayListBuilder* aBuilder) const {
3991   return Some(mColor.ToABGR());
3992 }
3993 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)3994 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
3995                                        const nsRect& aRect,
3996                                        HitTestState* aState,
3997                                        nsTArray<nsIFrame*>* aOutFrames) {
3998   if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3999     // aRect doesn't intersect our border-radius curve.
4000     return;
4001   }
4002 
4003   aOutFrames->AppendElement(mFrame);
4004 }
4005 
WriteDebugInfo(std::stringstream & aStream)4006 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) {
4007   aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << ","
4008           << mColor.a << ")";
4009   aStream << " backgroundRect" << mBackgroundRect;
4010 }
4011 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const4012 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
4013                                    bool* aSnap) const {
4014   *aSnap = false;
4015   return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
4016 }
4017 
GetInnerRect() const4018 nsRect nsDisplayOutline::GetInnerRect() const {
4019   nsRect* savedOutlineInnerRect =
4020       mFrame->GetProperty(nsIFrame::OutlineInnerRectProperty());
4021   if (savedOutlineInnerRect) {
4022     return *savedOutlineInnerRect;
4023   }
4024 
4025   // FIXME bug 1221888
4026   NS_ERROR("we should have saved a frame property");
4027   return nsRect(nsPoint(), mFrame->GetSize());
4028 }
4029 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4030 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4031   // TODO join outlines together
4032   MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(),
4033              "Should have not created a nsDisplayOutline!");
4034 
4035   nsRect rect = GetInnerRect() + ToReferenceFrame();
4036   nsPresContext* pc = mFrame->PresContext();
4037   if (IsThemedOutline()) {
4038     rect.Inflate(mFrame->StyleOutline()->mOutlineOffset.ToAppUnits());
4039     pc->Theme()->DrawWidgetBackground(aCtx, mFrame,
4040                                       StyleAppearance::FocusOutline, rect,
4041                                       GetPaintRect(aBuilder, aCtx));
4042     return;
4043   }
4044 
4045   nsCSSRendering::PaintNonThemedOutline(
4046       pc, *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), rect, mFrame->Style());
4047 }
4048 
IsThemedOutline() const4049 bool nsDisplayOutline::IsThemedOutline() const {
4050   const auto& outlineStyle = mFrame->StyleOutline()->mOutlineStyle;
4051   if (!outlineStyle.IsAuto() ||
4052       !StaticPrefs::layout_css_outline_style_auto_enabled()) {
4053     return false;
4054   }
4055 
4056   nsPresContext* pc = mFrame->PresContext();
4057   nsITheme* theme = pc->Theme();
4058   return theme->ThemeSupportsWidget(pc, mFrame, StyleAppearance::FocusOutline);
4059 }
4060 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4061 bool nsDisplayOutline::CreateWebRenderCommands(
4062     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4063     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4064     nsDisplayListBuilder* aDisplayListBuilder) {
4065   nsPresContext* pc = mFrame->PresContext();
4066   nsRect rect = GetInnerRect() + ToReferenceFrame();
4067   if (IsThemedOutline()) {
4068     rect.Inflate(mFrame->StyleOutline()->mOutlineOffset.ToAppUnits());
4069     return pc->Theme()->CreateWebRenderCommandsForWidget(
4070         aBuilder, aResources, aSc, aManager, mFrame,
4071         StyleAppearance::FocusOutline, rect);
4072   }
4073 
4074   bool dummy;
4075   Maybe<nsCSSBorderRenderer> borderRenderer =
4076       nsCSSRendering::CreateBorderRendererForNonThemedOutline(
4077           pc, /* aDrawTarget = */ nullptr, mFrame,
4078           GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style());
4079 
4080   if (!borderRenderer) {
4081     // No border renderer means "there is no outline".
4082     // Paint nothing and return success.
4083     return true;
4084   }
4085 
4086   borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
4087   return true;
4088 }
4089 
HasRadius() const4090 bool nsDisplayOutline::HasRadius() const {
4091   const auto& radius = mFrame->StyleBorder()->mBorderRadius;
4092   return !nsLayoutUtils::HasNonZeroCorner(radius);
4093 }
4094 
IsInvisibleInRect(const nsRect & aRect) const4095 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const {
4096   const nsStyleOutline* outline = mFrame->StyleOutline();
4097   nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
4098   if (borderBox.Contains(aRect) && !HasRadius() &&
4099       outline->mOutlineOffset.ToCSSPixels() >= 0.0f) {
4100     // aRect is entirely inside the border-rect, and the outline isn't rendered
4101     // inside the border-rect, so the outline is not visible.
4102     return true;
4103   }
4104   return false;
4105 }
4106 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)4107 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
4108                                      const nsRect& aRect, HitTestState* aState,
4109                                      nsTArray<nsIFrame*>* aOutFrames) {
4110   if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4111     // aRect doesn't intersect our border-radius curve.
4112     return;
4113   }
4114 
4115   aOutFrames->AppendElement(mFrame);
4116 }
4117 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4118 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4119     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4120     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4121     nsDisplayListBuilder* aDisplayListBuilder) {
4122   return true;
4123 }
4124 
ZIndex() const4125 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4126   return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
4127 }
4128 
SetOverrideZIndex(int32_t aZIndex)4129 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) {
4130   mOverrideZIndex = Some(aZIndex);
4131 }
4132 
nsDisplayCaret(nsDisplayListBuilder * aBuilder,nsIFrame * aCaretFrame)4133 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
4134                                nsIFrame* aCaretFrame)
4135     : nsPaintedDisplayItem(aBuilder, aCaretFrame),
4136       mCaret(aBuilder->GetCaret()),
4137       mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) {
4138   MOZ_COUNT_CTOR(nsDisplayCaret);
4139   // The presence of a caret doesn't change the overflow rect
4140   // of the owning frame, so the normal building rect might not
4141   // include the caret at all. We use MarkFrameForDisplay to ensure
4142   // we build this item, and here we override the building rect
4143   // to cover the pixels we're going to draw.
4144   SetBuildingRect(mBounds);
4145 }
4146 
4147 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayCaret()4148 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret); }
4149 #endif
4150 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const4151 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder,
4152                                  bool* aSnap) const {
4153   *aSnap = true;
4154   // The caret returns a rect in the coordinates of mFrame.
4155   return mBounds;
4156 }
4157 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4158 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4159   // Note: Because we exist, we know that the caret is visible, so we don't
4160   // need to check for the caret's visibility.
4161   mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
4162 }
4163 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4164 bool nsDisplayCaret::CreateWebRenderCommands(
4165     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4166     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4167     nsDisplayListBuilder* aDisplayListBuilder) {
4168   using namespace layers;
4169   nsRect caretRect;
4170   nsRect hookRect;
4171   nscolor caretColor;
4172   nsIFrame* frame =
4173       mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor);
4174   MOZ_ASSERT(frame == mFrame, "We're referring different frame");
4175   if (!frame) {
4176     return true;
4177   }
4178 
4179   int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
4180   gfx::DeviceColor color = ToDeviceColor(caretColor);
4181   LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
4182       caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
4183   LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
4184       hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
4185 
4186   wr::LayoutRect caret = wr::ToLayoutRect(devCaretRect);
4187   wr::LayoutRect hook = wr::ToLayoutRect(devHookRect);
4188 
4189   // Note, WR will pixel snap anything that is layout aligned.
4190   aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), false,
4191                     wr::ToColorF(color));
4192 
4193   if (!devHookRect.IsEmpty()) {
4194     aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), false,
4195                       wr::ToColorF(color));
4196   }
4197   return true;
4198 }
4199 
nsDisplayBorder(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)4200 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder,
4201                                  nsIFrame* aFrame)
4202     : nsPaintedDisplayItem(aBuilder, aFrame) {
4203   MOZ_COUNT_CTOR(nsDisplayBorder);
4204 
4205   mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
4206 }
4207 
IsInvisibleInRect(const nsRect & aRect) const4208 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const {
4209   nsRect paddingRect = GetPaddingRect();
4210   const nsStyleBorder* styleBorder;
4211   if (paddingRect.Contains(aRect) &&
4212       !(styleBorder = mFrame->StyleBorder())->IsBorderImageSizeAvailable() &&
4213       !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
4214     // aRect is entirely inside the content rect, and no part
4215     // of the border is rendered inside the content rect, so we are not
4216     // visible
4217     // Skip this if there's a border-image (which draws a background
4218     // too) or if there is a border-radius (which makes the border draw
4219     // further in).
4220     return true;
4221   }
4222 
4223   return false;
4224 }
4225 
AllocateGeometry(nsDisplayListBuilder * aBuilder)4226 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry(
4227     nsDisplayListBuilder* aBuilder) {
4228   return new nsDisplayBorderGeometry(this, aBuilder);
4229 }
4230 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const4231 void nsDisplayBorder::ComputeInvalidationRegion(
4232     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4233     nsRegion* aInvalidRegion) const {
4234   const auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
4235   bool snap;
4236 
4237   if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
4238     // We can probably get away with only invalidating the difference
4239     // between the border and padding rects, but the XUL ui at least
4240     // is apparently painting a background with this?
4241     aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4242   }
4243 
4244   if (aBuilder->ShouldSyncDecodeImages() &&
4245       geometry->ShouldInvalidateToSyncDecodeImages()) {
4246     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
4247   }
4248 }
4249 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4250 bool nsDisplayBorder::CreateWebRenderCommands(
4251     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4252     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4253     nsDisplayListBuilder* aDisplayListBuilder) {
4254   nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
4255 
4256   ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder(
4257       this, mFrame, rect, aBuilder, aResources, aSc, aManager,
4258       aDisplayListBuilder);
4259 
4260   if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
4261     return false;
4262   }
4263 
4264   nsDisplayBorderGeometry::UpdateDrawResult(this, drawResult);
4265   return true;
4266 };
4267 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4268 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4269   nsPoint offset = ToReferenceFrame();
4270 
4271   PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
4272                                ? PaintBorderFlags::SyncDecodeImages
4273                                : PaintBorderFlags();
4274 
4275   ImgDrawResult result = nsCSSRendering::PaintBorder(
4276       mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(aBuilder, aCtx),
4277       nsRect(offset, mFrame->GetSize()), mFrame->Style(), flags,
4278       mFrame->GetSkipSides());
4279 
4280   nsDisplayBorderGeometry::UpdateDrawResult(this, result);
4281 }
4282 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const4283 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder,
4284                                   bool* aSnap) const {
4285   *aSnap = true;
4286   return mBounds;
4287 }
4288 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4289 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
4290                                     gfxContext* aCtx) {
4291   nsPoint offset = ToReferenceFrame();
4292   nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4293   nsPresContext* presContext = mFrame->PresContext();
4294 
4295   AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
4296 
4297   nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
4298                                       GetPaintRect(aBuilder, aCtx), 1.0f);
4299 }
4300 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const4301 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
4302                                           bool* aSnap) const {
4303   *aSnap = false;
4304   return mBounds;
4305 }
4306 
GetBoundsInternal()4307 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() {
4308   return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
4309          ToReferenceFrame();
4310 }
4311 
IsInvisibleInRect(const nsRect & aRect) const4312 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const {
4313   nsPoint origin = ToReferenceFrame();
4314   nsRect frameRect(origin, mFrame->GetSize());
4315   if (!frameRect.Contains(aRect)) {
4316     return false;
4317   }
4318 
4319   // the visible region is entirely inside the border-rect, and box shadows
4320   // never render within the border-rect (unless there's a border radius).
4321   nscoord twipsRadii[8];
4322   bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
4323   if (!hasBorderRadii) {
4324     return true;
4325   }
4326 
4327   return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
4328 }
4329 
CanBuildWebRenderDisplayItems() const4330 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const {
4331   auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4332   if (shadows.IsEmpty()) {
4333     return false;
4334   }
4335 
4336   bool hasBorderRadius;
4337   // We don't support native themed things yet like box shadows around
4338   // input buttons.
4339   //
4340   // TODO(emilio): The non-native theme could provide the right rect+radius
4341   // instead relatively painlessly, if we find this causes performance issues or
4342   // what not.
4343   return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4344 }
4345 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4346 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
4347     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4348     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4349     nsDisplayListBuilder* aDisplayListBuilder) {
4350   if (!CanBuildWebRenderDisplayItems()) {
4351     return false;
4352   }
4353 
4354   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4355   nsPoint offset = ToReferenceFrame();
4356   nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
4357   bool snap;
4358   nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
4359 
4360   bool hasBorderRadius;
4361   bool nativeTheme =
4362       nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
4363 
4364   // Don't need the full size of the shadow rect like we do in
4365   // nsCSSRendering since WR takes care of calculations for blur
4366   // and spread radius.
4367   nsRect frameRect =
4368       nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
4369 
4370   RectCornerRadii borderRadii;
4371   if (hasBorderRadius) {
4372     hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
4373                                                      mFrame, borderRadii);
4374   }
4375 
4376   // Everything here is in app units, change to device units.
4377   LayoutDeviceRect clipRect =
4378       LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel);
4379   auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan();
4380   MOZ_ASSERT(!shadows.IsEmpty());
4381 
4382   for (const auto& shadow : Reversed(shadows)) {
4383     if (shadow.inset) {
4384       continue;
4385     }
4386 
4387     float blurRadius =
4388         float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4389     gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor(
4390         shadow.base, mFrame, aBuilder.GetInheritedOpacity());
4391 
4392     // We don't move the shadow rect here since WR does it for us
4393     // Now translate everything to device pixels.
4394     const nsRect& shadowRect = frameRect;
4395     LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4396         nsPoint(shadow.base.horizontal.ToAppUnits(),
4397                 shadow.base.vertical.ToAppUnits()),
4398         appUnitsPerDevPixel);
4399 
4400     LayoutDeviceRect deviceBox =
4401         LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4402     wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
4403     wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4404 
4405     LayoutDeviceSize zeroSize;
4406     wr::BorderRadius borderRadius =
4407         wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
4408     if (hasBorderRadius) {
4409       borderRadius = wr::ToBorderRadius(
4410           LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
4411           LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
4412           LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
4413           LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
4414     }
4415 
4416     float spreadRadius =
4417         float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4418 
4419     aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
4420                            deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
4421                            wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius,
4422                            spreadRadius, borderRadius,
4423                            wr::BoxShadowClipMode::Outset);
4424   }
4425 
4426   return true;
4427 }
4428 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const4429 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
4430     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4431     nsRegion* aInvalidRegion) const {
4432   const auto* geometry =
4433       static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
4434   bool snap;
4435   if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
4436       !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
4437     nsRegion oldShadow, newShadow;
4438     nscoord dontCare[8];
4439     bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
4440     if (hasBorderRadius) {
4441       // If we have rounded corners then we need to invalidate the frame area
4442       // too since we paint into it.
4443       oldShadow = geometry->mBounds;
4444       newShadow = GetBounds(aBuilder, &snap);
4445     } else {
4446       oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
4447       newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
4448     }
4449     aInvalidRegion->Or(oldShadow, newShadow);
4450   }
4451 }
4452 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4453 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
4454                                     gfxContext* aCtx) {
4455   nsPoint offset = ToReferenceFrame();
4456   nsRect borderRect = nsRect(offset, mFrame->GetSize());
4457   nsPresContext* presContext = mFrame->PresContext();
4458 
4459   AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS);
4460 
4461   nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
4462 }
4463 
CanCreateWebRenderCommands(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsPoint & aReferenceOffset)4464 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
4465     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4466     const nsPoint& aReferenceOffset) {
4467   auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4468   if (shadows.IsEmpty()) {
4469     // Means we don't have to paint anything
4470     return true;
4471   }
4472 
4473   bool hasBorderRadius;
4474   bool nativeTheme =
4475       nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
4476 
4477   // We don't support native themed things yet like box shadows around
4478   // input buttons.
4479   return !nativeTheme;
4480 }
4481 
4482 /* static */
CreateInsetBoxShadowWebRenderCommands(wr::DisplayListBuilder & aBuilder,const StackingContextHelper & aSc,nsRect & aVisibleRect,nsIFrame * aFrame,const nsRect & aBorderRect)4483 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4484     wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
4485     nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect) {
4486   if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) {
4487     return;
4488   }
4489 
4490   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
4491 
4492   auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
4493 
4494   LayoutDeviceRect clipRect =
4495       LayoutDeviceRect::FromAppUnits(aVisibleRect, appUnitsPerDevPixel);
4496 
4497   for (const auto& shadow : Reversed(shadows)) {
4498     if (!shadow.inset) {
4499       continue;
4500     }
4501 
4502     nsRect shadowRect =
4503         nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
4504     RectCornerRadii innerRadii;
4505     nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
4506 
4507     // Now translate everything to device pixels.
4508     LayoutDeviceRect deviceBoxRect =
4509         LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
4510     wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
4511     sRGBColor shadowColor =
4512         nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0);
4513 
4514     LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
4515         nsPoint(shadow.base.horizontal.ToAppUnits(),
4516                 shadow.base.vertical.ToAppUnits()),
4517         appUnitsPerDevPixel);
4518 
4519     float blurRadius =
4520         float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
4521 
4522     wr::BorderRadius borderRadius = wr::ToBorderRadius(
4523         LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
4524         LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
4525         LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
4526         LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
4527     // NOTE: Any spread radius > 0 will render nothing. WR Bug.
4528     float spreadRadius =
4529         float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
4530 
4531     aBuilder.PushBoxShadow(
4532         wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
4533         !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
4534         wr::ToLayoutVector2D(shadowOffset),
4535         wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, spreadRadius,
4536         borderRadius, wr::BoxShadowClipMode::Inset);
4537   }
4538 }
4539 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4540 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
4541     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4542     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4543     nsDisplayListBuilder* aDisplayListBuilder) {
4544   if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame,
4545                                   ToReferenceFrame())) {
4546     return false;
4547   }
4548 
4549   bool snap;
4550   nsRect visible = GetBounds(aDisplayListBuilder, &snap);
4551   nsPoint offset = ToReferenceFrame();
4552   nsRect borderRect = nsRect(offset, mFrame->GetSize());
4553   nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
4554       aBuilder, aSc, visible, mFrame, borderRect);
4555 
4556   return true;
4557 }
4558 
nsDisplayWrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList)4559 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4560                                      nsIFrame* aFrame, nsDisplayList* aList)
4561     : nsDisplayWrapList(aBuilder, aFrame, aList,
4562                         aBuilder->CurrentActiveScrolledRoot(), false) {}
4563 
nsDisplayWrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,bool aClearClipChain)4564 nsDisplayWrapList::nsDisplayWrapList(
4565     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4566     const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
4567     : nsPaintedDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
4568       mList(aBuilder),
4569       mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
4570       mOverrideZIndex(0),
4571       mHasZIndexOverride(false),
4572       mClearingClipChain(aClearClipChain) {
4573   MOZ_COUNT_CTOR(nsDisplayWrapList);
4574 
4575   mBaseBuildingRect = GetBuildingRect();
4576 
4577   mListPtr = &mList;
4578   mListPtr->AppendToTop(aList);
4579   mOriginalClipChain = mClipChain;
4580   nsDisplayWrapList::UpdateBounds(aBuilder);
4581 }
4582 
nsDisplayWrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayItem * aItem)4583 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
4584                                      nsIFrame* aFrame, nsDisplayItem* aItem)
4585     : nsPaintedDisplayItem(aBuilder, aFrame,
4586                            aBuilder->CurrentActiveScrolledRoot()),
4587       mList(aBuilder),
4588       mOverrideZIndex(0),
4589       mHasZIndexOverride(false) {
4590   MOZ_COUNT_CTOR(nsDisplayWrapList);
4591 
4592   mBaseBuildingRect = GetBuildingRect();
4593 
4594   mListPtr = &mList;
4595   mListPtr->AppendToTop(aItem);
4596   mOriginalClipChain = mClipChain;
4597   nsDisplayWrapList::UpdateBounds(aBuilder);
4598 
4599   if (!aFrame || !aFrame->IsTransformed()) {
4600     return;
4601   }
4602 
4603   // See the previous nsDisplayWrapList constructor
4604   if (aItem->Frame() == aFrame) {
4605     mToReferenceFrame = aItem->ToReferenceFrame();
4606   }
4607 
4608   nsRect visible = aBuilder->GetVisibleRect() +
4609                    aBuilder->GetCurrentFrameOffsetToReferenceFrame();
4610 
4611   SetBuildingRect(visible);
4612 }
4613 
~nsDisplayWrapList()4614 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList); }
4615 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)4616 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder,
4617                                 const nsRect& aRect, HitTestState* aState,
4618                                 nsTArray<nsIFrame*>* aOutFrames) {
4619   mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames);
4620 }
4621 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const4622 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder,
4623                                     bool* aSnap) const {
4624   *aSnap = false;
4625   return mBounds;
4626 }
4627 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const4628 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4629                                             bool* aSnap) const {
4630   *aSnap = false;
4631   bool snap;
4632   return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(),
4633                                     GetBounds(aBuilder, &snap));
4634 }
4635 
IsUniform(nsDisplayListBuilder * aBuilder) const4636 Maybe<nscolor> nsDisplayWrapList::IsUniform(
4637     nsDisplayListBuilder* aBuilder) const {
4638   // We could try to do something but let's conservatively just return Nothing.
4639   return Nothing();
4640 }
4641 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4642 void nsDisplayWrapper::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4643   NS_ERROR("nsDisplayWrapper should have been flattened away for painting");
4644 }
4645 
GetComponentAlphaBounds(nsDisplayListBuilder * aBuilder) const4646 nsRect nsDisplayWrapList::GetComponentAlphaBounds(
4647     nsDisplayListBuilder* aBuilder) const {
4648   return mListPtr->GetComponentAlphaBounds(aBuilder);
4649 }
4650 
CreateWebRenderCommandsNewClipListOption(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder,bool aNewClipList)4651 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption(
4652     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
4653     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
4654     nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList) {
4655   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
4656       GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources,
4657       aNewClipList);
4658   return true;
4659 }
4660 
WrapDisplayList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,nsDisplayItemWrapper * aWrapper)4661 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder,
4662                                 nsIFrame* aFrame, nsDisplayList* aList,
4663                                 nsDisplayItemWrapper* aWrapper) {
4664   if (!aList->GetTop()) {
4665     return NS_OK;
4666   }
4667   nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
4668   if (!item) {
4669     return NS_ERROR_OUT_OF_MEMORY;
4670   }
4671   // aList was emptied
4672   aList->AppendToTop(item);
4673   return NS_OK;
4674 }
4675 
WrapEachDisplayItem(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,nsDisplayItemWrapper * aWrapper)4676 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
4677                                     nsDisplayList* aList,
4678                                     nsDisplayItemWrapper* aWrapper) {
4679   for (nsDisplayItem* item : aList->TakeItems()) {
4680     item = aWrapper->WrapItem(aBuilder, item);
4681     if (!item) {
4682       return NS_ERROR_OUT_OF_MEMORY;
4683     }
4684     aList->AppendToTop(item);
4685   }
4686   // aList was emptied
4687   return NS_OK;
4688 }
4689 
WrapLists(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsDisplayListSet & aIn,const nsDisplayListSet & aOut)4690 nsresult nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
4691                                          nsIFrame* aFrame,
4692                                          const nsDisplayListSet& aIn,
4693                                          const nsDisplayListSet& aOut) {
4694   nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
4695   NS_ENSURE_SUCCESS(rv, rv);
4696 
4697   if (&aOut == &aIn) {
4698     return NS_OK;
4699   }
4700   aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
4701   aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
4702   aOut.Floats()->AppendToTop(aIn.Floats());
4703   aOut.Content()->AppendToTop(aIn.Content());
4704   aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
4705   aOut.Outlines()->AppendToTop(aIn.Outlines());
4706   return NS_OK;
4707 }
4708 
WrapListsInPlace(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsDisplayListSet & aLists)4709 nsresult nsDisplayItemWrapper::WrapListsInPlace(
4710     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4711     const nsDisplayListSet& aLists) {
4712   nsresult rv;
4713   if (WrapBorderBackground()) {
4714     // Our border-backgrounds are in-flow
4715     rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
4716     NS_ENSURE_SUCCESS(rv, rv);
4717   }
4718   // Our block border-backgrounds are in-flow
4719   rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
4720   NS_ENSURE_SUCCESS(rv, rv);
4721   // The floats are not in flow
4722   rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
4723   NS_ENSURE_SUCCESS(rv, rv);
4724   // Our child content is in flow
4725   rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
4726   NS_ENSURE_SUCCESS(rv, rv);
4727   // The positioned descendants may not be in-flow
4728   rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
4729   NS_ENSURE_SUCCESS(rv, rv);
4730   // The outlines may not be in-flow
4731   return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
4732 }
4733 
nsDisplayOpacity(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,bool aForEventsOnly,bool aNeedsActiveLayer)4734 nsDisplayOpacity::nsDisplayOpacity(
4735     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
4736     const ActiveScrolledRoot* aActiveScrolledRoot, bool aForEventsOnly,
4737     bool aNeedsActiveLayer)
4738     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
4739       mOpacity(aFrame->StyleEffects()->mOpacity),
4740       mForEventsOnly(aForEventsOnly),
4741       mNeedsActiveLayer(aNeedsActiveLayer),
4742       mChildOpacityState(ChildOpacityState::Unknown) {
4743   MOZ_COUNT_CTOR(nsDisplayOpacity);
4744 }
4745 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,nsDisplayItem::HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)4746 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder,
4747                                const nsRect& aRect,
4748                                nsDisplayItem::HitTestState* aState,
4749                                nsTArray<nsIFrame*>* aOutFrames) {
4750   AutoRestore<float> opacity(aState->mCurrentOpacity);
4751   aState->mCurrentOpacity *= mOpacity;
4752 
4753   // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should
4754   // only consider fully opaque items? Or make this configurable somehow?
4755   if (aBuilder->HitTestIsForVisibility() && mOpacity == 0.0f) {
4756     return;
4757   }
4758   nsDisplayWrapList::HitTest(aBuilder, aRect, aState, aOutFrames);
4759 }
4760 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const4761 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
4762                                            bool* aSnap) const {
4763   *aSnap = false;
4764   // The only time where mOpacity == 1.0 should be when we have will-change.
4765   // We could report this as opaque then but when the will-change value starts
4766   // animating the element would become non opaque and could cause repaints.
4767   return nsRegion();
4768 }
4769 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4770 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4771   if (GetOpacity() == 0.0f) {
4772     return;
4773   }
4774 
4775   if (GetOpacity() == 1.0f) {
4776     GetChildren()->Paint(aBuilder, aCtx,
4777                          mFrame->PresContext()->AppUnitsPerDevPixel());
4778     return;
4779   }
4780 
4781   // TODO: Compute a bounds rect to pass to PushLayer for a smaller
4782   // allocation.
4783   aCtx->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix());
4784   GetChildren()->Paint(aBuilder, aCtx,
4785                        mFrame->PresContext()->AppUnitsPerDevPixel());
4786   aCtx->GetDrawTarget()->PopLayer();
4787 }
4788 
4789 /* static */
NeedsActiveLayer(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)4790 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
4791                                         nsIFrame* aFrame) {
4792   return EffectCompositor::HasAnimationsForCompositor(
4793              aFrame, DisplayItemType::TYPE_OPACITY) ||
4794          (ActiveLayerTracker::IsStyleAnimated(
4795              aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()));
4796 }
4797 
CanApplyOpacity(WebRenderLayerManager * aManager,nsDisplayListBuilder * aBuilder) const4798 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager,
4799                                        nsDisplayListBuilder* aBuilder) const {
4800   return !EffectCompositor::HasAnimationsForCompositor(
4801       mFrame, DisplayItemType::TYPE_OPACITY);
4802 }
4803 
4804 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount|
4805 // children that don't overlap and can all apply the opacity to themselves.
4806 static const size_t kOpacityMaxChildCount = 3;
4807 
4808 // |kOpacityMaxListSize| defines an early exit condition for opacity items that
4809 // are likely have more child items than |kOpacityMaxChildCount|.
4810 static const size_t kOpacityMaxListSize = kOpacityMaxChildCount * 2;
4811 
4812 /**
4813  * Recursively iterates through |aList| and collects at most
4814  * |kOpacityMaxChildCount| display item pointers to items that return true for
4815  * CanApplyOpacity(). The item pointers are added to |aArray|.
4816  *
4817  * LayerEventRegions and WrapList items are ignored.
4818  *
4819  * We need to do this recursively, because the child display items might contain
4820  * nested nsDisplayWrapLists.
4821  *
4822  * Returns false if there are more than |kOpacityMaxChildCount| items, or if an
4823  * item that returns false for CanApplyOpacity() is encountered.
4824  * Otherwise returns true.
4825  */
CollectItemsWithOpacity(WebRenderLayerManager * aManager,nsDisplayListBuilder * aBuilder,nsDisplayList * aList,nsTArray<nsPaintedDisplayItem * > & aArray)4826 static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager,
4827                                     nsDisplayListBuilder* aBuilder,
4828                                     nsDisplayList* aList,
4829                                     nsTArray<nsPaintedDisplayItem*>& aArray) {
4830   if (aList->Length() > kOpacityMaxListSize) {
4831     // Exit early, since |aList| will likely contain more than
4832     // |kOpacityMaxChildCount| items.
4833     return false;
4834   }
4835 
4836   for (nsDisplayItem* i : *aList) {
4837     const DisplayItemType type = i->GetType();
4838 
4839     if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
4840       continue;
4841     }
4842 
4843     // Descend only into wraplists.
4844     if (type == DisplayItemType::TYPE_WRAP_LIST ||
4845         type == DisplayItemType::TYPE_CONTAINER) {
4846       // The current display item has children, process them first.
4847       if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(),
4848                                    aArray)) {
4849         return false;
4850       }
4851 
4852       continue;
4853     }
4854 
4855     if (aArray.Length() == kOpacityMaxChildCount) {
4856       return false;
4857     }
4858 
4859     auto* item = i->AsPaintedDisplayItem();
4860     if (!item || !item->CanApplyOpacity(aManager, aBuilder)) {
4861       return false;
4862     }
4863 
4864     aArray.AppendElement(item);
4865   }
4866 
4867   return true;
4868 }
4869 
CanApplyToChildren(WebRenderLayerManager * aManager,nsDisplayListBuilder * aBuilder)4870 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager,
4871                                           nsDisplayListBuilder* aBuilder) {
4872   if (mChildOpacityState == ChildOpacityState::Deferred) {
4873     return false;
4874   }
4875 
4876   // Iterate through the child display list and copy at most
4877   // |kOpacityMaxChildCount| child display item pointers to a temporary list.
4878   AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items;
4879   if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) {
4880     mChildOpacityState = ChildOpacityState::Deferred;
4881     return false;
4882   }
4883 
4884   struct {
4885     nsPaintedDisplayItem* item{};
4886     nsRect bounds;
4887   } children[kOpacityMaxChildCount];
4888 
4889   bool snap;
4890   size_t childCount = 0;
4891   for (nsPaintedDisplayItem* item : items) {
4892     children[childCount].item = item;
4893     children[childCount].bounds = item->GetBounds(aBuilder, &snap);
4894     childCount++;
4895   }
4896 
4897   for (size_t i = 0; i < childCount; i++) {
4898     for (size_t j = i + 1; j < childCount; j++) {
4899       if (children[i].bounds.Intersects(children[j].bounds)) {
4900         mChildOpacityState = ChildOpacityState::Deferred;
4901         return false;
4902       }
4903     }
4904   }
4905 
4906   mChildOpacityState = ChildOpacityState::Applied;
4907   return true;
4908 }
4909 
4910 /**
4911  * Returns true if this nsDisplayOpacity contains only a filter or a mask item
4912  * that has the same frame as the opacity item, and that supports painting with
4913  * opacity. In this case the opacity item can be optimized away.
4914  */
ApplyToMask()4915 bool nsDisplayOpacity::ApplyToMask() {
4916   if (mList.Length() != 1) {
4917     return false;
4918   }
4919 
4920   nsDisplayItem* item = mList.GetBottom();
4921   if (item->Frame() != mFrame) {
4922     // The effect item needs to have the same frame as the opacity item.
4923     return false;
4924   }
4925 
4926   const DisplayItemType type = item->GetType();
4927   if (type == DisplayItemType::TYPE_MASK) {
4928     return true;
4929   }
4930 
4931   return false;
4932 }
4933 
CanApplyOpacityToChildren(WebRenderLayerManager * aManager,nsDisplayListBuilder * aBuilder,float aInheritedOpacity)4934 bool nsDisplayOpacity::CanApplyOpacityToChildren(
4935     WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder,
4936     float aInheritedOpacity) {
4937   if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
4938       mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
4939     // If we've been split, then we might need to merge, so
4940     // don't flatten us away.
4941     return false;
4942   }
4943 
4944   if (mNeedsActiveLayer || mOpacity == 0.0) {
4945     // If our opacity is zero then we'll discard all descendant display items
4946     // except for layer event regions, so there's no point in doing this
4947     // optimization (and if we do do it, then invalidations of those descendants
4948     // might trigger repainting).
4949     return false;
4950   }
4951 
4952   if (mList.IsEmpty()) {
4953     return false;
4954   }
4955 
4956   // We can only flatten opacity items into a mask if we haven't
4957   // already flattened an earlier ancestor, since the SVG code pulls the opacity
4958   // from style directly, and won't know about the outer opacity value.
4959   if (aInheritedOpacity == 1.0f && ApplyToMask()) {
4960     MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame));
4961     mChildOpacityState = ChildOpacityState::Applied;
4962     return true;
4963   }
4964 
4965   // Return true if we successfully applied opacity to child items.
4966   return CanApplyToChildren(aManager, aBuilder);
4967 }
4968 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const4969 void nsDisplayOpacity::ComputeInvalidationRegion(
4970     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4971     nsRegion* aInvalidRegion) const {
4972   const auto* geometry =
4973       static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
4974 
4975   bool snap;
4976   if (mOpacity != geometry->mOpacity) {
4977     aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
4978   }
4979 }
4980 
WriteDebugInfo(std::stringstream & aStream)4981 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) {
4982   aStream << " (opacity " << mOpacity << ", mChildOpacityState: ";
4983   switch (mChildOpacityState) {
4984     case ChildOpacityState::Unknown:
4985       aStream << "Unknown";
4986       break;
4987     case ChildOpacityState::Applied:
4988       aStream << "Applied";
4989       break;
4990     case ChildOpacityState::Deferred:
4991       aStream << "Deferred";
4992       break;
4993     default:
4994       break;
4995   }
4996 
4997   aStream << ")";
4998 }
4999 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5000 bool nsDisplayOpacity::CreateWebRenderCommands(
5001     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5002     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5003     nsDisplayListBuilder* aDisplayListBuilder) {
5004   MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied);
5005   float oldOpacity = aBuilder.GetInheritedOpacity();
5006   const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
5007   aBuilder.SetInheritedOpacity(1.0f);
5008   aBuilder.SetInheritedClipChain(nullptr);
5009   float opacity = mOpacity * oldOpacity;
5010   float* opacityForSC = &opacity;
5011 
5012   uint64_t animationsId =
5013       AddAnimationsForWebRender(this, aManager, aDisplayListBuilder);
5014   wr::WrAnimationProperty prop{
5015       wr::WrAnimationType::Opacity,
5016       animationsId,
5017   };
5018 
5019   wr::StackingContextParams params;
5020   params.animation = animationsId ? &prop : nullptr;
5021   params.opacity = opacityForSC;
5022   params.clip =
5023       wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5024   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5025                            params);
5026 
5027   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
5028       &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
5029   aBuilder.SetInheritedOpacity(oldOpacity);
5030   aBuilder.SetInheritedClipChain(oldClipChain);
5031   return true;
5032 }
5033 
nsDisplayBlendMode(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,StyleBlend aBlendMode,const ActiveScrolledRoot * aActiveScrolledRoot,const bool aIsForBackground)5034 nsDisplayBlendMode::nsDisplayBlendMode(
5035     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5036     StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
5037     const bool aIsForBackground)
5038     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5039       mBlendMode(aBlendMode),
5040       mIsForBackground(aIsForBackground) {
5041   MOZ_COUNT_CTOR(nsDisplayBlendMode);
5042 }
5043 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const5044 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5045                                              bool* aSnap) const {
5046   *aSnap = false;
5047   // We are never considered opaque
5048   return nsRegion();
5049 }
5050 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5051 bool nsDisplayBlendMode::CreateWebRenderCommands(
5052     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5053     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5054     nsDisplayListBuilder* aDisplayListBuilder) {
5055   wr::StackingContextParams params;
5056   params.mix_blend_mode =
5057       wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
5058   params.clip =
5059       wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5060   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5061                            params);
5062 
5063   return nsDisplayWrapList::CreateWebRenderCommands(
5064       aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5065 }
5066 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)5067 void nsDisplayBlendMode::Paint(nsDisplayListBuilder* aBuilder,
5068                                gfxContext* aCtx) {
5069   // This should be switched to use PushLayerWithBlend, once it's
5070   // been implemented for all DrawTarget backends.
5071   DrawTarget* dt = aCtx->GetDrawTarget();
5072   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5073   Rect rect = NSRectToRect(GetPaintRect(aBuilder, aCtx), appUnitsPerDevPixel);
5074   rect.RoundOut();
5075 
5076   // Create a temporary DrawTarget that is clipped to the area that
5077   // we're going to draw to. This will include the same transform as
5078   // is currently on |dt|.
5079   RefPtr<DrawTarget> temp =
5080       dt->CreateClippedDrawTarget(rect, SurfaceFormat::B8G8R8A8);
5081   if (!temp) {
5082     return;
5083   }
5084 
5085   RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(temp);
5086 
5087   GetChildren()->Paint(aBuilder, ctx,
5088                        mFrame->PresContext()->AppUnitsPerDevPixel());
5089 
5090   // Draw the temporary DT to the real destination, applying the blend mode, but
5091   // no transform.
5092   temp->Flush();
5093   RefPtr<SourceSurface> surface = temp->Snapshot();
5094   gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
5095   dt->SetTransform(Matrix());
5096   dt->DrawSurface(
5097       surface, Rect(surface->GetRect()), Rect(surface->GetRect()),
5098       DrawSurfaceOptions(),
5099       DrawOptions(1.0f, nsCSSRendering::GetGFXBlendMode(mBlendMode)));
5100 }
5101 
BlendMode()5102 gfx::CompositionOp nsDisplayBlendMode::BlendMode() {
5103   return nsCSSRendering::GetGFXBlendMode(mBlendMode);
5104 }
5105 
CanMerge(const nsDisplayItem * aItem) const5106 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const {
5107   // Items for the same content element should be merged into a single
5108   // compositing group.
5109   if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
5110       !HasSameContent(aItem)) {
5111     return false;
5112   }
5113 
5114   const auto* item = static_cast<const nsDisplayBlendMode*>(aItem);
5115   if (mIsForBackground || item->mIsForBackground) {
5116     // Don't merge background-blend-mode items
5117     return false;
5118   }
5119 
5120   return true;
5121 }
5122 
5123 /* static */
CreateForMixBlendMode(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot)5124 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForMixBlendMode(
5125     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5126     const ActiveScrolledRoot* aActiveScrolledRoot) {
5127   return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
5128                                                   aActiveScrolledRoot, false);
5129 }
5130 
5131 /* static */
CreateForBackgroundBlendMode(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsIFrame * aSecondaryFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot)5132 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForBackgroundBlendMode(
5133     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5134     nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot) {
5135   if (aSecondaryFrame) {
5136     auto type = GetTableTypeFromFrame(aFrame);
5137     auto index = static_cast<uint16_t>(type);
5138 
5139     return MakeDisplayItemWithIndex<nsDisplayTableBlendContainer>(
5140         aBuilder, aSecondaryFrame, index, aList, aActiveScrolledRoot, true,
5141         aFrame);
5142   }
5143 
5144   return MakeDisplayItemWithIndex<nsDisplayBlendContainer>(
5145       aBuilder, aFrame, 1, aList, aActiveScrolledRoot, true);
5146 }
5147 
nsDisplayBlendContainer(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,bool aIsForBackground)5148 nsDisplayBlendContainer::nsDisplayBlendContainer(
5149     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5150     const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground)
5151     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
5152       mIsForBackground(aIsForBackground) {
5153   MOZ_COUNT_CTOR(nsDisplayBlendContainer);
5154 }
5155 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)5156 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder* aBuilder,
5157                                     gfxContext* aCtx) {
5158   aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
5159   GetChildren()->Paint(aBuilder, aCtx,
5160                        mFrame->PresContext()->AppUnitsPerDevPixel());
5161   aCtx->GetDrawTarget()->PopLayer();
5162 }
5163 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5164 bool nsDisplayBlendContainer::CreateWebRenderCommands(
5165     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5166     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5167     nsDisplayListBuilder* aDisplayListBuilder) {
5168   wr::StackingContextParams params;
5169   params.flags |= wr::StackingContextFlags::IS_BLEND_CONTAINER;
5170   params.clip =
5171       wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5172   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5173                            params);
5174 
5175   return nsDisplayWrapList::CreateWebRenderCommands(
5176       aBuilder, aResources, sc, aManager, aDisplayListBuilder);
5177 }
5178 
nsDisplayOwnLayer(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,nsDisplayOwnLayerFlags aFlags,const ScrollbarData & aScrollbarData,bool aForceActive,bool aClearClipChain)5179 nsDisplayOwnLayer::nsDisplayOwnLayer(
5180     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5181     const ActiveScrolledRoot* aActiveScrolledRoot,
5182     nsDisplayOwnLayerFlags aFlags, const ScrollbarData& aScrollbarData,
5183     bool aForceActive, bool aClearClipChain)
5184     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
5185                         aClearClipChain),
5186       mFlags(aFlags),
5187       mScrollbarData(aScrollbarData),
5188       mForceActive(aForceActive),
5189       mWrAnimationId(0) {
5190   MOZ_COUNT_CTOR(nsDisplayOwnLayer);
5191 }
5192 
IsScrollThumbLayer() const5193 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
5194   return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb;
5195 }
5196 
IsScrollbarContainer() const5197 bool nsDisplayOwnLayer::IsScrollbarContainer() const {
5198   return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container;
5199 }
5200 
IsRootScrollbarContainer() const5201 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const {
5202   if (!IsScrollbarContainer()) {
5203     return false;
5204   }
5205 
5206   return mFrame->PresContext()->IsRootContentDocumentCrossProcess() &&
5207          mScrollbarData.mTargetViewId ==
5208              nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5209 }
5210 
IsZoomingLayer() const5211 bool nsDisplayOwnLayer::IsZoomingLayer() const {
5212   return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM;
5213 }
5214 
IsFixedPositionLayer() const5215 bool nsDisplayOwnLayer::IsFixedPositionLayer() const {
5216   return GetType() == DisplayItemType::TYPE_FIXED_POSITION;
5217 }
5218 
IsStickyPositionLayer() const5219 bool nsDisplayOwnLayer::IsStickyPositionLayer() const {
5220   return GetType() == DisplayItemType::TYPE_STICKY_POSITION;
5221 }
5222 
HasDynamicToolbar() const5223 bool nsDisplayOwnLayer::HasDynamicToolbar() const {
5224   if (!mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
5225     return false;
5226   }
5227   return mFrame->PresContext()->HasDynamicToolbar() ||
5228          // For tests on Android, this pref is set to simulate the dynamic
5229          // toolbar
5230          StaticPrefs::apz_fixed_margin_override_enabled();
5231 }
5232 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5233 bool nsDisplayOwnLayer::CreateWebRenderCommands(
5234     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5235     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5236     nsDisplayListBuilder* aDisplayListBuilder) {
5237   Maybe<wr::WrAnimationProperty> prop;
5238   bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() &&
5239                    (IsScrollThumbLayer() || IsZoomingLayer() ||
5240                     (IsFixedPositionLayer() && HasDynamicToolbar()) ||
5241                     (IsStickyPositionLayer() && HasDynamicToolbar()) ||
5242                     (IsRootScrollbarContainer() && HasDynamicToolbar()));
5243 
5244   if (needsProp) {
5245     // APZ is enabled and this is a scroll thumb or zooming layer, so we need
5246     // to create and set an animation id. That way APZ can adjust the position/
5247     // zoom of this content asynchronously as needed.
5248     RefPtr<WebRenderAPZAnimationData> animationData =
5249         aManager->CommandBuilder()
5250             .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(this);
5251     mWrAnimationId = animationData->GetAnimationId();
5252 
5253     prop.emplace();
5254     prop->id = mWrAnimationId;
5255     prop->key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5256                                wr::SpatialKeyKind::APZ);
5257     prop->effect_type = wr::WrAnimationType::Transform;
5258   }
5259 
5260   wr::StackingContextParams params;
5261   params.animation = prop.ptrOr(nullptr);
5262   params.clip =
5263       wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5264   if (IsScrollbarContainer()) {
5265     params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER;
5266   }
5267   if (IsScrollThumbLayer()) {
5268     params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_THUMB;
5269   }
5270   if (IsZoomingLayer() ||
5271       ((IsFixedPositionLayer() && HasDynamicToolbar()) ||
5272        (IsStickyPositionLayer() && HasDynamicToolbar()) ||
5273        (IsRootScrollbarContainer() && HasDynamicToolbar()))) {
5274     params.is_2d_scale_translation = true;
5275     params.should_snap = true;
5276   }
5277 
5278   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
5279                            params);
5280 
5281   nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
5282                                              aDisplayListBuilder);
5283   return true;
5284 }
5285 
UpdateScrollData(WebRenderScrollData * aData,WebRenderLayerScrollData * aLayerData)5286 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData,
5287                                          WebRenderLayerScrollData* aLayerData) {
5288   bool isRelevantToApz =
5289       (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() ||
5290        (IsFixedPositionLayer() && HasDynamicToolbar()) ||
5291        (IsStickyPositionLayer() && HasDynamicToolbar()));
5292 
5293   if (!isRelevantToApz) {
5294     return false;
5295   }
5296 
5297   if (!aLayerData) {
5298     return true;
5299   }
5300 
5301   if (IsZoomingLayer()) {
5302     aLayerData->SetZoomAnimationId(mWrAnimationId);
5303     return true;
5304   }
5305 
5306   if (IsFixedPositionLayer() && HasDynamicToolbar()) {
5307     aLayerData->SetFixedPositionAnimationId(mWrAnimationId);
5308     return true;
5309   }
5310 
5311   if (IsStickyPositionLayer() && HasDynamicToolbar()) {
5312     aLayerData->SetStickyPositionAnimationId(mWrAnimationId);
5313     return true;
5314   }
5315 
5316   MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer());
5317 
5318   aLayerData->SetScrollbarData(mScrollbarData);
5319 
5320   if (IsRootScrollbarContainer() && HasDynamicToolbar()) {
5321     aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5322     return true;
5323   }
5324 
5325   if (IsScrollThumbLayer()) {
5326     aLayerData->SetScrollbarAnimationId(mWrAnimationId);
5327     LayoutDeviceRect bounds = LayoutDeviceIntRect::FromAppUnits(
5328         mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
5329     // We use a resolution of 1.0 because this is a WebRender codepath which
5330     // always uses containerless scrolling, and so resolution doesn't apply to
5331     // scrollbars.
5332     LayerIntRect layerBounds =
5333         RoundedOut(bounds * LayoutDeviceToLayerScale(1.0f));
5334     aLayerData->SetVisibleRegion(LayerIntRegion(layerBounds));
5335   }
5336   return true;
5337 }
5338 
WriteDebugInfo(std::stringstream & aStream)5339 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) {
5340   aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
5341                              (int)mFlags, mScrollbarData.mTargetViewId)
5342                  .get();
5343 }
5344 
nsDisplaySubDocument(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsSubDocumentFrame * aSubDocFrame,nsDisplayList * aList,nsDisplayOwnLayerFlags aFlags)5345 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
5346                                            nsIFrame* aFrame,
5347                                            nsSubDocumentFrame* aSubDocFrame,
5348                                            nsDisplayList* aList,
5349                                            nsDisplayOwnLayerFlags aFlags)
5350     : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5351                         aBuilder->CurrentActiveScrolledRoot(), aFlags),
5352       mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5353       mShouldFlatten(false),
5354       mSubDocFrame(aSubDocFrame) {
5355   MOZ_COUNT_CTOR(nsDisplaySubDocument);
5356 
5357   if (mSubDocFrame && mSubDocFrame != mFrame) {
5358     mSubDocFrame->AddDisplayItem(this);
5359   }
5360 }
5361 
~nsDisplaySubDocument()5362 nsDisplaySubDocument::~nsDisplaySubDocument() {
5363   MOZ_COUNT_DTOR(nsDisplaySubDocument);
5364   if (mSubDocFrame) {
5365     mSubDocFrame->RemoveDisplayItem(this);
5366   }
5367 }
5368 
FrameForInvalidation() const5369 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const {
5370   return mSubDocFrame ? mSubDocFrame : mFrame;
5371 }
5372 
RemoveFrame(nsIFrame * aFrame)5373 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) {
5374   if (aFrame == mSubDocFrame) {
5375     mSubDocFrame = nullptr;
5376     SetDeletedFrame();
5377   }
5378   nsDisplayOwnLayer::RemoveFrame(aFrame);
5379 }
5380 
Disown()5381 void nsDisplaySubDocument::Disown() {
5382   if (mFrame) {
5383     mFrame->RemoveDisplayItem(this);
5384     RemoveFrame(mFrame);
5385   }
5386   if (mSubDocFrame) {
5387     mSubDocFrame->RemoveDisplayItem(this);
5388     RemoveFrame(mSubDocFrame);
5389   }
5390 }
5391 
UseDisplayPortForViewport(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)5392 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder,
5393                                       nsIFrame* aFrame) {
5394   return aBuilder->IsPaintingToWindow() &&
5395          DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext());
5396 }
5397 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const5398 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder,
5399                                        bool* aSnap) const {
5400   bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5401 
5402   if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5403       usingDisplayPort) {
5404     *aSnap = false;
5405     return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
5406   }
5407 
5408   return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
5409 }
5410 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const5411 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5412                                                bool* aSnap) const {
5413   bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
5414 
5415   if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) &&
5416       usingDisplayPort) {
5417     *aSnap = false;
5418     return nsRegion();
5419   }
5420 
5421   return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
5422 }
5423 
5424 /* static */
CreateForFixedBackground(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsIFrame * aSecondaryFrame,nsDisplayBackgroundImage * aImage,const uint16_t aIndex,const ActiveScrolledRoot * aScrollTargetASR)5425 nsDisplayFixedPosition* nsDisplayFixedPosition::CreateForFixedBackground(
5426     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
5427     nsDisplayBackgroundImage* aImage, const uint16_t aIndex,
5428     const ActiveScrolledRoot* aScrollTargetASR) {
5429   nsDisplayList temp(aBuilder);
5430   temp.AppendToTop(aImage);
5431 
5432   if (aSecondaryFrame) {
5433     auto tableType = GetTableTypeFromFrame(aFrame);
5434     const uint16_t index = CalculateTablePerFrameKey(aIndex + 1, tableType);
5435     return MakeDisplayItemWithIndex<nsDisplayTableFixedPosition>(
5436         aBuilder, aSecondaryFrame, index, &temp, aFrame, aScrollTargetASR);
5437   }
5438 
5439   return MakeDisplayItemWithIndex<nsDisplayFixedPosition>(
5440       aBuilder, aFrame, aIndex + 1, &temp, aScrollTargetASR);
5441 }
5442 
nsDisplayFixedPosition(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,const ActiveScrolledRoot * aScrollTargetASR)5443 nsDisplayFixedPosition::nsDisplayFixedPosition(
5444     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5445     const ActiveScrolledRoot* aActiveScrolledRoot,
5446     const ActiveScrolledRoot* aScrollTargetASR)
5447     : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5448       mScrollTargetASR(aScrollTargetASR),
5449       mIsFixedBackground(false) {
5450   MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5451 }
5452 
nsDisplayFixedPosition(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aScrollTargetASR)5453 nsDisplayFixedPosition::nsDisplayFixedPosition(
5454     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5455     const ActiveScrolledRoot* aScrollTargetASR)
5456     : nsDisplayOwnLayer(aBuilder, aFrame, aList,
5457                         aBuilder->CurrentActiveScrolledRoot()),
5458       // For fixed backgrounds, this is the ASR for the nearest scroll frame.
5459       mScrollTargetASR(aScrollTargetASR),
5460       mIsFixedBackground(true) {
5461   MOZ_COUNT_CTOR(nsDisplayFixedPosition);
5462 }
5463 
GetScrollTargetId()5464 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() {
5465   if (mScrollTargetASR &&
5466       (mIsFixedBackground || !nsLayoutUtils::IsReallyFixedPos(mFrame))) {
5467     return mScrollTargetASR->GetViewId();
5468   }
5469   return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext());
5470 }
5471 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5472 bool nsDisplayFixedPosition::CreateWebRenderCommands(
5473     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5474     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5475     nsDisplayListBuilder* aDisplayListBuilder) {
5476   SideBits sides = SideBits::eNone;
5477   if (!mIsFixedBackground) {
5478     sides = nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5479   }
5480 
5481   // We install this RAII scrolltarget tracker so that any
5482   // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
5483   // share the same ASR as this item) use the correct scroll target. That way
5484   // attempts to scroll on those items will scroll the root scroll frame.
5485   wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker(
5486       aBuilder, GetActiveScrolledRoot(), GetScrollTargetId(), sides);
5487   return nsDisplayOwnLayer::CreateWebRenderCommands(
5488       aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
5489 }
5490 
UpdateScrollData(WebRenderScrollData * aData,WebRenderLayerScrollData * aLayerData)5491 bool nsDisplayFixedPosition::UpdateScrollData(
5492     WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5493   if (aLayerData) {
5494     if (!mIsFixedBackground) {
5495       aLayerData->SetFixedPositionSides(
5496           nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame));
5497     }
5498     aLayerData->SetFixedPositionScrollContainerId(GetScrollTargetId());
5499   }
5500   nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5501   return true;
5502 }
5503 
WriteDebugInfo(std::stringstream & aStream)5504 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream& aStream) {
5505   aStream << nsPrintfCString(
5506                  " (containerASR %s) (scrolltarget %" PRIu64 ")",
5507                  ActiveScrolledRoot::ToString(mScrollTargetASR).get(),
5508                  GetScrollTargetId())
5509                  .get();
5510 }
5511 
GetTableTypeFromFrame(nsIFrame * aFrame)5512 TableType GetTableTypeFromFrame(nsIFrame* aFrame) {
5513   if (aFrame->IsTableFrame()) {
5514     return TableType::Table;
5515   }
5516 
5517   if (aFrame->IsTableColFrame()) {
5518     return TableType::TableCol;
5519   }
5520 
5521   if (aFrame->IsTableColGroupFrame()) {
5522     return TableType::TableColGroup;
5523   }
5524 
5525   if (aFrame->IsTableRowFrame()) {
5526     return TableType::TableRow;
5527   }
5528 
5529   if (aFrame->IsTableRowGroupFrame()) {
5530     return TableType::TableRowGroup;
5531   }
5532 
5533   if (aFrame->IsTableCellFrame()) {
5534     return TableType::TableCell;
5535   }
5536 
5537   MOZ_ASSERT_UNREACHABLE("Invalid frame.");
5538   return TableType::Table;
5539 }
5540 
nsDisplayTableFixedPosition(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,nsIFrame * aAncestorFrame,const ActiveScrolledRoot * aScrollTargetASR)5541 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
5542     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5543     nsIFrame* aAncestorFrame, const ActiveScrolledRoot* aScrollTargetASR)
5544     : nsDisplayFixedPosition(aBuilder, aFrame, aList, aScrollTargetASR),
5545       mAncestorFrame(aAncestorFrame) {
5546   if (aBuilder->IsRetainingDisplayList()) {
5547     mAncestorFrame->AddDisplayItem(this);
5548   }
5549 }
5550 
nsDisplayStickyPosition(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,const ActiveScrolledRoot * aContainerASR,bool aClippedToDisplayPort)5551 nsDisplayStickyPosition::nsDisplayStickyPosition(
5552     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5553     const ActiveScrolledRoot* aActiveScrolledRoot,
5554     const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
5555     : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5556       mContainerASR(aContainerASR),
5557       mClippedToDisplayPort(aClippedToDisplayPort) {
5558   MOZ_COUNT_CTOR(nsDisplayStickyPosition);
5559 }
5560 
5561 // Returns the smallest distance from "0" to the range [min, max] where
5562 // min <= max. Despite the name, the return value is actually a 1-D vector,
5563 // and so may be negative if max < 0.
DistanceToRange(nscoord min,nscoord max)5564 static nscoord DistanceToRange(nscoord min, nscoord max) {
5565   MOZ_ASSERT(min <= max);
5566   if (max < 0) {
5567     return max;
5568   }
5569   if (min > 0) {
5570     return min;
5571   }
5572   MOZ_ASSERT(min <= 0 && max >= 0);
5573   return 0;
5574 }
5575 
5576 // Returns the magnitude of the part of the range [min, max] that is greater
5577 // than zero. The return value is always non-negative.
PositivePart(nscoord min,nscoord max)5578 static nscoord PositivePart(nscoord min, nscoord max) {
5579   MOZ_ASSERT(min <= max);
5580   if (min >= 0) {
5581     return max - min;
5582   }
5583   if (max > 0) {
5584     return max;
5585   }
5586   return 0;
5587 }
5588 
5589 // Returns the magnitude of the part of the range [min, max] that is less
5590 // than zero. The return value is always non-negative.
NegativePart(nscoord min,nscoord max)5591 static nscoord NegativePart(nscoord min, nscoord max) {
5592   MOZ_ASSERT(min <= max);
5593   if (max <= 0) {
5594     return max - min;
5595   }
5596   if (min < 0) {
5597     return 0 - min;
5598   }
5599   return 0;
5600 }
5601 
GetStickyScrollContainer()5602 StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() {
5603   StickyScrollContainer* stickyScrollContainer =
5604       StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
5605   if (stickyScrollContainer) {
5606     // If there's no ASR for the scrollframe that this sticky item is attached
5607     // to, then don't create a WR sticky item for it either. Trying to do so
5608     // will end in sadness because WR will interpret some coordinates as
5609     // relative to the nearest enclosing scrollframe, which will correspond
5610     // to the nearest ancestor ASR on the gecko side. That ASR will not be the
5611     // same as the scrollframe this sticky item is actually supposed to be
5612     // attached to, thus the sadness.
5613     // Not sending WR the sticky item is ok, because the enclosing scrollframe
5614     // will never be asynchronously scrolled. Instead we will always position
5615     // the sticky items correctly on the gecko side and WR will never need to
5616     // adjust their position itself.
5617     MOZ_ASSERT(
5618         stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled());
5619     if (!stickyScrollContainer->ScrollFrame()
5620              ->IsMaybeAsynchronouslyScrolled()) {
5621       stickyScrollContainer = nullptr;
5622     }
5623   }
5624   return stickyScrollContainer;
5625 }
5626 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5627 bool nsDisplayStickyPosition::CreateWebRenderCommands(
5628     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5629     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5630     nsDisplayListBuilder* aDisplayListBuilder) {
5631   StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5632 
5633   Maybe<wr::SpaceAndClipChainHelper> saccHelper;
5634 
5635   if (stickyScrollContainer) {
5636     float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5637 
5638     bool snap;
5639     nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
5640 
5641     Maybe<float> topMargin;
5642     Maybe<float> rightMargin;
5643     Maybe<float> bottomMargin;
5644     Maybe<float> leftMargin;
5645     wr::StickyOffsetBounds vBounds = {0.0, 0.0};
5646     wr::StickyOffsetBounds hBounds = {0.0, 0.0};
5647     nsPoint appliedOffset;
5648 
5649     nsRectAbsolute outer;
5650     nsRectAbsolute inner;
5651     stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5652 
5653     nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
5654     nsPoint offset =
5655         scrollFrame->GetOffsetToCrossDoc(Frame()) + ToReferenceFrame();
5656 
5657     // Adjust the scrollPort coordinates to be relative to the reference frame,
5658     // so that it is in the same space as everything else.
5659     nsRect scrollPort =
5660         stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
5661     scrollPort += offset;
5662 
5663     // The following computations make more sense upon understanding the
5664     // semantics of "inner" and "outer", which is explained in the comment on
5665     // SetStickyPositionData in Layers.h.
5666 
5667     if (outer.YMost() != inner.YMost()) {
5668       // Question: How far will itemBounds.y be from the top of the scrollport
5669       // when we have scrolled from the current scroll position of "0" to
5670       // reach the range [inner.YMost(), outer.YMost()] where the item gets
5671       // stuck?
5672       // Answer: the current distance is "itemBounds.y - scrollPort.y". That
5673       // needs to be adjusted by the distance to the range, less any other
5674       // sticky ranges that fall between 0 and the range. If the distance is
5675       // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
5676       // scrolling upwards (decreasing scroll offset) to reach that range,
5677       // which would increase itemBounds.y and make it farther away from the
5678       // top of the scrollport. So in that case the adjustment is -distance.
5679       // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
5680       // we would be scrolling downwards, itemBounds.y would decrease, and we
5681       // again need to adjust by -distance. If we are already in the range
5682       // then no adjustment is needed and distance is 0 so again using
5683       // -distance works. If the distance is positive, and the item has both
5684       // top and bottom sticky ranges, then the bottom sticky range may fall
5685       // (entirely[1] or partly[2]) between the current scroll position.
5686       // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
5687       // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
5688       // In these cases, the item doesn't actually move for that part of the
5689       // distance, so we need to subtract out that bit, which can be computed
5690       // as the positive portion of the range [outer.Y(), inner.Y()].
5691       nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
5692       if (distance > 0) {
5693         distance -= PositivePart(outer.Y(), inner.Y());
5694       }
5695       topMargin = Some(NSAppUnitsToFloatPixels(
5696           itemBounds.y - scrollPort.y - distance, auPerDevPixel));
5697       // Question: What is the maximum positive ("downward") offset that WR
5698       // will have to apply to this item in order to prevent the item from
5699       // visually moving?
5700       // Answer: Since the item is "sticky" in the range [inner.YMost(),
5701       // outer.YMost()], the maximum offset will be the size of the range, which
5702       // is outer.YMost() - inner.YMost().
5703       vBounds.max =
5704           NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
5705       // Question: how much of an offset has layout already applied to the item?
5706       // Answer: if we are
5707       // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
5708       // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
5709       // then layout has already applied some offset to the position of the
5710       // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
5711       // and |outer.YMost() - inner.YMost()| in case (b).
5712       if (inner.YMost() < 0) {
5713         appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
5714         MOZ_ASSERT(appliedOffset.y > 0);
5715       }
5716     }
5717     if (outer.Y() != inner.Y()) {
5718       // Similar logic as in the previous section, but this time we care about
5719       // the distance from itemBounds.YMost() to scrollPort.YMost().
5720       nscoord distance = DistanceToRange(outer.Y(), inner.Y());
5721       if (distance < 0) {
5722         distance += NegativePart(inner.YMost(), outer.YMost());
5723       }
5724       bottomMargin = Some(NSAppUnitsToFloatPixels(
5725           scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
5726       // And here WR will be moving the item upwards rather than downwards so
5727       // again things are inverted from the previous block.
5728       vBounds.min =
5729           NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
5730       // We can't have appliedOffset be both positive and negative, and the top
5731       // adjustment takes priority. So here we only update appliedOffset.y if
5732       // it wasn't set by the top-sticky case above.
5733       if (appliedOffset.y == 0 && inner.Y() > 0) {
5734         appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
5735         MOZ_ASSERT(appliedOffset.y < 0);
5736       }
5737     }
5738     // Same as above, but for the x-axis
5739     if (outer.XMost() != inner.XMost()) {
5740       nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
5741       if (distance > 0) {
5742         distance -= PositivePart(outer.X(), inner.X());
5743       }
5744       leftMargin = Some(NSAppUnitsToFloatPixels(
5745           itemBounds.x - scrollPort.x - distance, auPerDevPixel));
5746       hBounds.max =
5747           NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
5748       if (inner.XMost() < 0) {
5749         appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
5750         MOZ_ASSERT(appliedOffset.x > 0);
5751       }
5752     }
5753     if (outer.X() != inner.X()) {
5754       nscoord distance = DistanceToRange(outer.X(), inner.X());
5755       if (distance < 0) {
5756         distance += NegativePart(inner.XMost(), outer.XMost());
5757       }
5758       rightMargin = Some(NSAppUnitsToFloatPixels(
5759           scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
5760       hBounds.min =
5761           NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
5762       if (appliedOffset.x == 0 && inner.X() > 0) {
5763         appliedOffset.x = std::max(0, outer.X()) - inner.X();
5764         MOZ_ASSERT(appliedOffset.x < 0);
5765       }
5766     }
5767 
5768     LayoutDeviceRect bounds =
5769         LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
5770     wr::LayoutVector2D applied = {
5771         NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
5772         NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
5773     wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame(
5774         wr::ToLayoutRect(bounds), topMargin.ptrOr(nullptr),
5775         rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
5776         leftMargin.ptrOr(nullptr), vBounds, hBounds, applied,
5777         wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
5778                        wr::SpatialKeyKind::Sticky));
5779 
5780     saccHelper.emplace(aBuilder, spatialId);
5781     aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId);
5782   }
5783 
5784   {
5785     wr::StackingContextParams params;
5786     params.clip =
5787         wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
5788     StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
5789                              aBuilder, params);
5790     nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc,
5791                                                aManager, aDisplayListBuilder);
5792   }
5793 
5794   if (stickyScrollContainer) {
5795     aManager->CommandBuilder().PopOverrideForASR(mContainerASR);
5796   }
5797 
5798   return true;
5799 }
5800 
CalculateLayerScrollRanges(StickyScrollContainer * aStickyScrollContainer,float aAppUnitsPerDevPixel,float aScaleX,float aScaleY,LayerRectAbsolute & aStickyOuter,LayerRectAbsolute & aStickyInner)5801 void nsDisplayStickyPosition::CalculateLayerScrollRanges(
5802     StickyScrollContainer* aStickyScrollContainer, float aAppUnitsPerDevPixel,
5803     float aScaleX, float aScaleY, LayerRectAbsolute& aStickyOuter,
5804     LayerRectAbsolute& aStickyInner) {
5805   nsRectAbsolute outer;
5806   nsRectAbsolute inner;
5807   aStickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
5808   aStickyOuter.SetBox(
5809       NSAppUnitsToFloatPixels(outer.X(), aAppUnitsPerDevPixel) * aScaleX,
5810       NSAppUnitsToFloatPixels(outer.Y(), aAppUnitsPerDevPixel) * aScaleY,
5811       NSAppUnitsToFloatPixels(outer.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5812       NSAppUnitsToFloatPixels(outer.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5813   aStickyInner.SetBox(
5814       NSAppUnitsToFloatPixels(inner.X(), aAppUnitsPerDevPixel) * aScaleX,
5815       NSAppUnitsToFloatPixels(inner.Y(), aAppUnitsPerDevPixel) * aScaleY,
5816       NSAppUnitsToFloatPixels(inner.XMost(), aAppUnitsPerDevPixel) * aScaleX,
5817       NSAppUnitsToFloatPixels(inner.YMost(), aAppUnitsPerDevPixel) * aScaleY);
5818 }
5819 
UpdateScrollData(WebRenderScrollData * aData,WebRenderLayerScrollData * aLayerData)5820 bool nsDisplayStickyPosition::UpdateScrollData(
5821     WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5822   bool hasDynamicToolbar = HasDynamicToolbar();
5823   if (aLayerData && hasDynamicToolbar) {
5824     StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer();
5825     if (stickyScrollContainer) {
5826       float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5827       float cumulativeResolution =
5828           mFrame->PresShell()->GetCumulativeResolution();
5829       LayerRectAbsolute stickyOuter;
5830       LayerRectAbsolute stickyInner;
5831       CalculateLayerScrollRanges(stickyScrollContainer, auPerDevPixel,
5832                                  cumulativeResolution, cumulativeResolution,
5833                                  stickyOuter, stickyInner);
5834       aLayerData->SetStickyScrollRangeOuter(stickyOuter);
5835       aLayerData->SetStickyScrollRangeInner(stickyInner);
5836 
5837       SideBits sides =
5838           nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame);
5839       aLayerData->SetFixedPositionSides(sides);
5840 
5841       ScrollableLayerGuid::ViewID scrollId =
5842           nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame()
5843                                                ->GetScrolledFrame()
5844                                                ->GetContent());
5845       aLayerData->SetStickyPositionScrollContainerId(scrollId);
5846     }
5847   }
5848   // Return true if either there is a dynamic toolbar affecting this sticky
5849   // item or the OwnLayer base implementation returns true for some other
5850   // reason.
5851   bool ret = hasDynamicToolbar;
5852   ret |= nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5853   return ret;
5854 }
5855 
nsDisplayScrollInfoLayer(nsDisplayListBuilder * aBuilder,nsIFrame * aScrolledFrame,nsIFrame * aScrollFrame,const CompositorHitTestInfo & aHitInfo,const nsRect & aHitArea)5856 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
5857     nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
5858     nsIFrame* aScrollFrame, const CompositorHitTestInfo& aHitInfo,
5859     const nsRect& aHitArea)
5860     : nsDisplayWrapList(aBuilder, aScrollFrame),
5861       mScrollFrame(aScrollFrame),
5862       mScrolledFrame(aScrolledFrame),
5863       mScrollParentId(aBuilder->GetCurrentScrollParentId()),
5864       mHitInfo(aHitInfo),
5865       mHitArea(aHitArea) {
5866 #ifdef NS_BUILD_REFCNT_LOGGING
5867   MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
5868 #endif
5869 }
5870 
ComputeScrollMetadata(nsDisplayListBuilder * aBuilder,WebRenderLayerManager * aLayerManager)5871 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
5872     nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) {
5873   ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
5874       mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(),
5875       ToReferenceFrame(), aLayerManager, mScrollParentId,
5876       mScrollFrame->GetSize(), false);
5877   metadata.GetMetrics().SetIsScrollInfoLayer(true);
5878   nsIScrollableFrame* scrollableFrame = mScrollFrame->GetScrollTargetFrame();
5879   if (scrollableFrame) {
5880     aBuilder->AddScrollFrameToNotify(scrollableFrame);
5881   }
5882 
5883   return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
5884 }
5885 
UpdateScrollData(WebRenderScrollData * aData,WebRenderLayerScrollData * aLayerData)5886 bool nsDisplayScrollInfoLayer::UpdateScrollData(
5887     WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5888   if (aLayerData) {
5889     UniquePtr<ScrollMetadata> metadata =
5890         ComputeScrollMetadata(aData->GetBuilder(), aData->GetManager());
5891     MOZ_ASSERT(aData);
5892     MOZ_ASSERT(metadata);
5893     aLayerData->AppendScrollMetadata(*aData, *metadata);
5894   }
5895   return true;
5896 }
5897 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5898 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands(
5899     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
5900     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
5901     nsDisplayListBuilder* aDisplayListBuilder) {
5902   ScrollableLayerGuid::ViewID scrollId =
5903       nsLayoutUtils::FindOrCreateIDFor(mScrollFrame->GetContent());
5904 
5905   const LayoutDeviceRect devRect = LayoutDeviceRect::FromAppUnits(
5906       mHitArea, mScrollFrame->PresContext()->AppUnitsPerDevPixel());
5907 
5908   const wr::LayoutRect rect = wr::ToLayoutRect(devRect);
5909 
5910   aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, mHitInfo,
5911                        SideBits::eNone);
5912 
5913   return true;
5914 }
5915 
WriteDebugInfo(std::stringstream & aStream)5916 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) {
5917   aStream << " (scrollframe " << mScrollFrame << " scrolledFrame "
5918           << mScrolledFrame << ")";
5919 }
5920 
nsDisplayZoom(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsSubDocumentFrame * aSubDocFrame,nsDisplayList * aList,int32_t aAPD,int32_t aParentAPD,nsDisplayOwnLayerFlags aFlags)5921 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5922                              nsSubDocumentFrame* aSubDocFrame,
5923                              nsDisplayList* aList, int32_t aAPD,
5924                              int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags)
5925     : nsDisplaySubDocument(aBuilder, aFrame, aSubDocFrame, aList, aFlags),
5926       mAPD(aAPD),
5927       mParentAPD(aParentAPD) {
5928   MOZ_COUNT_CTOR(nsDisplayZoom);
5929 }
5930 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const5931 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder,
5932                                 bool* aSnap) const {
5933   nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
5934   *aSnap = false;
5935   return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD);
5936 }
5937 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)5938 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5939                             HitTestState* aState,
5940                             nsTArray<nsIFrame*>* aOutFrames) {
5941   nsRect rect;
5942   // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
5943   // rect as well instead of possibly rounding the width or height to zero.
5944   if (aRect.width == 1 && aRect.height == 1) {
5945     rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD));
5946     rect.width = rect.height = 1;
5947   } else {
5948     rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
5949   }
5950   mList.HitTest(aBuilder, rect, aState, aOutFrames);
5951 }
5952 
nsDisplayAsyncZoom(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,FrameMetrics::ViewID aViewID)5953 nsDisplayAsyncZoom::nsDisplayAsyncZoom(
5954     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5955     const ActiveScrolledRoot* aActiveScrolledRoot, FrameMetrics::ViewID aViewID)
5956     : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
5957       mViewID(aViewID) {
5958   MOZ_COUNT_CTOR(nsDisplayAsyncZoom);
5959 }
5960 
5961 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayAsyncZoom()5962 nsDisplayAsyncZoom::~nsDisplayAsyncZoom() {
5963   MOZ_COUNT_DTOR(nsDisplayAsyncZoom);
5964 }
5965 #endif
5966 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)5967 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder* aBuilder,
5968                                  const nsRect& aRect, HitTestState* aState,
5969                                  nsTArray<nsIFrame*>* aOutFrames) {
5970 #ifdef DEBUG
5971   nsIScrollableFrame* scrollFrame = do_QueryFrame(mFrame);
5972   MOZ_ASSERT(scrollFrame && ViewportUtils::IsZoomedContentRoot(
5973                                 scrollFrame->GetScrolledFrame()));
5974 #endif
5975   nsRect rect = ViewportUtils::VisualToLayout(aRect, mFrame->PresShell());
5976   mList.HitTest(aBuilder, rect, aState, aOutFrames);
5977 }
5978 
UpdateScrollData(WebRenderScrollData * aData,WebRenderLayerScrollData * aLayerData)5979 bool nsDisplayAsyncZoom::UpdateScrollData(
5980     WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
5981   bool ret = nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData);
5982   MOZ_ASSERT(ret);
5983   if (aLayerData) {
5984     aLayerData->SetAsyncZoomContainerId(mViewID);
5985   }
5986   return ret;
5987 }
5988 
5989 ///////////////////////////////////////////////////
5990 // nsDisplayTransform Implementation
5991 //
5992 
5993 #ifndef DEBUG
5994 static_assert(sizeof(nsDisplayTransform) <= 512,
5995               "nsDisplayTransform has grown");
5996 #endif
5997 
nsDisplayTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const nsRect & aChildrenBuildingRect)5998 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
5999                                        nsIFrame* aFrame, nsDisplayList* aList,
6000                                        const nsRect& aChildrenBuildingRect)
6001     : nsPaintedDisplayItem(aBuilder, aFrame),
6002       mChildren(aBuilder),
6003       mTransform(Some(Matrix4x4())),
6004       mChildrenBuildingRect(aChildrenBuildingRect),
6005       mPrerenderDecision(PrerenderDecision::No),
6006       mIsTransformSeparator(true),
6007       mHasTransformGetter(false),
6008       mHasAssociatedPerspective(false) {
6009   MOZ_COUNT_CTOR(nsDisplayTransform);
6010   MOZ_ASSERT(aFrame, "Must have a frame!");
6011   Init(aBuilder, aList);
6012 }
6013 
nsDisplayTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const nsRect & aChildrenBuildingRect,PrerenderDecision aPrerenderDecision)6014 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
6015                                        nsIFrame* aFrame, nsDisplayList* aList,
6016                                        const nsRect& aChildrenBuildingRect,
6017                                        PrerenderDecision aPrerenderDecision)
6018     : nsPaintedDisplayItem(aBuilder, aFrame),
6019       mChildren(aBuilder),
6020       mChildrenBuildingRect(aChildrenBuildingRect),
6021       mPrerenderDecision(aPrerenderDecision),
6022       mIsTransformSeparator(false),
6023       mHasTransformGetter(false),
6024       mHasAssociatedPerspective(false) {
6025   MOZ_COUNT_CTOR(nsDisplayTransform);
6026   MOZ_ASSERT(aFrame, "Must have a frame!");
6027   SetReferenceFrameToAncestor(aBuilder);
6028   Init(aBuilder, aList);
6029 }
6030 
nsDisplayTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const nsRect & aChildrenBuildingRect,decltype(WithTransformGetter))6031 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
6032                                        nsIFrame* aFrame, nsDisplayList* aList,
6033                                        const nsRect& aChildrenBuildingRect,
6034                                        decltype(WithTransformGetter))
6035     : nsPaintedDisplayItem(aBuilder, aFrame),
6036       mChildren(aBuilder),
6037       mChildrenBuildingRect(aChildrenBuildingRect),
6038       mPrerenderDecision(PrerenderDecision::No),
6039       mIsTransformSeparator(false),
6040       mHasTransformGetter(true),
6041       mHasAssociatedPerspective(false) {
6042   MOZ_COUNT_CTOR(nsDisplayTransform);
6043   MOZ_ASSERT(aFrame, "Must have a frame!");
6044   MOZ_ASSERT(aFrame->GetTransformGetter());
6045   Init(aBuilder, aList);
6046 }
6047 
SetReferenceFrameToAncestor(nsDisplayListBuilder * aBuilder)6048 void nsDisplayTransform::SetReferenceFrameToAncestor(
6049     nsDisplayListBuilder* aBuilder) {
6050   if (mFrame == aBuilder->RootReferenceFrame()) {
6051     return;
6052   }
6053   // We manually recompute mToReferenceFrame without going through the
6054   // builder, since this won't apply the 'additional offset'. Our
6055   // children will already be painting with that applied, and we don't
6056   // want to include it a second time in our transform. We don't recompute
6057   // our visible/building rects, since those should still include the additional
6058   // offset.
6059   // TODO: Are there are things computed using our ToReferenceFrame that should
6060   // have the additional offset applied? Should we instead just manually remove
6061   // the offset from our transform instead of this more general value?
6062   // Can we instead apply the additional offset to us and not our children, like
6063   // we do for all other offsets (and how reference frames are supposed to
6064   // work)?
6065   nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame);
6066   const nsIFrame* referenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
6067   mToReferenceFrame = mFrame->GetOffsetToCrossDoc(referenceFrame);
6068 }
6069 
Init(nsDisplayListBuilder * aBuilder,nsDisplayList * aChildren)6070 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
6071                               nsDisplayList* aChildren) {
6072   mChildren.AppendToTop(aChildren);
6073   UpdateBounds(aBuilder);
6074 }
6075 
ShouldFlattenAway(nsDisplayListBuilder * aBuilder)6076 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
6077   return false;
6078 }
6079 
6080 /* Returns the delta specified by the transform-origin property.
6081  * This is a positive delta, meaning that it indicates the direction to move
6082  * to get from (0, 0) of the frame to the transform origin.  This function is
6083  * called off the main thread.
6084  */
6085 /* static */
GetDeltaToTransformOrigin(const nsIFrame * aFrame,TransformReferenceBox & aRefBox,float aAppUnitsPerPixel)6086 Point3D nsDisplayTransform::GetDeltaToTransformOrigin(
6087     const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6088     float aAppUnitsPerPixel) {
6089   MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6090   MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6091                  aFrame->Combines3DTransformWithAncestors(),
6092              "Shouldn't get a delta for an untransformed frame!");
6093 
6094   if (!aFrame->IsTransformed()) {
6095     return Point3D();
6096   }
6097 
6098   /* For both of the coordinates, if the value of transform is a
6099    * percentage, it's relative to the size of the frame.  Otherwise, if it's
6100    * a distance, it's already computed for us!
6101    */
6102   const nsStyleDisplay* display = aFrame->StyleDisplay();
6103 
6104   const StyleTransformOrigin& transformOrigin = display->mTransformOrigin;
6105   CSSPoint origin = nsStyleTransformMatrix::Convert2DPosition(
6106       transformOrigin.horizontal, transformOrigin.vertical, aRefBox);
6107 
6108   if (aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
6109     // SVG frames (unlike other frames) have a reference box that can be (and
6110     // typically is) offset from the TopLeft() of the frame. We need to account
6111     // for that here.
6112     origin.x += CSSPixel::FromAppUnits(aRefBox.X());
6113     origin.y += CSSPixel::FromAppUnits(aRefBox.Y());
6114   }
6115 
6116   float scale = AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel);
6117   float z = transformOrigin.depth._0;
6118   return Point3D(origin.x * scale, origin.y * scale, z * scale);
6119 }
6120 
6121 /* static */
ComputePerspectiveMatrix(const nsIFrame * aFrame,float aAppUnitsPerPixel,Matrix4x4 & aOutMatrix)6122 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
6123                                                   float aAppUnitsPerPixel,
6124                                                   Matrix4x4& aOutMatrix) {
6125   MOZ_ASSERT(aFrame, "Can't get delta for a null frame!");
6126   MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
6127                  aFrame->Combines3DTransformWithAncestors(),
6128              "Shouldn't get a delta for an untransformed frame!");
6129   MOZ_ASSERT(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
6130 
6131   if (!aFrame->IsTransformed()) {
6132     return false;
6133   }
6134 
6135   // TODO: Is it possible that the perspectiveFrame's bounds haven't been set
6136   // correctly yet (similar to the aBoundsOverride case for
6137   // GetResultingTransformMatrix)?
6138   nsIFrame* perspectiveFrame =
6139       aFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6140   if (!perspectiveFrame) {
6141     return false;
6142   }
6143 
6144   /* Grab the values for perspective and perspective-origin (if present) */
6145   const nsStyleDisplay* perspectiveDisplay = perspectiveFrame->StyleDisplay();
6146   if (perspectiveDisplay->mChildPerspective.IsNone()) {
6147     return false;
6148   }
6149 
6150   MOZ_ASSERT(perspectiveDisplay->mChildPerspective.IsLength());
6151   float perspective =
6152       perspectiveDisplay->mChildPerspective.AsLength().ToCSSPixels();
6153   perspective = std::max(1.0f, perspective);
6154   if (perspective < std::numeric_limits<Float>::epsilon()) {
6155     return true;
6156   }
6157 
6158   TransformReferenceBox refBox(perspectiveFrame);
6159 
6160   Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition(
6161       perspectiveDisplay->mPerspectiveOrigin.horizontal,
6162       perspectiveDisplay->mPerspectiveOrigin.vertical, refBox,
6163       aAppUnitsPerPixel);
6164 
6165   /* GetOffsetTo computes the offset required to move from 0,0 in
6166    * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of
6167    * this, it's faster to compute this way.
6168    */
6169   nsPoint frameToPerspectiveOffset = -aFrame->GetOffsetTo(perspectiveFrame);
6170   Point frameToPerspectiveGfxOffset(
6171       NSAppUnitsToFloatPixels(frameToPerspectiveOffset.x, aAppUnitsPerPixel),
6172       NSAppUnitsToFloatPixels(frameToPerspectiveOffset.y, aAppUnitsPerPixel));
6173 
6174   /* Move the perspective origin to be relative to aFrame, instead of relative
6175    * to the containing block which is how it was specified in the style system.
6176    */
6177   perspectiveOrigin += frameToPerspectiveGfxOffset;
6178 
6179   aOutMatrix._34 =
6180       -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective),
6181                                      aAppUnitsPerPixel);
6182 
6183   aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
6184   return true;
6185 }
6186 
FrameTransformProperties(const nsIFrame * aFrame,TransformReferenceBox & aRefBox,float aAppUnitsPerPixel)6187 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
6188     const nsIFrame* aFrame, TransformReferenceBox& aRefBox,
6189     float aAppUnitsPerPixel)
6190     : mFrame(aFrame),
6191       mTranslate(aFrame->StyleDisplay()->mTranslate),
6192       mRotate(aFrame->StyleDisplay()->mRotate),
6193       mScale(aFrame->StyleDisplay()->mScale),
6194       mTransform(aFrame->StyleDisplay()->mTransform),
6195       mMotion(MotionPathUtils::ResolveMotionPath(aFrame, aRefBox)),
6196       mToTransformOrigin(
6197           GetDeltaToTransformOrigin(aFrame, aRefBox, aAppUnitsPerPixel)) {}
6198 
6199 /* Wraps up the transform matrix in a change-of-basis matrix pair that
6200  * translates from local coordinate space to transform coordinate space, then
6201  * hands it back.
6202  */
GetResultingTransformMatrix(const FrameTransformProperties & aProperties,TransformReferenceBox & aRefBox,float aAppUnitsPerPixel)6203 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6204     const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6205     float aAppUnitsPerPixel) {
6206   return GetResultingTransformMatrixInternal(aProperties, aRefBox, nsPoint(),
6207                                              aAppUnitsPerPixel, 0);
6208 }
6209 
GetResultingTransformMatrix(const nsIFrame * aFrame,const nsPoint & aOrigin,float aAppUnitsPerPixel,uint32_t aFlags)6210 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
6211     const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel,
6212     uint32_t aFlags) {
6213   TransformReferenceBox refBox(aFrame);
6214   FrameTransformProperties props(aFrame, refBox, aAppUnitsPerPixel);
6215   return GetResultingTransformMatrixInternal(props, refBox, aOrigin,
6216                                              aAppUnitsPerPixel, aFlags);
6217 }
6218 
GetResultingTransformMatrixInternal(const FrameTransformProperties & aProperties,TransformReferenceBox & aRefBox,const nsPoint & aOrigin,float aAppUnitsPerPixel,uint32_t aFlags)6219 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal(
6220     const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox,
6221     const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags) {
6222   const nsIFrame* frame = aProperties.mFrame;
6223   NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE),
6224                "Must have a frame to compute perspective!");
6225 
6226   // Get the underlying transform matrix:
6227 
6228   /* Get the matrix, then change its basis to factor in the origin. */
6229   Matrix4x4 result;
6230   // Call IsSVGTransformed() regardless of the value of
6231   // aProperties.HasTransform(), since we still need any
6232   // potential parentsChildrenOnlyTransform.
6233   Matrix svgTransform, parentsChildrenOnlyTransform;
6234   const bool hasSVGTransforms =
6235       frame && frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
6236       frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform);
6237   bool shouldRound = nsLayoutUtils::ShouldSnapToGrid(frame);
6238 
6239   /* Transformed frames always have a transform, or are preserving 3d (and might
6240    * still have perspective!) */
6241   if (aProperties.HasTransform()) {
6242     result = nsStyleTransformMatrix::ReadTransforms(
6243         aProperties.mTranslate, aProperties.mRotate, aProperties.mScale,
6244         aProperties.mMotion, aProperties.mTransform, aRefBox,
6245         aAppUnitsPerPixel);
6246   } else if (hasSVGTransforms) {
6247     // Correct the translation components for zoom:
6248     float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6249     svgTransform._31 *= pixelsPerCSSPx;
6250     svgTransform._32 *= pixelsPerCSSPx;
6251     result = Matrix4x4::From2D(svgTransform);
6252   }
6253 
6254   // Apply any translation due to 'transform-origin' and/or 'transform-box':
6255   result.ChangeBasis(aProperties.mToTransformOrigin);
6256 
6257   // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for
6258   // an explanation of what children-only transforms are.
6259   const bool parentHasChildrenOnlyTransform =
6260       hasSVGTransforms && !parentsChildrenOnlyTransform.IsIdentity();
6261 
6262   if (parentHasChildrenOnlyTransform) {
6263     float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
6264     parentsChildrenOnlyTransform._31 *= pixelsPerCSSPx;
6265     parentsChildrenOnlyTransform._32 *= pixelsPerCSSPx;
6266 
6267     Point3D frameOffset(
6268         NSAppUnitsToFloatPixels(-frame->GetPosition().x, aAppUnitsPerPixel),
6269         NSAppUnitsToFloatPixels(-frame->GetPosition().y, aAppUnitsPerPixel), 0);
6270     Matrix4x4 parentsChildrenOnlyTransform3D =
6271         Matrix4x4::From2D(parentsChildrenOnlyTransform)
6272             .ChangeBasis(frameOffset);
6273 
6274     result *= parentsChildrenOnlyTransform3D;
6275   }
6276 
6277   Matrix4x4 perspectiveMatrix;
6278   bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE;
6279   if (hasPerspective) {
6280     if (ComputePerspectiveMatrix(frame, aAppUnitsPerPixel, perspectiveMatrix)) {
6281       result *= perspectiveMatrix;
6282     }
6283   }
6284 
6285   if ((aFlags & INCLUDE_PRESERVE3D_ANCESTORS) && frame &&
6286       frame->Combines3DTransformWithAncestors()) {
6287     // Include the transform set on our parent
6288     nsIFrame* parentFrame =
6289         frame->GetClosestFlattenedTreeAncestorPrimaryFrame();
6290     NS_ASSERTION(parentFrame && parentFrame->IsTransformed() &&
6291                      parentFrame->Extend3DContext(),
6292                  "Preserve3D mismatch!");
6293     TransformReferenceBox refBox(parentFrame);
6294     FrameTransformProperties props(parentFrame, refBox, aAppUnitsPerPixel);
6295 
6296     uint32_t flags =
6297         aFlags & (INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE);
6298 
6299     // If this frame isn't transformed (but we exist for backface-visibility),
6300     // then we're not a reference frame so no offset to origin will be added.
6301     // Otherwise we need to manually translate into our parent's coordinate
6302     // space.
6303     if (frame->IsTransformed()) {
6304       nsLayoutUtils::PostTranslate(result, frame->GetPosition(),
6305                                    aAppUnitsPerPixel, shouldRound);
6306     }
6307     Matrix4x4 parent = GetResultingTransformMatrixInternal(
6308         props, refBox, nsPoint(0, 0), aAppUnitsPerPixel, flags);
6309     result = result * parent;
6310   }
6311 
6312   if (aFlags & OFFSET_BY_ORIGIN) {
6313     nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel,
6314                                  shouldRound);
6315   }
6316 
6317   return result;
6318 }
6319 
CanUseAsyncAnimations(nsDisplayListBuilder * aBuilder)6320 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6321   static constexpr nsCSSPropertyIDSet opacitySet =
6322       nsCSSPropertyIDSet::OpacityProperties();
6323   if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, opacitySet)) {
6324     return true;
6325   }
6326 
6327   EffectCompositor::SetPerformanceWarning(
6328       mFrame, opacitySet,
6329       AnimationPerformanceWarning(
6330           AnimationPerformanceWarning::Type::OpacityFrameInactive));
6331 
6332   return false;
6333 }
6334 
CanUseAsyncAnimations(nsDisplayListBuilder * aBuilder)6335 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
6336   return mPrerenderDecision != PrerenderDecision::No;
6337 }
6338 
CanUseAsyncAnimations(nsDisplayListBuilder * aBuilder)6339 bool nsDisplayBackgroundColor::CanUseAsyncAnimations(
6340     nsDisplayListBuilder* aBuilder) {
6341   return StaticPrefs::gfx_omta_background_color();
6342 }
6343 
IsInStickyPositionedSubtree(const nsIFrame * aFrame)6344 static bool IsInStickyPositionedSubtree(const nsIFrame* aFrame) {
6345   for (const nsIFrame* frame = aFrame; frame;
6346        frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame)) {
6347     if (frame->IsStickyPositioned()) {
6348       return true;
6349     }
6350   }
6351   return false;
6352 }
6353 
ShouldUsePartialPrerender(const nsIFrame * aFrame)6354 static bool ShouldUsePartialPrerender(const nsIFrame* aFrame) {
6355   return StaticPrefs::layout_animation_prerender_partial() &&
6356          // Bug 1642547: Support partial prerender for position:sticky elements.
6357          !IsInStickyPositionedSubtree(aFrame);
6358 }
6359 
6360 /* static */
ShouldPrerenderTransformedContent(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsRect * aDirtyRect)6361 auto nsDisplayTransform::ShouldPrerenderTransformedContent(
6362     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect)
6363     -> PrerenderInfo {
6364   PrerenderInfo result;
6365   // If we are in a preserve-3d tree, and we've disallowed async animations, we
6366   // return No prerender decision directly.
6367   if ((aFrame->Extend3DContext() ||
6368        aFrame->Combines3DTransformWithAncestors()) &&
6369       !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
6370     return result;
6371   }
6372 
6373   // Elements whose transform has been modified recently, or which
6374   // have a compositor-animated transform, can be prerendered. An element
6375   // might have only just had its transform animated in which case
6376   // the ActiveLayerManager may not have been notified yet.
6377   static constexpr nsCSSPropertyIDSet transformSet =
6378       nsCSSPropertyIDSet::TransformLikeProperties();
6379   if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame) &&
6380       !EffectCompositor::HasAnimationsForCompositor(
6381           aFrame, DisplayItemType::TYPE_TRANSFORM)) {
6382     EffectCompositor::SetPerformanceWarning(
6383         aFrame, transformSet,
6384         AnimationPerformanceWarning(
6385             AnimationPerformanceWarning::Type::TransformFrameInactive));
6386 
6387     // This case happens when we're sure that the frame is not animated and its
6388     // preserve-3d ancestors are not, either. So we don't need to pre-render.
6389     // However, this decision shouldn't affect the decisions for other frames in
6390     // the preserve-3d context. We need this flag to determine whether we should
6391     // block async animations on other frames in the current preserve-3d tree.
6392     result.mHasAnimations = false;
6393     return result;
6394   }
6395 
6396   // We should not allow prerender if any ancestor container element has
6397   // mask/clip-path effects.
6398   //
6399   // With prerender and async transform animation, we do not need to restyle an
6400   // animated element to respect position changes, since that transform is done
6401   // by layer animation. As a result, the container element is not aware of
6402   // position change of that containing element and loses the chance to update
6403   // the content of mask/clip-path.
6404   //
6405   // Why do we need to update a mask? This is relative to how we generate a
6406   // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
6407   // mask layer, to reduce memory usage, we did not choose the size of the
6408   // masked element as mask size. Instead, we read the union of bounds of all
6409   // children display items by nsDisplayWrapList::GetBounds, which is smaller
6410   // than or equal to the masked element's boundary, and use it as the position
6411   // size of the mask layer. That union bounds is actually affected by the
6412   // geometry of the animated element. To keep the content of mask up to date,
6413   // forbidding of prerender is required.
6414   for (nsIFrame* container =
6415            nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
6416        container;
6417        container = nsLayoutUtils::GetCrossDocParentFrameInProcess(container)) {
6418     const nsStyleSVGReset* svgReset = container->StyleSVGReset();
6419     if (svgReset->HasMask() || svgReset->HasClipPath()) {
6420       return result;
6421     }
6422   }
6423 
6424   // If the incoming dirty rect already contains the entire overflow area,
6425   // we are already rendering the entire content.
6426   nsRect overflow = aFrame->InkOverflowRectRelativeToSelf();
6427   // UntransformRect will not touch the output rect (`&untranformedDirtyRect`)
6428   // in cases of non-invertible transforms, so we set `untransformedRect` to
6429   // `aDirtyRect` as an initial value for such cases.
6430   nsRect untransformedDirtyRect = *aDirtyRect;
6431   UntransformRect(*aDirtyRect, overflow, aFrame, &untransformedDirtyRect);
6432   if (untransformedDirtyRect.Contains(overflow)) {
6433     *aDirtyRect = untransformedDirtyRect;
6434     result.mDecision = PrerenderDecision::Full;
6435     return result;
6436   }
6437 
6438   float viewportRatio =
6439       StaticPrefs::layout_animation_prerender_viewport_ratio_limit();
6440   uint32_t absoluteLimitX =
6441       StaticPrefs::layout_animation_prerender_absolute_limit_x();
6442   uint32_t absoluteLimitY =
6443       StaticPrefs::layout_animation_prerender_absolute_limit_y();
6444   nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
6445 
6446   float resolution = aFrame->PresShell()->GetCumulativeResolution();
6447   if (resolution < 1.0f) {
6448     refSize.SizeTo(
6449         NSCoordSaturatingNonnegativeMultiply(refSize.width, 1.0f / resolution),
6450         NSCoordSaturatingNonnegativeMultiply(refSize.height,
6451                                              1.0f / resolution));
6452   }
6453 
6454   // Only prerender if the transformed frame's size is <= a multiple of the
6455   // reference frame size (~viewport), and less than an absolute limit.
6456   // Both the ratio and the absolute limit are configurable.
6457   nscoord maxLength = std::max(nscoord(refSize.width * viewportRatio),
6458                                nscoord(refSize.height * viewportRatio));
6459   nsSize relativeLimit(maxLength, maxLength);
6460   nsSize absoluteLimit(
6461       aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
6462       aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
6463   nsSize maxSize = Min(relativeLimit, absoluteLimit);
6464 
6465   const auto transform = nsLayoutUtils::GetTransformToAncestor(
6466       RelativeTo{aFrame},
6467       RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});
6468   const gfxRect transformedBounds = transform.TransformAndClipBounds(
6469       gfxRect(overflow.x, overflow.y, overflow.width, overflow.height),
6470       gfxRect::MaxIntRect());
6471   const nsSize frameSize =
6472       nsSize(transformedBounds.width, transformedBounds.height);
6473 
6474   uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
6475   uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
6476   if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
6477     *aDirtyRect = overflow;
6478     result.mDecision = PrerenderDecision::Full;
6479     return result;
6480   }
6481 
6482   if (ShouldUsePartialPrerender(aFrame)) {
6483     *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(
6484         aFrame, untransformedDirtyRect, overflow, maxSize);
6485     result.mDecision = PrerenderDecision::Partial;
6486     return result;
6487   }
6488 
6489   if (frameArea > maxLimitArea) {
6490     uint64_t appUnitsPerPixel = AppUnitsPerCSSPixel();
6491     EffectCompositor::SetPerformanceWarning(
6492         aFrame, transformSet,
6493         AnimationPerformanceWarning(
6494             AnimationPerformanceWarning::Type::ContentTooLargeArea,
6495             {
6496                 int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
6497                 int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
6498             }));
6499   } else {
6500     EffectCompositor::SetPerformanceWarning(
6501         aFrame, transformSet,
6502         AnimationPerformanceWarning(
6503             AnimationPerformanceWarning::Type::ContentTooLarge,
6504             {
6505                 nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
6506                 nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
6507                 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
6508                 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
6509                 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
6510                 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
6511             }));
6512   }
6513 
6514   return result;
6515 }
6516 
6517 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
6518  * visible or hit. */
IsFrameVisible(nsIFrame * aFrame,const Matrix4x4 & aMatrix)6519 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix) {
6520   if (aMatrix.IsSingular()) {
6521     return false;
6522   }
6523   if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) {
6524     return false;
6525   }
6526   return true;
6527 }
6528 
GetTransform() const6529 const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const {
6530   if (mTransform) {
6531     return *mTransform;
6532   }
6533 
6534   float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6535 
6536   if (mHasTransformGetter) {
6537     mTransform.emplace((mFrame->GetTransformGetter())(mFrame, scale));
6538     Point3D newOrigin =
6539         Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
6540                 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f);
6541     mTransform->ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
6542   } else if (!mIsTransformSeparator) {
6543     DebugOnly<bool> isReference = mFrame->IsTransformed() ||
6544                                   mFrame->Combines3DTransformWithAncestors() ||
6545                                   mFrame->Extend3DContext();
6546     MOZ_ASSERT(isReference);
6547     mTransform.emplace(
6548         GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale,
6549                                     INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN));
6550   } else {
6551     // Use identity matrix
6552     mTransform.emplace();
6553   }
6554 
6555   return *mTransform;
6556 }
6557 
GetInverseTransform() const6558 const Matrix4x4Flagged& nsDisplayTransform::GetInverseTransform() const {
6559   if (mInverseTransform) {
6560     return *mInverseTransform;
6561   }
6562 
6563   MOZ_ASSERT(!GetTransform().IsSingular());
6564 
6565   mInverseTransform.emplace(GetTransform().Inverse());
6566 
6567   return *mInverseTransform;
6568 }
6569 
GetTransformForRendering(LayoutDevicePoint * aOutOrigin) const6570 Matrix4x4 nsDisplayTransform::GetTransformForRendering(
6571     LayoutDevicePoint* aOutOrigin) const {
6572   if (!mFrame->HasPerspective() || mHasTransformGetter ||
6573       mIsTransformSeparator) {
6574     if (!mHasTransformGetter && !mIsTransformSeparator && aOutOrigin) {
6575       // If aOutOrigin is provided, put the offset to origin into it, because
6576       // we need to keep it separate for webrender. The combination of
6577       // *aOutOrigin and the returned matrix here should always be equivalent
6578       // to what GetTransform() would have returned.
6579       float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6580       *aOutOrigin = LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale);
6581 
6582       // The rounding behavior should also be the same as GetTransform().
6583       if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6584         aOutOrigin->Round();
6585       }
6586       return GetResultingTransformMatrix(mFrame, nsPoint(0, 0), scale,
6587                                          INCLUDE_PERSPECTIVE);
6588     }
6589     return GetTransform().GetMatrix();
6590   }
6591   MOZ_ASSERT(!mHasTransformGetter);
6592 
6593   float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6594   // Don't include perspective transform, or the offset to origin, since
6595   // nsDisplayPerspective will handle both of those.
6596   return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
6597 }
6598 
GetAccumulatedPreserved3DTransform(nsDisplayListBuilder * aBuilder)6599 const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
6600     nsDisplayListBuilder* aBuilder) {
6601   MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
6602 
6603   if (!IsLeafOf3DContext()) {
6604     return GetTransform().GetMatrix();
6605   }
6606 
6607   if (!mTransformPreserves3D) {
6608     const nsIFrame* establisher;  // Establisher of the 3D rendering context.
6609     for (establisher = mFrame;
6610          establisher && establisher->Combines3DTransformWithAncestors();
6611          establisher =
6612              establisher->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
6613     }
6614     const nsIFrame* establisherReference = aBuilder->FindReferenceFrameFor(
6615         nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher));
6616 
6617     nsPoint offset = establisher->GetOffsetToCrossDoc(establisherReference);
6618     float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
6619     uint32_t flags =
6620         INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN;
6621     mTransformPreserves3D = MakeUnique<Matrix4x4>(
6622         GetResultingTransformMatrix(mFrame, offset, scale, flags));
6623   }
6624 
6625   return *mTransformPreserves3D;
6626 }
6627 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)6628 bool nsDisplayTransform::CreateWebRenderCommands(
6629     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
6630     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
6631     nsDisplayListBuilder* aDisplayListBuilder) {
6632   // We want to make sure we don't pollute the transform property in the WR
6633   // stacking context by including the position of this frame (relative to the
6634   // parent reference frame). We need to keep those separate; the position of
6635   // this frame goes into the stacking context bounds while the transform goes
6636   // into the transform.
6637   LayoutDevicePoint position;
6638   Matrix4x4 newTransformMatrix = GetTransformForRendering(&position);
6639 
6640   gfx::Matrix4x4* transformForSC = &newTransformMatrix;
6641   if (newTransformMatrix.IsIdentity()) {
6642     // If the transform is an identity transform, strip it out so that WR
6643     // doesn't turn this stacking context into a reference frame, as it
6644     // affects positioning. Bug 1345577 tracks a better fix.
6645     transformForSC = nullptr;
6646 
6647     // In ChooseScaleAndSetTransform, we round the offset from the reference
6648     // frame used to adjust the transform, if there is no transform, or it
6649     // is just a translation. We need to do the same here.
6650     if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) {
6651       position.Round();
6652     }
6653   }
6654 
6655   auto key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
6656                             wr::SpatialKeyKind::Transform);
6657 
6658   // We don't send animations for transform separator display items.
6659   uint64_t animationsId =
6660       mIsTransformSeparator
6661           ? 0
6662           : AddAnimationsForWebRender(
6663                 this, aManager, aDisplayListBuilder,
6664                 IsPartialPrerender() ? Some(position) : Nothing());
6665   wr::WrAnimationProperty prop{wr::WrAnimationType::Transform, animationsId,
6666                                key};
6667 
6668   nsDisplayTransform* deferredTransformItem = nullptr;
6669   if (!mFrame->ChildrenHavePerspective()) {
6670     // If it has perspective, we create a new scroll data via the
6671     // UpdateScrollData call because that scenario is more complex. Otherwise
6672     // we can just stash the transform on the StackingContextHelper and
6673     // apply it to any scroll data that are created inside this
6674     // nsDisplayTransform.
6675     deferredTransformItem = this;
6676   }
6677 
6678   // Determine if we're possibly animated (= would need an active layer in FLB).
6679   bool animated = !mIsTransformSeparator &&
6680                   ActiveLayerTracker::IsTransformMaybeAnimated(Frame());
6681 
6682   wr::StackingContextParams params;
6683   params.mBoundTransform = &newTransformMatrix;
6684   params.animation = animationsId ? &prop : nullptr;
6685 
6686   wr::WrTransformInfo transform_info;
6687   if (transformForSC) {
6688     transform_info.transform = wr::ToLayoutTransform(newTransformMatrix);
6689     transform_info.key = key;
6690     params.mTransformPtr = &transform_info;
6691   } else {
6692     params.mTransformPtr = nullptr;
6693   }
6694 
6695   params.prim_flags = !BackfaceIsHidden()
6696                           ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
6697                           : wr::PrimitiveFlags{0};
6698   params.paired_with_perspective = mHasAssociatedPerspective;
6699   params.mDeferredTransformItem = deferredTransformItem;
6700   params.mAnimated = animated;
6701   // Determine if we would have to rasterize any items in local raster space
6702   // (i.e. disable subpixel AA). We don't always need to rasterize locally even
6703   // if the stacking context is possibly animated (at the cost of potentially
6704   // some false negatives with respect to will-change handling), so we pass in
6705   // this determination separately to accurately match with when FLB would
6706   // normally disable subpixel AA.
6707   params.mRasterizeLocally = animated && Frame()->HasAnimationOfTransform();
6708   params.SetPreserve3D(mFrame->Extend3DContext() && !mIsTransformSeparator);
6709   params.clip =
6710       wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
6711 
6712   LayoutDeviceSize boundsSize = LayoutDeviceSize::FromAppUnits(
6713       mChildBounds.Size(), mFrame->PresContext()->AppUnitsPerDevPixel());
6714 
6715   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
6716                            params, LayoutDeviceRect(position, boundsSize));
6717 
6718   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6719       GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
6720   return true;
6721 }
6722 
UpdateScrollData(WebRenderScrollData * aData,WebRenderLayerScrollData * aLayerData)6723 bool nsDisplayTransform::UpdateScrollData(
6724     WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
6725   if (!mFrame->ChildrenHavePerspective()) {
6726     // This case is handled in CreateWebRenderCommands by stashing the transform
6727     // on the stacking context.
6728     return false;
6729   }
6730   if (aLayerData) {
6731     aLayerData->SetTransform(GetTransform().GetMatrix());
6732     aLayerData->SetTransformIsPerspective(true);
6733   }
6734   return true;
6735 }
6736 
ShouldSkipTransform(nsDisplayListBuilder * aBuilder) const6737 bool nsDisplayTransform::ShouldSkipTransform(
6738     nsDisplayListBuilder* aBuilder) const {
6739   return (aBuilder->RootReferenceFrame() == mFrame) &&
6740          aBuilder->IsForGenerateGlyphMask();
6741 }
6742 
Collect3DTransformLeaves(nsDisplayListBuilder * aBuilder,nsTArray<nsDisplayTransform * > & aLeaves)6743 void nsDisplayTransform::Collect3DTransformLeaves(
6744     nsDisplayListBuilder* aBuilder, nsTArray<nsDisplayTransform*>& aLeaves) {
6745   if (!IsParticipating3DContext() || IsLeafOf3DContext()) {
6746     aLeaves.AppendElement(this);
6747     return;
6748   }
6749 
6750   FlattenedDisplayListIterator iter(aBuilder, &mChildren);
6751   while (iter.HasNext()) {
6752     nsDisplayItem* item = iter.GetNextItem();
6753     if (item->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
6754       auto* perspective = static_cast<nsDisplayPerspective*>(item);
6755       if (!perspective->GetChildren()->GetTop()) {
6756         continue;
6757       }
6758       item = perspective->GetChildren()->GetTop();
6759     }
6760     if (item->GetType() != DisplayItemType::TYPE_TRANSFORM) {
6761       gfxCriticalError() << "Invalid child item within 3D transform of type: "
6762                          << item->Name();
6763       continue;
6764     }
6765     static_cast<nsDisplayTransform*>(item)->Collect3DTransformLeaves(aBuilder,
6766                                                                      aLeaves);
6767   }
6768 }
6769 
BuildPathFromPolygon(const RefPtr<DrawTarget> & aDT,const gfx::Polygon & aPolygon)6770 static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
6771                                               const gfx::Polygon& aPolygon) {
6772   MOZ_ASSERT(!aPolygon.IsEmpty());
6773 
6774   RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
6775   const nsTArray<Point4D>& points = aPolygon.GetPoints();
6776 
6777   pathBuilder->MoveTo(points[0].As2DPoint());
6778 
6779   for (size_t i = 1; i < points.Length(); ++i) {
6780     pathBuilder->LineTo(points[i].As2DPoint());
6781   }
6782 
6783   pathBuilder->Close();
6784   return pathBuilder->Finish();
6785 }
6786 
CollectSorted3DTransformLeaves(nsDisplayListBuilder * aBuilder,nsTArray<TransformPolygon> & aLeaves)6787 void nsDisplayTransform::CollectSorted3DTransformLeaves(
6788     nsDisplayListBuilder* aBuilder, nsTArray<TransformPolygon>& aLeaves) {
6789   std::list<TransformPolygon> inputLayers;
6790 
6791   nsTArray<nsDisplayTransform*> leaves;
6792   Collect3DTransformLeaves(aBuilder, leaves);
6793   for (nsDisplayTransform* item : leaves) {
6794     auto bounds = LayoutDeviceRect::FromAppUnits(
6795         item->mChildBounds, item->mFrame->PresContext()->AppUnitsPerDevPixel());
6796     Matrix4x4 transform = item->GetAccumulatedPreserved3DTransform(aBuilder);
6797 
6798     if (!IsFrameVisible(item->mFrame, transform)) {
6799       continue;
6800     }
6801     gfx::Polygon polygon =
6802         gfx::Polygon::FromRect(gfx::Rect(bounds.ToUnknownRect()));
6803 
6804     polygon.TransformToScreenSpace(transform);
6805 
6806     if (polygon.GetPoints().Length() >= 3) {
6807       inputLayers.push_back(TransformPolygon(item, std::move(polygon)));
6808     }
6809   }
6810 
6811   if (inputLayers.empty()) {
6812     return;
6813   }
6814 
6815   BSPTree<nsDisplayTransform> tree(inputLayers);
6816   nsTArray<TransformPolygon> orderedLayers(tree.GetDrawOrder());
6817 
6818   for (TransformPolygon& polygon : orderedLayers) {
6819     Matrix4x4 inverse =
6820         polygon.data->GetAccumulatedPreserved3DTransform(aBuilder).Inverse();
6821 
6822     MOZ_ASSERT(polygon.geometry);
6823     polygon.geometry->TransformToLayerSpace(inverse);
6824   }
6825 
6826   aLeaves = std::move(orderedLayers);
6827 }
6828 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)6829 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder,
6830                                gfxContext* aCtx) {
6831   Paint(aBuilder, aCtx, Nothing());
6832 }
6833 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,const Maybe<gfx::Polygon> & aPolygon)6834 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
6835                                const Maybe<gfx::Polygon>& aPolygon) {
6836   if (IsParticipating3DContext() && !IsLeafOf3DContext()) {
6837     MOZ_ASSERT(!aPolygon);
6838     nsTArray<TransformPolygon> leaves;
6839     CollectSorted3DTransformLeaves(aBuilder, leaves);
6840     for (TransformPolygon& item : leaves) {
6841       item.data->Paint(aBuilder, aCtx, item.geometry);
6842     }
6843     return;
6844   }
6845 
6846   gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
6847   Matrix4x4 trans = ShouldSkipTransform(aBuilder)
6848                         ? Matrix4x4()
6849                         : GetAccumulatedPreserved3DTransform(aBuilder);
6850   if (!IsFrameVisible(mFrame, trans)) {
6851     return;
6852   }
6853 
6854   Matrix trans2d;
6855   if (trans.CanDraw2D(&trans2d)) {
6856     aCtx->Multiply(ThebesMatrix(trans2d));
6857 
6858     if (aPolygon) {
6859       RefPtr<gfx::Path> path =
6860           BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6861       aCtx->GetDrawTarget()->PushClip(path);
6862     }
6863 
6864     GetChildren()->Paint(aBuilder, aCtx,
6865                          mFrame->PresContext()->AppUnitsPerDevPixel());
6866 
6867     if (aPolygon) {
6868       aCtx->GetDrawTarget()->PopClip();
6869     }
6870     return;
6871   }
6872 
6873   // TODO: Implement 3d transform handling, including plane splitting and
6874   // sorting. See BasicCompositor.
6875   auto pixelBounds = LayoutDeviceRect::FromAppUnitsToOutside(
6876       mChildBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
6877   RefPtr<DrawTarget> untransformedDT =
6878       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
6879           IntSize(pixelBounds.Width(), pixelBounds.Height()),
6880           SurfaceFormat::B8G8R8A8, true);
6881   if (!untransformedDT || !untransformedDT->IsValid()) {
6882     return;
6883   }
6884   untransformedDT->SetTransform(
6885       Matrix::Translation(-Point(pixelBounds.X(), pixelBounds.Y())));
6886 
6887   RefPtr<gfxContext> groupTarget =
6888       gfxContext::CreatePreservingTransformOrNull(untransformedDT);
6889 
6890   if (aPolygon) {
6891     RefPtr<gfx::Path> path =
6892         BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon);
6893     aCtx->GetDrawTarget()->PushClip(path);
6894   }
6895 
6896   GetChildren()->Paint(aBuilder, groupTarget,
6897                        mFrame->PresContext()->AppUnitsPerDevPixel());
6898 
6899   if (aPolygon) {
6900     aCtx->GetDrawTarget()->PopClip();
6901   }
6902 
6903   RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
6904 
6905   trans.PreTranslate(pixelBounds.X(), pixelBounds.Y(), 0);
6906   aCtx->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf, trans);
6907 }
6908 
MayBeAnimated(nsDisplayListBuilder * aBuilder) const6909 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
6910   // If EffectCompositor::HasAnimationsForCompositor() is true then we can
6911   // completely bypass the main thread for this animation, so it is always
6912   // worthwhile.
6913   // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is
6914   // already involved so there is less to be gained.
6915   // Therefore we check that the *post-transform* bounds of this item are
6916   // big enough to justify an active layer.
6917   return EffectCompositor::HasAnimationsForCompositor(
6918              mFrame, DisplayItemType::TYPE_TRANSFORM) ||
6919          (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame));
6920 }
6921 
TransformUntransformedBounds(nsDisplayListBuilder * aBuilder,const Matrix4x4Flagged & aMatrix) const6922 nsRect nsDisplayTransform::TransformUntransformedBounds(
6923     nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
6924   bool snap;
6925   const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
6926   // GetTransform always operates in dev pixels.
6927   const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
6928   return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
6929                                             factor);
6930 }
6931 
6932 /**
6933  * Returns the bounds for this transform. The bounds are calculated during
6934  * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
6935  */
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const6936 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
6937                                      bool* aSnap) const {
6938   *aSnap = false;
6939   return mBounds;
6940 }
6941 
ComputeBounds(nsDisplayListBuilder * aBuilder)6942 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
6943   MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
6944 
6945   /* Some transforms can get empty bounds in 2D, but might get transformed again
6946    * and get non-empty bounds. A simple example of this would be a 180 degree
6947    * rotation getting applied twice.
6948    * We should not depend on transforming bounds level by level.
6949    *
6950    * This function collects the bounds of this transform and stores it in
6951    * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
6952    * down and include the bounds of the child transforms.
6953    * The bounds are transformed with the accumulated transformation matrix up to
6954    * the 3D context root coordinate space.
6955    */
6956   nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
6957   accTransform.Accumulate(GetTransform().GetMatrix());
6958 
6959   // Do not dive into another 3D context.
6960   if (!IsLeafOf3DContext()) {
6961     for (nsDisplayItem* i : *GetChildren()) {
6962       i->DoUpdateBoundsPreserves3D(aBuilder);
6963     }
6964   }
6965 
6966   /* The child transforms that extend 3D context further will have empty bounds,
6967    * so the untransformed bounds here is the bounds of all the non-preserve-3d
6968    * content under this transform.
6969    */
6970   const nsRect rect = TransformUntransformedBounds(
6971       aBuilder, accTransform.GetCurrentTransform());
6972   aBuilder->AccumulateRect(rect);
6973 }
6974 
DoUpdateBoundsPreserves3D(nsDisplayListBuilder * aBuilder)6975 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
6976     nsDisplayListBuilder* aBuilder) {
6977   MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
6978              IsTransformSeparator());
6979   // Updating is not going through to child 3D context.
6980   ComputeBounds(aBuilder);
6981 }
6982 
UpdateBounds(nsDisplayListBuilder * aBuilder)6983 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
6984   UpdateUntransformedBounds(aBuilder);
6985 
6986   if (IsTransformSeparator()) {
6987     MOZ_ASSERT(GetTransform().IsIdentity());
6988     mBounds = mChildBounds;
6989     return;
6990   }
6991 
6992   if (mFrame->Extend3DContext()) {
6993     if (!Combines3DTransformWithAncestors()) {
6994       // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
6995       // collect the bounds from the child transforms.
6996       UpdateBoundsFor3D(aBuilder);
6997     } else {
6998       // With nested 3D transforms, the 2D bounds might not be useful.
6999       mBounds = nsRect();
7000     }
7001 
7002     return;
7003   }
7004 
7005   MOZ_ASSERT(!mFrame->Extend3DContext());
7006 
7007   // We would like to avoid calculating 2D bounds here for nested 3D transforms,
7008   // but mix-blend-mode relies on having bounds set. See bug 1556956.
7009 
7010   // A stand-alone transform.
7011   mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
7012 }
7013 
UpdateBoundsFor3D(nsDisplayListBuilder * aBuilder)7014 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
7015   MOZ_ASSERT(mFrame->Extend3DContext() &&
7016              !mFrame->Combines3DTransformWithAncestors() &&
7017              !IsTransformSeparator());
7018 
7019   // Always start updating from an establisher of a 3D rendering context.
7020   nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
7021   nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
7022   accTransform.StartRoot();
7023   ComputeBounds(aBuilder);
7024   mBounds = aBuilder->GetAccumulatedRect();
7025 }
7026 
UpdateUntransformedBounds(nsDisplayListBuilder * aBuilder)7027 void nsDisplayTransform::UpdateUntransformedBounds(
7028     nsDisplayListBuilder* aBuilder) {
7029   mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
7030       aBuilder, mActiveScrolledRoot);
7031 }
7032 
7033 #ifdef DEBUG_HIT
7034 #  include <time.h>
7035 #endif
7036 
7037 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)7038 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
7039                                  const nsRect& aRect, HitTestState* aState,
7040                                  nsTArray<nsIFrame*>* aOutFrames) {
7041   if (aState->mInPreserves3D) {
7042     GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames);
7043     return;
7044   }
7045 
7046   /* Here's how this works:
7047    * 1. Get the matrix.  If it's singular, abort (clearly we didn't hit
7048    *    anything).
7049    * 2. Invert the matrix.
7050    * 3. Use it to transform the rect into the correct space.
7051    * 4. Pass that rect down through to the list's version of HitTest.
7052    */
7053   // GetTransform always operates in dev pixels.
7054   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7055   Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7056 
7057   if (!IsFrameVisible(mFrame, matrix)) {
7058     return;
7059   }
7060 
7061   /* We want to go from transformed-space to regular space.
7062    * Thus we have to invert the matrix, which normally does
7063    * the reverse operation (e.g. regular->transformed)
7064    */
7065 
7066   /* Now, apply the transform and pass it down the channel. */
7067   matrix.Invert();
7068   nsRect resultingRect;
7069   if (aRect.width == 1 && aRect.height == 1) {
7070     // Magic width/height indicating we're hit testing a point, not a rect
7071     Point4D point =
7072         matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
7073                                   NSAppUnitsToFloatPixels(aRect.y, factor)));
7074     if (!point.HasPositiveWCoord()) {
7075       return;
7076     }
7077 
7078     Point point2d = point.As2DPoint();
7079 
7080     resultingRect =
7081         nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
7082                NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1);
7083 
7084   } else {
7085     Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
7086                       NSAppUnitsToFloatPixels(aRect.y, factor),
7087                       NSAppUnitsToFloatPixels(aRect.width, factor),
7088                       NSAppUnitsToFloatPixels(aRect.height, factor));
7089 
7090     bool snap;
7091     nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
7092     Rect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
7093                         NSAppUnitsToFloatPixels(childBounds.y, factor),
7094                         NSAppUnitsToFloatPixels(childBounds.width, factor),
7095                         NSAppUnitsToFloatPixels(childBounds.height, factor));
7096 
7097     Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
7098 
7099     resultingRect =
7100         nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
7101                NSFloatPixelsToAppUnits(float(rect.Y()), factor),
7102                NSFloatPixelsToAppUnits(float(rect.Width()), factor),
7103                NSFloatPixelsToAppUnits(float(rect.Height()), factor));
7104   }
7105 
7106   if (resultingRect.IsEmpty()) {
7107     return;
7108   }
7109 
7110 #ifdef DEBUG_HIT
7111   printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
7112   printf("  Untransformed point: (%f, %f)\n", resultingRect.X(),
7113          resultingRect.Y());
7114   uint32_t originalFrameCount = aOutFrames.Length();
7115 #endif
7116 
7117   GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames);
7118 
7119 #ifdef DEBUG_HIT
7120   if (originalFrameCount != aOutFrames.Length())
7121     printf("  Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
7122            dynamic_cast<void*>(aOutFrames.ElementAt(0)));
7123   printf("=== end of hit test ===\n");
7124 #endif
7125 }
7126 
GetHitDepthAtPoint(nsDisplayListBuilder * aBuilder,const nsPoint & aPoint)7127 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
7128                                              const nsPoint& aPoint) {
7129   // GetTransform always operates in dev pixels.
7130   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7131   Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
7132 
7133   NS_ASSERTION(IsFrameVisible(mFrame, matrix),
7134                "We can't have hit a frame that isn't visible!");
7135 
7136   Matrix4x4 inverse = matrix;
7137   inverse.Invert();
7138   Point4D point =
7139       inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
7140                                  NSAppUnitsToFloatPixels(aPoint.y, factor)));
7141 
7142   Point point2d = point.As2DPoint();
7143 
7144   Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
7145   return transformed.z;
7146 }
7147 
7148 /* The transform is opaque iff the transform consists solely of scales and
7149  * translations and if the underlying content is opaque.  Thus if the transform
7150  * is of the form
7151  *
7152  * |a c e|
7153  * |b d f|
7154  * |0 0 1|
7155  *
7156  * We need b and c to be zero.
7157  *
7158  * We also need to check whether the underlying opaque content completely fills
7159  * our visible rect. We use UntransformRect which expands to the axis-aligned
7160  * bounding rect, but that's OK since if
7161  * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
7162  * certainly contains the actual (non-axis-aligned) untransformed rect.
7163  */
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const7164 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7165                                              bool* aSnap) const {
7166   *aSnap = false;
7167 
7168   nsRect untransformedVisible;
7169   if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) {
7170     return nsRegion();
7171   }
7172 
7173   const Matrix4x4Flagged& matrix = GetTransform();
7174   Matrix matrix2d;
7175   if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) {
7176     return nsRegion();
7177   }
7178 
7179   nsRegion result;
7180 
7181   bool tmpSnap;
7182   const nsRect bounds = GetUntransformedBounds(aBuilder, &tmpSnap);
7183   const nsRegion opaque =
7184       ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), bounds);
7185 
7186   if (opaque.Contains(untransformedVisible)) {
7187     result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap));
7188   }
7189   return result;
7190 }
7191 
GetComponentAlphaBounds(nsDisplayListBuilder * aBuilder) const7192 nsRect nsDisplayTransform::GetComponentAlphaBounds(
7193     nsDisplayListBuilder* aBuilder) const {
7194   if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) {
7195     return nsRect();
7196   }
7197 
7198   bool snap;
7199   return GetBounds(aBuilder, &snap);
7200 }
7201 
7202 /* TransformRect takes in as parameters a rectangle (in app space) and returns
7203  * the smallest rectangle (in app space) containing the transformed image of
7204  * that rectangle.  That is, it takes the four corners of the rectangle,
7205  * transforms them according to the matrix associated with the specified frame,
7206  * then returns the smallest rectangle containing the four transformed points.
7207  *
7208  * @param aUntransformedBounds The rectangle (in app units) to transform.
7209  * @param aFrame The frame whose transformation should be applied.
7210  * @param aOrigin The delta from the frame origin to the coordinate space origin
7211  * @return The smallest rectangle containing the image of the transformed
7212  *         rectangle.
7213  */
TransformRect(const nsRect & aUntransformedBounds,const nsIFrame * aFrame,TransformReferenceBox & aRefBox)7214 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds,
7215                                          const nsIFrame* aFrame,
7216                                          TransformReferenceBox& aRefBox) {
7217   MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7218 
7219   float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7220 
7221   uint32_t flags =
7222       INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN | INCLUDE_PRESERVE3D_ANCESTORS;
7223   FrameTransformProperties props(aFrame, aRefBox, factor);
7224   return nsLayoutUtils::MatrixTransformRect(
7225       aUntransformedBounds,
7226       GetResultingTransformMatrixInternal(props, aRefBox, nsPoint(0, 0), factor,
7227                                           flags),
7228       factor);
7229 }
7230 
UntransformRect(const nsRect & aTransformedBounds,const nsRect & aChildBounds,const nsIFrame * aFrame,nsRect * aOutRect)7231 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
7232                                          const nsRect& aChildBounds,
7233                                          const nsIFrame* aFrame,
7234                                          nsRect* aOutRect) {
7235   MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!");
7236 
7237   float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
7238 
7239   uint32_t flags =
7240       INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN | INCLUDE_PRESERVE3D_ANCESTORS;
7241 
7242   Matrix4x4 transform =
7243       GetResultingTransformMatrix(aFrame, nsPoint(0, 0), factor, flags);
7244   if (transform.IsSingular()) {
7245     return false;
7246   }
7247 
7248   RectDouble result(NSAppUnitsToFloatPixels(aTransformedBounds.x, factor),
7249                     NSAppUnitsToFloatPixels(aTransformedBounds.y, factor),
7250                     NSAppUnitsToFloatPixels(aTransformedBounds.width, factor),
7251                     NSAppUnitsToFloatPixels(aTransformedBounds.height, factor));
7252 
7253   RectDouble childGfxBounds(
7254       NSAppUnitsToFloatPixels(aChildBounds.x, factor),
7255       NSAppUnitsToFloatPixels(aChildBounds.y, factor),
7256       NSAppUnitsToFloatPixels(aChildBounds.width, factor),
7257       NSAppUnitsToFloatPixels(aChildBounds.height, factor));
7258 
7259   result = transform.Inverse().ProjectRectBounds(result, childGfxBounds);
7260   *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
7261   return true;
7262 }
7263 
UntransformRect(nsDisplayListBuilder * aBuilder,const nsRect & aRect,nsRect * aOutRect) const7264 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder,
7265                                          const nsRect& aRect,
7266                                          nsRect* aOutRect) const {
7267   if (GetTransform().IsSingular()) {
7268     return false;
7269   }
7270 
7271   // GetTransform always operates in dev pixels.
7272   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
7273   RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor),
7274                     NSAppUnitsToFloatPixels(aRect.y, factor),
7275                     NSAppUnitsToFloatPixels(aRect.width, factor),
7276                     NSAppUnitsToFloatPixels(aRect.height, factor));
7277 
7278   bool snap;
7279   nsRect childBounds = GetUntransformedBounds(aBuilder, &snap);
7280   RectDouble childGfxBounds(
7281       NSAppUnitsToFloatPixels(childBounds.x, factor),
7282       NSAppUnitsToFloatPixels(childBounds.y, factor),
7283       NSAppUnitsToFloatPixels(childBounds.width, factor),
7284       NSAppUnitsToFloatPixels(childBounds.height, factor));
7285 
7286   /* We want to untransform the matrix, so invert the transformation first! */
7287   result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds);
7288 
7289   *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
7290 
7291   return true;
7292 }
7293 
WriteDebugInfo(std::stringstream & aStream)7294 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) {
7295   aStream << GetTransform().GetMatrix();
7296   if (IsTransformSeparator()) {
7297     aStream << " transform-separator";
7298   }
7299   if (IsLeafOf3DContext()) {
7300     aStream << " 3d-context-leaf";
7301   }
7302   if (mFrame->Extend3DContext()) {
7303     aStream << " extends-3d-context";
7304   }
7305   if (mFrame->Combines3DTransformWithAncestors()) {
7306     aStream << " combines-3d-with-ancestors";
7307   }
7308 
7309   aStream << " prerender(";
7310   switch (mPrerenderDecision) {
7311     case PrerenderDecision::No:
7312       aStream << "no";
7313       break;
7314     case PrerenderDecision::Partial:
7315       aStream << "partial";
7316       break;
7317     case PrerenderDecision::Full:
7318       aStream << "full";
7319       break;
7320   }
7321   aStream << ")";
7322   aStream << " childrenBuildingRect" << mChildrenBuildingRect;
7323 }
7324 
nsDisplayPerspective(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList)7325 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
7326                                            nsIFrame* aFrame,
7327                                            nsDisplayList* aList)
7328     : nsPaintedDisplayItem(aBuilder, aFrame), mList(aBuilder) {
7329   mList.AppendToTop(aList);
7330   MOZ_ASSERT(mList.Length() == 1);
7331   MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
7332 }
7333 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)7334 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder,
7335                                  gfxContext* aCtx) {
7336   // Just directly recurse into children, since we'll include the persepctive
7337   // value in any nsDisplayTransform children.
7338   GetChildren()->Paint(aBuilder, aCtx,
7339                        mFrame->PresContext()->AppUnitsPerDevPixel());
7340 }
7341 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const7342 nsRegion nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7343                                                bool* aSnap) const {
7344   if (!GetChildren()->GetTop()) {
7345     *aSnap = false;
7346     return nsRegion();
7347   }
7348 
7349   return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder, aSnap);
7350 }
7351 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)7352 bool nsDisplayPerspective::CreateWebRenderCommands(
7353     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7354     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7355     nsDisplayListBuilder* aDisplayListBuilder) {
7356   float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
7357   Matrix4x4 perspectiveMatrix;
7358   DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
7359       mFrame, appUnitsPerPixel, perspectiveMatrix);
7360   MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
7361 
7362   /*
7363    * ClipListToRange can remove our child after we were created.
7364    */
7365   if (!GetChildren()->GetTop()) {
7366     return false;
7367   }
7368 
7369   /*
7370    * The resulting matrix is still in the coordinate space of the transformed
7371    * frame. Append a translation to the reference frame coordinates.
7372    */
7373   nsDisplayTransform* transform =
7374       static_cast<nsDisplayTransform*>(GetChildren()->GetTop());
7375 
7376   Point3D newOrigin =
7377       Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
7378                                       appUnitsPerPixel),
7379               NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
7380                                       appUnitsPerPixel),
7381               0.0f);
7382   Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
7383 
7384   perspectiveMatrix.PostTranslate(roundedOrigin);
7385 
7386   nsIFrame* perspectiveFrame =
7387       mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame();
7388 
7389   // Passing true here is always correct, since perspective always combines
7390   // transforms with the descendants. However that'd make WR do a lot of work
7391   // that it doesn't really need to do if there aren't other transforms forming
7392   // part of the 3D context.
7393   //
7394   // WR knows how to treat perspective in that case, so the only thing we need
7395   // to do is to ensure we pass true when we're involved in a 3d context in any
7396   // other way via the transform-style property on either the transformed frame
7397   // or the perspective frame in order to not confuse WR's preserve-3d code in
7398   // very awful ways.
7399   bool preserve3D =
7400       mFrame->Extend3DContext() || perspectiveFrame->Extend3DContext();
7401 
7402   wr::StackingContextParams params;
7403 
7404   wr::WrTransformInfo transform_info;
7405   transform_info.transform = wr::ToLayoutTransform(perspectiveMatrix);
7406   transform_info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
7407                                       wr::SpatialKeyKind::Perspective);
7408   params.mTransformPtr = &transform_info;
7409 
7410   params.reference_frame_kind = wr::WrReferenceFrameKind::Perspective;
7411   params.prim_flags = !BackfaceIsHidden()
7412                           ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE
7413                           : wr::PrimitiveFlags{0};
7414   params.SetPreserve3D(preserve3D);
7415   params.clip =
7416       wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
7417 
7418   Maybe<uint64_t> scrollingRelativeTo;
7419   for (const auto* asr = GetActiveScrolledRoot(); asr; asr = asr->mParent) {
7420     // In OOP documents, the root scrollable frame of the in-process root
7421     // document is always active, so using IsAncestorFrameCrossDocInProcess
7422     // should be fine here.
7423     if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
7424             asr->mScrollableFrame->GetScrolledFrame(), perspectiveFrame)) {
7425       scrollingRelativeTo.emplace(asr->GetViewId());
7426       break;
7427     }
7428   }
7429 
7430   // We put the perspective reference frame wrapping the transformed frame,
7431   // even though there may be arbitrarily nested scroll frames in between.
7432   //
7433   // We need to know how many ancestor scroll-frames are we nested in, in order
7434   // for the async scrolling code in WebRender to calculate the right
7435   // transformation for the perspective contents.
7436   params.scrolling_relative_to = scrollingRelativeTo.ptrOr(nullptr);
7437 
7438   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
7439                            params);
7440 
7441   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
7442       GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources);
7443 
7444   return true;
7445 }
7446 
nsDisplayText(nsDisplayListBuilder * aBuilder,nsTextFrame * aFrame)7447 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
7448                              nsTextFrame* aFrame)
7449     : nsPaintedDisplayItem(aBuilder, aFrame),
7450       mVisIStartEdge(0),
7451       mVisIEndEdge(0) {
7452   MOZ_COUNT_CTOR(nsDisplayText);
7453   mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
7454   // Bug 748228
7455   mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
7456   mVisibleRect = aBuilder->GetVisibleRect() +
7457                  aBuilder->GetCurrentFrameOffsetToReferenceFrame();
7458 }
7459 
CanApplyOpacity(WebRenderLayerManager * aManager,nsDisplayListBuilder * aBuilder) const7460 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager,
7461                                     nsDisplayListBuilder* aBuilder) const {
7462   auto* f = static_cast<nsTextFrame*>(mFrame);
7463 
7464   if (f->IsSelected()) {
7465     return false;
7466   }
7467 
7468   const nsStyleText* textStyle = f->StyleText();
7469   if (textStyle->HasTextShadow()) {
7470     return false;
7471   }
7472 
7473   nsTextFrame::TextDecorations decorations;
7474   f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7475                         decorations);
7476   return !decorations.HasDecorationLines();
7477 }
7478 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)7479 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
7480   AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
7481   // We don't pass mVisibleRect here, since this can be called from within
7482   // the WebRender fallback painting path, and we don't want to issue
7483   // recorded commands that are dependent on the visible/building rect.
7484   RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx));
7485 }
7486 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)7487 bool nsDisplayText::CreateWebRenderCommands(
7488     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
7489     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
7490     nsDisplayListBuilder* aDisplayListBuilder) {
7491   auto* f = static_cast<nsTextFrame*>(mFrame);
7492   auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
7493 
7494   nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
7495   // Bug 748228
7496   bounds.Inflate(appUnitsPerDevPixel);
7497 
7498   if (bounds.IsEmpty()) {
7499     return true;
7500   }
7501 
7502   // For large font sizes, punt to a blob image, to avoid the blurry rendering
7503   // that results from WR clamping the glyph size used for rasterization.
7504   //
7505   // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.)
7506   //
7507   // This is not strictly accurate, as final used font sizes might not be the
7508   // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust
7509   // altering the used size of the font actually used).
7510   // It also fails to consider how transforms might affect the device-font-size
7511   // that webrender uses (and clamps).
7512   // But it should be near enough for practical purposes; the limitations just
7513   // mean we might sometimes end up with webrender still applying some bitmap
7514   // scaling, or bail out when we didn't really need to.
7515   constexpr float kWebRenderFontSizeLimit = 320.0;
7516   f->EnsureTextRun(nsTextFrame::eInflated);
7517   gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated);
7518   if (textRun &&
7519       textRun->GetFontGroup()->GetStyle()->size > kWebRenderFontSizeLimit) {
7520     return false;
7521   }
7522 
7523   gfx::Point deviceOffset =
7524       LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
7525           .ToUnknownPoint();
7526 
7527   // Clipping the bounds to the PaintRect (factoring in what's covered by parent
7528   // frames) let's us early reject a bunch of things, but it can produce
7529   // incorrect results for shadows, because they can translate things back into
7530   // view. Also if we're selected we might have some shadows from the
7531   // ::selected and ::inctive-selected pseudo-selectors. So don't do this
7532   // optimization if we have shadows or a selection.
7533   if (!(f->IsSelected() || f->StyleText()->HasTextShadow())) {
7534     nsRect visible = mVisibleRect;
7535     visible.Inflate(3 * appUnitsPerDevPixel);
7536     bounds = bounds.Intersect(visible);
7537   }
7538 
7539   RefPtr<gfxContext> textDrawer = aBuilder.GetTextContext(
7540       aResources, aSc, aManager, this, bounds, deviceOffset);
7541 
7542   aBuilder.StartGroup(this);
7543 
7544   RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect,
7545                   aBuilder.GetInheritedOpacity(), true);
7546   const bool result = textDrawer->GetTextDrawer()->Finish();
7547 
7548   if (result) {
7549     aBuilder.FinishGroup();
7550   } else {
7551     aBuilder.CancelGroup(true);
7552   }
7553 
7554   return result;
7555 }
7556 
RenderToContext(gfxContext * aCtx,nsDisplayListBuilder * aBuilder,const nsRect & aVisibleRect,float aOpacity,bool aIsRecording)7557 void nsDisplayText::RenderToContext(gfxContext* aCtx,
7558                                     nsDisplayListBuilder* aBuilder,
7559                                     const nsRect& aVisibleRect, float aOpacity,
7560                                     bool aIsRecording) {
7561   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7562 
7563   // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
7564   // antialiased pixels beyond the measured text extents.
7565   // This is temporary until we do this in the actual calculation of text
7566   // extents.
7567   auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
7568   LayoutDeviceRect extraVisible =
7569       LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D);
7570   extraVisible.Inflate(1);
7571 
7572   gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width,
7573                        extraVisible.height);
7574   pixelVisible.Inflate(2);
7575   pixelVisible.RoundOut();
7576 
7577   bool willClip = !aBuilder->IsForGenerateGlyphMask() && !aIsRecording;
7578   if (willClip) {
7579     aCtx->Clip(pixelVisible);
7580   }
7581 
7582   NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
7583   NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
7584 
7585   gfxContextMatrixAutoSaveRestore matrixSR;
7586 
7587   nsPoint framePt = ToReferenceFrame();
7588   if (f->Style()->IsTextCombined()) {
7589     float scaleFactor = nsTextFrame::GetTextCombineScaleFactor(f);
7590     if (scaleFactor != 1.0f) {
7591       if (auto* textDrawer = aCtx->GetTextDrawer()) {
7592         // WebRender doesn't support scaling text like this yet
7593         textDrawer->FoundUnsupportedFeature();
7594         return;
7595       }
7596       matrixSR.SetContext(aCtx);
7597       // Setup matrix to compress text for text-combine-upright if
7598       // necessary. This is done here because we want selection be
7599       // compressed at the same time as text.
7600       gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
7601       gfxMatrix mat = aCtx->CurrentMatrixDouble()
7602                           .PreTranslate(pt)
7603                           .PreScale(scaleFactor, 1.0)
7604                           .PreTranslate(-pt);
7605       aCtx->SetMatrixDouble(mat);
7606     }
7607   }
7608   nsTextFrame::PaintTextParams params(aCtx);
7609   params.framePt = gfx::Point(framePt.x, framePt.y);
7610   params.dirtyRect = extraVisible;
7611 
7612   if (aBuilder->IsForGenerateGlyphMask()) {
7613     params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
7614   } else {
7615     params.state = nsTextFrame::PaintTextParams::PaintText;
7616   }
7617 
7618   f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
7619                f->IsSelected(), aOpacity);
7620 
7621   if (willClip) {
7622     aCtx->PopClip();
7623   }
7624 }
7625 
7626 // This could go to nsDisplayListInvalidation.h, but
7627 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which
7628 // would produce circular dependencies.
7629 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
7630  public:
nsDisplayTextGeometry(nsDisplayText * aItem,nsDisplayListBuilder * aBuilder)7631   nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
7632       : nsDisplayItemGenericGeometry(aItem, aBuilder),
7633         mVisIStartEdge(aItem->VisIStartEdge()),
7634         mVisIEndEdge(aItem->VisIEndEdge()) {
7635     nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
7636     f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7637                           mDecorations);
7638   }
7639 
7640   /**
7641    * We store the computed text decorations here since they are
7642    * computed using style data from parent frames. Any changes to these
7643    * styles will only invalidate the parent frame and not this frame.
7644    */
7645   nsTextFrame::TextDecorations mDecorations;
7646   nscoord mVisIStartEdge;
7647   nscoord mVisIEndEdge;
7648 };
7649 
AllocateGeometry(nsDisplayListBuilder * aBuilder)7650 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry(
7651     nsDisplayListBuilder* aBuilder) {
7652   return new nsDisplayTextGeometry(this, aBuilder);
7653 }
7654 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const7655 void nsDisplayText::ComputeInvalidationRegion(
7656     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7657     nsRegion* aInvalidRegion) const {
7658   const nsDisplayTextGeometry* geometry =
7659       static_cast<const nsDisplayTextGeometry*>(aGeometry);
7660   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7661 
7662   nsTextFrame::TextDecorations decorations;
7663   f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors,
7664                         decorations);
7665 
7666   bool snap;
7667   const nsRect& newRect = geometry->mBounds;
7668   nsRect oldRect = GetBounds(aBuilder, &snap);
7669   if (decorations != geometry->mDecorations ||
7670       mVisIStartEdge != geometry->mVisIStartEdge ||
7671       mVisIEndEdge != geometry->mVisIEndEdge ||
7672       !oldRect.IsEqualInterior(newRect) ||
7673       !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
7674     aInvalidRegion->Or(oldRect, newRect);
7675   }
7676 }
7677 
WriteDebugInfo(std::stringstream & aStream)7678 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) {
7679 #ifdef DEBUG
7680   aStream << " (\"";
7681 
7682   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
7683   nsCString buf;
7684   f->ToCString(buf);
7685 
7686   aStream << buf.get() << "\")";
7687 #endif
7688 }
7689 
nsDisplayEffectsBase(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,bool aClearClipChain)7690 nsDisplayEffectsBase::nsDisplayEffectsBase(
7691     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7692     const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
7693     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
7694                         aClearClipChain) {
7695   MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7696 }
7697 
nsDisplayEffectsBase(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList)7698 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder,
7699                                            nsIFrame* aFrame,
7700                                            nsDisplayList* aList)
7701     : nsDisplayWrapList(aBuilder, aFrame, aList) {
7702   MOZ_COUNT_CTOR(nsDisplayEffectsBase);
7703 }
7704 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const7705 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
7706                                                bool* aSnap) const {
7707   *aSnap = false;
7708   return nsRegion();
7709 }
7710 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)7711 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder* aBuilder,
7712                                    const nsRect& aRect, HitTestState* aState,
7713                                    nsTArray<nsIFrame*>* aOutFrames) {
7714   nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
7715   if (SVGIntegrationUtils::HitTestFrameForEffects(
7716           mFrame, rectCenter - ToReferenceFrame())) {
7717     mList.HitTest(aBuilder, aRect, aState, aOutFrames);
7718   }
7719 }
7720 
BBoxInUserSpace() const7721 gfxRect nsDisplayEffectsBase::BBoxInUserSpace() const {
7722   return SVGUtils::GetBBox(mFrame);
7723 }
7724 
UserSpaceOffset() const7725 gfxPoint nsDisplayEffectsBase::UserSpaceOffset() const {
7726   return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
7727 }
7728 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const7729 void nsDisplayEffectsBase::ComputeInvalidationRegion(
7730     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7731     nsRegion* aInvalidRegion) const {
7732   const auto* geometry =
7733       static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
7734   bool snap;
7735   nsRect bounds = GetBounds(aBuilder, &snap);
7736   if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
7737       geometry->mUserSpaceOffset != UserSpaceOffset() ||
7738       !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
7739     // Filter and mask output can depend on the location of the frame's user
7740     // space and on the frame's BBox. We need to invalidate if either of these
7741     // change relative to the reference frame.
7742     // Invalidations from our inactive layer manager are not enough to catch
7743     // some of these cases because filters can produce output even if there's
7744     // nothing in the filter input.
7745     aInvalidRegion->Or(bounds, geometry->mBounds);
7746   }
7747 }
7748 
ValidateSVGFrame()7749 bool nsDisplayEffectsBase::ValidateSVGFrame() {
7750   const nsIContent* content = mFrame->GetContent();
7751   bool hasSVGLayout = mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT);
7752   if (hasSVGLayout) {
7753     ISVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame);
7754     if (!svgFrame || !mFrame->GetContent()->IsSVGElement()) {
7755       NS_ASSERTION(false, "why?");
7756       return false;
7757     }
7758     if (!static_cast<const SVGElement*>(content)->HasValidDimensions()) {
7759       return false;  // The SVG spec says not to draw filters for this
7760     }
7761   }
7762 
7763   return true;
7764 }
7765 
7766 using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams;
7767 
ComputeMaskGeometry(PaintFramesParams & aParams)7768 static void ComputeMaskGeometry(PaintFramesParams& aParams) {
7769   // Properties are added lazily and may have been removed by a restyle, so
7770   // make sure all applicable ones are set again.
7771   nsIFrame* firstFrame =
7772       nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
7773 
7774   const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
7775 
7776   nsTArray<SVGMaskFrame*> maskFrames;
7777   // XXX check return value?
7778   SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
7779 
7780   if (maskFrames.Length() == 0) {
7781     return;
7782   }
7783 
7784   gfxContext& ctx = aParams.ctx;
7785   nsIFrame* frame = aParams.frame;
7786 
7787   nsPoint offsetToUserSpace =
7788       nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame);
7789 
7790   auto cssToDevScale = frame->PresContext()->CSSToDevPixelScale();
7791   int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
7792 
7793   gfxPoint devPixelOffsetToUserSpace =
7794       nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, appUnitsPerDevPixel);
7795 
7796   gfxContextMatrixAutoSaveRestore matSR(&ctx);
7797   ctx.SetMatrixDouble(
7798       ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace));
7799 
7800   // Convert boaderArea and dirtyRect to user space.
7801   nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
7802   nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
7803 
7804   // Union all mask layer rectangles in user space.
7805   LayoutDeviceRect maskInUserSpace;
7806   for (size_t i = 0; i < maskFrames.Length(); i++) {
7807     SVGMaskFrame* maskFrame = maskFrames[i];
7808     LayoutDeviceRect currentMaskSurfaceRect;
7809 
7810     if (maskFrame) {
7811       auto rect = maskFrame->GetMaskArea(aParams.frame);
7812       currentMaskSurfaceRect =
7813           CSSRect::FromUnknownRect(ToRect(rect)) * cssToDevScale;
7814     } else {
7815       nsCSSRendering::ImageLayerClipState clipState;
7816       nsCSSRendering::GetImageLayerClip(
7817           svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(),
7818           userSpaceBorderArea, userSpaceDirtyRect, false, /* aWillPaintBorder */
7819           appUnitsPerDevPixel, &clipState);
7820       currentMaskSurfaceRect = LayoutDeviceRect::FromUnknownRect(
7821           ToRect(clipState.mDirtyRectInDevPx));
7822     }
7823 
7824     maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
7825   }
7826 
7827   if (!maskInUserSpace.IsEmpty()) {
7828     aParams.maskRect = Some(maskInUserSpace);
7829   } else {
7830     aParams.maskRect = Nothing();
7831   }
7832 }
7833 
nsDisplayMasksAndClipPaths(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot)7834 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths(
7835     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7836     const ActiveScrolledRoot* aActiveScrolledRoot)
7837     : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, true) {
7838   MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths);
7839 
7840   nsPresContext* presContext = mFrame->PresContext();
7841   uint32_t flags =
7842       aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
7843   const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
7844   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7845     const auto& layer = svgReset->mMask.mLayers[i];
7846     if (!layer.mImage.IsResolved()) {
7847       continue;
7848     }
7849     const nsRect& borderArea = mFrame->GetRectRelativeToSelf();
7850     // NOTE(emilio): We only care about the dest rect so we don't bother
7851     // computing a clip.
7852     bool isTransformedFixed = false;
7853     nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
7854         presContext, aFrame, flags, borderArea, borderArea, layer,
7855         &isTransformedFixed);
7856     mDestRects.AppendElement(state.mDestArea);
7857   }
7858 }
7859 
CanMergeDisplayMaskFrame(nsIFrame * aFrame)7860 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) {
7861   // Do not merge items for box-decoration-break:clone elements,
7862   // since each box should have its own mask in that case.
7863   if (aFrame->StyleBorder()->mBoxDecorationBreak ==
7864       StyleBoxDecorationBreak::Clone) {
7865     return false;
7866   }
7867 
7868   // Do not merge if either frame has a mask. Continuation frames should apply
7869   // the mask independently (just like nsDisplayBackgroundImage).
7870   if (aFrame->StyleSVGReset()->HasMask()) {
7871     return false;
7872   }
7873 
7874   return true;
7875 }
7876 
CanMerge(const nsDisplayItem * aItem) const7877 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem* aItem) const {
7878   // Items for the same content element should be merged into a single
7879   // compositing group.
7880   if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) ||
7881       !HasSameContent(aItem)) {
7882     return false;
7883   }
7884 
7885   return CanMergeDisplayMaskFrame(mFrame) &&
7886          CanMergeDisplayMaskFrame(aItem->Frame());
7887 }
7888 
IsValidMask()7889 bool nsDisplayMasksAndClipPaths::IsValidMask() {
7890   if (!ValidateSVGFrame()) {
7891     return false;
7892   }
7893 
7894   nsIFrame* firstFrame =
7895       nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
7896 
7897   return !(SVGObserverUtils::GetAndObserveClipPath(firstFrame, nullptr) ==
7898                SVGObserverUtils::eHasRefsSomeInvalid ||
7899            SVGObserverUtils::GetAndObserveMasks(firstFrame, nullptr) ==
7900                SVGObserverUtils::eHasRefsSomeInvalid);
7901 }
7902 
PaintMask(nsDisplayListBuilder * aBuilder,gfxContext * aMaskContext,bool aHandleOpacity,bool * aMaskPainted)7903 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder,
7904                                            gfxContext* aMaskContext,
7905                                            bool aHandleOpacity,
7906                                            bool* aMaskPainted) {
7907   MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
7908 
7909   imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7910   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7911   SVGIntegrationUtils::PaintFramesParams params(*aMaskContext, mFrame, mBounds,
7912                                                 borderArea, aBuilder,
7913                                                 aHandleOpacity, imgParams);
7914   ComputeMaskGeometry(params);
7915   bool maskIsComplete = false;
7916   bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete);
7917   if (aMaskPainted) {
7918     *aMaskPainted = painted;
7919   }
7920 
7921   nsDisplayMasksAndClipPathsGeometry::UpdateDrawResult(this, imgParams.result);
7922 
7923   return maskIsComplete &&
7924          (imgParams.result == ImgDrawResult::SUCCESS ||
7925           imgParams.result == ImgDrawResult::SUCCESS_NOT_COMPLETE ||
7926           imgParams.result == ImgDrawResult::WRONG_SIZE);
7927 }
7928 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const7929 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion(
7930     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
7931     nsRegion* aInvalidRegion) const {
7932   nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
7933                                                   aInvalidRegion);
7934 
7935   const auto* geometry =
7936       static_cast<const nsDisplayMasksAndClipPathsGeometry*>(aGeometry);
7937   bool snap;
7938   nsRect bounds = GetBounds(aBuilder, &snap);
7939 
7940   if (mDestRects.Length() != geometry->mDestRects.Length()) {
7941     aInvalidRegion->Or(bounds, geometry->mBounds);
7942   } else {
7943     for (size_t i = 0; i < mDestRects.Length(); i++) {
7944       if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) {
7945         aInvalidRegion->Or(bounds, geometry->mBounds);
7946         break;
7947       }
7948     }
7949   }
7950 
7951   if (aBuilder->ShouldSyncDecodeImages() &&
7952       geometry->ShouldInvalidateToSyncDecodeImages()) {
7953     const nsStyleSVGReset* svgReset = mFrame->StyleSVGReset();
7954     NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
7955       const auto& image = svgReset->mMask.mLayers[i].mImage;
7956       if (image.IsImageRequestType()) {
7957         aInvalidRegion->Or(*aInvalidRegion, bounds);
7958         break;
7959       }
7960     }
7961   }
7962 }
7963 
PaintWithContentsPaintCallback(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,const std::function<void ()> & aPaintChildren)7964 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback(
7965     nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
7966     const std::function<void()>& aPaintChildren) {
7967   // Clip the drawing target by mVisibleRect, which contains the visible
7968   // region of the target frame and its out-of-flow and inflow descendants.
7969   gfxContext* context = aCtx;
7970 
7971   Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx),
7972                              mFrame->PresContext()->AppUnitsPerDevPixel());
7973   bounds.RoundOut();
7974   context->Clip(bounds);
7975 
7976   imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
7977   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
7978   SVGIntegrationUtils::PaintFramesParams params(
7979       *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), borderArea, aBuilder, false,
7980       imgParams);
7981 
7982   ComputeMaskGeometry(params);
7983 
7984   SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren);
7985 
7986   context->PopClip();
7987 
7988   nsDisplayMasksAndClipPathsGeometry::UpdateDrawResult(this, imgParams.result);
7989 }
7990 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)7991 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder* aBuilder,
7992                                        gfxContext* aCtx) {
7993   if (!IsValidMask()) {
7994     return;
7995   }
7996   PaintWithContentsPaintCallback(aBuilder, aCtx, [&] {
7997     GetChildren()->Paint(aBuilder, aCtx,
7998                          mFrame->PresContext()->AppUnitsPerDevPixel());
7999   });
8000 }
8001 
CreateSimpleClipRegion(const nsDisplayMasksAndClipPaths & aDisplayItem,wr::DisplayListBuilder & aBuilder)8002 static Maybe<wr::WrClipId> CreateSimpleClipRegion(
8003     const nsDisplayMasksAndClipPaths& aDisplayItem,
8004     wr::DisplayListBuilder& aBuilder) {
8005   nsIFrame* frame = aDisplayItem.Frame();
8006   const auto* style = frame->StyleSVGReset();
8007   MOZ_ASSERT(style->HasClipPath() || style->HasMask());
8008   if (!SVGIntegrationUtils::UsingSimpleClipPathForFrame(frame)) {
8009     return Nothing();
8010   }
8011 
8012   const auto& clipPath = style->mClipPath;
8013   const auto& shape = *clipPath.AsShape()._0;
8014 
8015   auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8016   const nsRect refBox =
8017       nsLayoutUtils::ComputeGeometryBox(frame, clipPath.AsShape()._1);
8018 
8019   wr::WrClipId clipId{};
8020 
8021   switch (shape.tag) {
8022     case StyleBasicShape::Tag::Inset: {
8023       const nsRect insetRect = ShapeUtils::ComputeInsetRect(shape, refBox) +
8024                                aDisplayItem.ToReferenceFrame();
8025 
8026       nscoord radii[8] = {0};
8027 
8028       if (ShapeUtils::ComputeInsetRadii(shape, refBox, radii)) {
8029         clipId = aBuilder.DefineRoundedRectClip(
8030             Nothing(),
8031             wr::ToComplexClipRegion(insetRect, radii, appUnitsPerDevPixel));
8032       } else {
8033         clipId = aBuilder.DefineRectClip(
8034             Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
8035                            insetRect, appUnitsPerDevPixel)));
8036       }
8037 
8038       break;
8039     }
8040     case StyleBasicShape::Tag::Ellipse:
8041     case StyleBasicShape::Tag::Circle: {
8042       nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox);
8043 
8044       nsSize radii;
8045       if (shape.IsEllipse()) {
8046         radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox);
8047       } else {
8048         nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox);
8049         radii = {radius, radius};
8050       }
8051 
8052       nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center -
8053                              nsPoint(radii.width, radii.height),
8054                          radii * 2);
8055 
8056       nscoord ellipseRadii[8];
8057       for (const auto corner : AllPhysicalHalfCorners()) {
8058         ellipseRadii[corner] =
8059             HalfCornerIsX(corner) ? radii.width : radii.height;
8060       }
8061 
8062       clipId = aBuilder.DefineRoundedRectClip(
8063           Nothing(), wr::ToComplexClipRegion(ellipseRect, ellipseRadii,
8064                                              appUnitsPerDevPixel));
8065 
8066       break;
8067     }
8068     default:
8069       // Please don't add more exceptions, try to find a way to define the clip
8070       // without using a mask image.
8071       //
8072       // And if you _really really_ need to add an exception, add it to
8073       // SVGIntegrationUtils::UsingSimpleClipPathForFrame
8074       MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
8075       return Nothing();
8076   }
8077 
8078   return Some(clipId);
8079 }
8080 
FillPolygonDataForDisplayItem(const nsDisplayMasksAndClipPaths & aDisplayItem,nsTArray<wr::LayoutPoint> & aPoints,wr::FillRule & aFillRule)8081 static void FillPolygonDataForDisplayItem(
8082     const nsDisplayMasksAndClipPaths& aDisplayItem,
8083     nsTArray<wr::LayoutPoint>& aPoints, wr::FillRule& aFillRule) {
8084   nsIFrame* frame = aDisplayItem.Frame();
8085   const auto* style = frame->StyleSVGReset();
8086   bool isPolygon = style->HasClipPath() && style->mClipPath.IsShape() &&
8087                    style->mClipPath.AsShape()._0->IsPolygon();
8088   if (!isPolygon) {
8089     return;
8090   }
8091 
8092   const auto& clipPath = style->mClipPath;
8093   const auto& shape = *clipPath.AsShape()._0;
8094   const nsRect refBox =
8095       nsLayoutUtils::ComputeGeometryBox(frame, clipPath.AsShape()._1);
8096 
8097   // We only fill polygon data for polygons that are below a complexity
8098   // limit.
8099   nsTArray<nsPoint> vertices =
8100       ShapeUtils::ComputePolygonVertices(shape, refBox);
8101   if (vertices.Length() > wr::POLYGON_CLIP_VERTEX_MAX) {
8102     return;
8103   }
8104 
8105   auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8106 
8107   for (size_t i = 0; i < vertices.Length(); ++i) {
8108     wr::LayoutPoint point = wr::ToLayoutPoint(
8109         LayoutDevicePoint::FromAppUnits(vertices[i], appUnitsPerDevPixel));
8110     aPoints.AppendElement(point);
8111   }
8112 
8113   aFillRule = (shape.AsPolygon().fill == StyleFillRule::Nonzero)
8114                   ? wr::FillRule::Nonzero
8115                   : wr::FillRule::Evenodd;
8116 }
8117 
CreateWRClipPathAndMasks(nsDisplayMasksAndClipPaths * aDisplayItem,const LayoutDeviceRect & aBounds,wr::IpcResourceUpdateQueue & aResources,wr::DisplayListBuilder & aBuilder,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)8118 static Maybe<wr::WrClipId> CreateWRClipPathAndMasks(
8119     nsDisplayMasksAndClipPaths* aDisplayItem, const LayoutDeviceRect& aBounds,
8120     wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder,
8121     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8122     nsDisplayListBuilder* aDisplayListBuilder) {
8123   if (auto clip = CreateSimpleClipRegion(*aDisplayItem, aBuilder)) {
8124     return clip;
8125   }
8126 
8127   Maybe<wr::ImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(
8128       aDisplayItem, aBuilder, aResources, aSc, aDisplayListBuilder, aBounds);
8129   if (!mask) {
8130     return Nothing();
8131   }
8132 
8133   // We couldn't create a simple clip region, but before we create an image
8134   // mask clip, see if we can get a polygon clip to add to it.
8135   nsTArray<wr::LayoutPoint> points;
8136   wr::FillRule fillRule = wr::FillRule::Nonzero;
8137   FillPolygonDataForDisplayItem(*aDisplayItem, points, fillRule);
8138 
8139   wr::WrClipId clipId =
8140       aBuilder.DefineImageMaskClip(mask.ref(), points, fillRule);
8141 
8142   return Some(clipId);
8143 }
8144 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)8145 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands(
8146     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8147     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8148     nsDisplayListBuilder* aDisplayListBuilder) {
8149   bool snap;
8150   auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8151   nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
8152   LayoutDeviceRect bounds =
8153       LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel);
8154 
8155   Maybe<wr::WrClipId> clip = CreateWRClipPathAndMasks(
8156       this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder);
8157 
8158   float oldOpacity = aBuilder.GetInheritedOpacity();
8159 
8160   Maybe<StackingContextHelper> layer;
8161   const StackingContextHelper* sc = &aSc;
8162   if (clip) {
8163     // Create a new stacking context to attach the mask to, ensuring the mask is
8164     // applied to the aggregate, and not the individual elements.
8165 
8166     // The stacking context shouldn't have any offset.
8167     bounds.MoveTo(0, 0);
8168 
8169     Maybe<float> opacity =
8170         (SVGIntegrationUtils::UsingSimpleClipPathForFrame(mFrame) &&
8171          aBuilder.GetInheritedOpacity() != 1.0f)
8172             ? Some(aBuilder.GetInheritedOpacity())
8173             : Nothing();
8174 
8175     wr::StackingContextParams params;
8176     params.clip = wr::WrStackingContextClip::ClipId(*clip);
8177     params.opacity = opacity.ptrOr(nullptr);
8178     layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params,
8179                   bounds);
8180     sc = layer.ptr();
8181   }
8182 
8183   aBuilder.SetInheritedOpacity(1.0f);
8184   const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8185   aBuilder.SetInheritedClipChain(nullptr);
8186   CreateWebRenderCommandsNewClipListOption(aBuilder, aResources, *sc, aManager,
8187                                            aDisplayListBuilder, layer.isSome());
8188   aBuilder.SetInheritedOpacity(oldOpacity);
8189   aBuilder.SetInheritedClipChain(oldClipChain);
8190 
8191   return true;
8192 }
8193 
GetClipWithRespectToASR(nsDisplayListBuilder * aBuilder,const ActiveScrolledRoot * aASR) const8194 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR(
8195     nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
8196   if (const DisplayItemClip* clip =
8197           DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
8198     return Some(clip->GetClipRect());
8199   }
8200   // This item does not have a clip with respect to |aASR|. However, we
8201   // might still have finite bounds with respect to |aASR|. Check our
8202   // children.
8203   nsDisplayList* childList = GetSameCoordinateSystemChildren();
8204   if (childList) {
8205     return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR));
8206   }
8207 #ifdef DEBUG
8208   MOZ_ASSERT(false, "item should have finite clip with respect to aASR");
8209 #endif
8210   return Nothing();
8211 }
8212 
8213 #ifdef MOZ_DUMP_PAINTING
PrintEffects(nsACString & aTo)8214 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) {
8215   nsIFrame* firstFrame =
8216       nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8217   bool first = true;
8218   aTo += " effects=(";
8219   SVGClipPathFrame* clipPathFrame;
8220   // XXX Check return value?
8221   SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
8222   if (clipPathFrame) {
8223     if (!first) {
8224       aTo += ", ";
8225     }
8226     aTo += nsPrintfCString(
8227         "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
8228     first = false;
8229   } else if (mFrame->StyleSVGReset()->HasClipPath()) {
8230     if (!first) {
8231       aTo += ", ";
8232     }
8233     aTo += "clip(basic-shape)";
8234     first = false;
8235   }
8236 
8237   nsTArray<SVGMaskFrame*> masks;
8238   // XXX check return value?
8239   SVGObserverUtils::GetAndObserveMasks(firstFrame, &masks);
8240   if (!masks.IsEmpty() && masks[0]) {
8241     if (!first) {
8242       aTo += ", ";
8243     }
8244     aTo += "mask";
8245   }
8246   aTo += ")";
8247 }
8248 #endif
8249 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)8250 void nsDisplayBackdropRootContainer::Paint(nsDisplayListBuilder* aBuilder,
8251                                            gfxContext* aCtx) {
8252   aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix());
8253   GetChildren()->Paint(aBuilder, aCtx,
8254                        mFrame->PresContext()->AppUnitsPerDevPixel());
8255   aCtx->GetDrawTarget()->PopLayer();
8256 }
8257 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)8258 bool nsDisplayBackdropRootContainer::CreateWebRenderCommands(
8259     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8260     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8261     nsDisplayListBuilder* aDisplayListBuilder) {
8262   wr::StackingContextParams params;
8263   params.flags |= wr::StackingContextFlags::IS_BACKDROP_ROOT;
8264   params.clip =
8265       wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8266   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8267                            params);
8268 
8269   nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8270                                              aDisplayListBuilder);
8271   return true;
8272 }
8273 
8274 /* static */
CanCreateWebRenderCommands(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)8275 bool nsDisplayBackdropFilters::CanCreateWebRenderCommands(
8276     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
8277   return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(aFrame);
8278 }
8279 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)8280 bool nsDisplayBackdropFilters::CreateWebRenderCommands(
8281     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8282     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8283     nsDisplayListBuilder* aDisplayListBuilder) {
8284   WrFiltersHolder wrFilters;
8285   Maybe<nsRect> filterClip;
8286   auto filterChain = mFrame->StyleEffects()->mBackdropFilters.AsSpan();
8287   bool initialized = true;
8288   if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8289                                                       wrFilters) &&
8290       !SVGIntegrationUtils::BuildWebRenderFilters(
8291           mFrame, filterChain, wrFilters, filterClip, initialized)) {
8292     return false;
8293   }
8294 
8295   if (!initialized) {
8296     // draw nothing
8297     return true;
8298   }
8299 
8300   nsCSSRendering::ImageLayerClipState clip;
8301   nsCSSRendering::GetImageLayerClip(
8302       mFrame->StyleBackground()->BottomLayer(), mFrame, *mFrame->StyleBorder(),
8303       mBackdropRect, mBackdropRect, false,
8304       mFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
8305 
8306   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
8307       mBackdropRect, mFrame->PresContext()->AppUnitsPerDevPixel());
8308 
8309   wr::ComplexClipRegion region =
8310       wr::ToComplexClipRegion(clip.mBGClipArea, clip.mRadii,
8311                               mFrame->PresContext()->AppUnitsPerDevPixel());
8312 
8313   aBuilder.PushBackdropFilter(wr::ToLayoutRect(bounds), region,
8314                               wrFilters.filters, wrFilters.filter_datas,
8315                               !BackfaceIsHidden());
8316 
8317   wr::StackingContextParams params;
8318   params.clip =
8319       wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8320   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8321                            params);
8322 
8323   nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8324                                              aDisplayListBuilder);
8325   return true;
8326 }
8327 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)8328 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder* aBuilder,
8329                                      gfxContext* aCtx) {
8330   // TODO: Implement backdrop filters
8331   GetChildren()->Paint(aBuilder, aCtx,
8332                        mFrame->PresContext()->AppUnitsPerDevPixel());
8333 }
8334 
8335 /* static */
nsDisplayFilters(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList)8336 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder,
8337                                    nsIFrame* aFrame, nsDisplayList* aList)
8338     : nsDisplayEffectsBase(aBuilder, aFrame, aList),
8339       mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()) {
8340   MOZ_COUNT_CTOR(nsDisplayFilters);
8341   mVisibleRect = aBuilder->GetVisibleRect() +
8342                  aBuilder->GetCurrentFrameOffsetToReferenceFrame();
8343 }
8344 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)8345 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8346   PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) {
8347     GetChildren()->Paint(aBuilder, aContext,
8348                          mFrame->PresContext()->AppUnitsPerDevPixel());
8349   });
8350 }
8351 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const8352 void nsDisplayFilters::ComputeInvalidationRegion(
8353     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
8354     nsRegion* aInvalidRegion) const {
8355   nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry,
8356                                                   aInvalidRegion);
8357 
8358   const auto* geometry =
8359       static_cast<const nsDisplayFiltersGeometry*>(aGeometry);
8360 
8361   if (aBuilder->ShouldSyncDecodeImages() &&
8362       geometry->ShouldInvalidateToSyncDecodeImages()) {
8363     bool snap;
8364     nsRect bounds = GetBounds(aBuilder, &snap);
8365     aInvalidRegion->Or(*aInvalidRegion, bounds);
8366   }
8367 }
8368 
PaintWithContentsPaintCallback(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,const std::function<void (gfxContext * aContext)> & aPaintChildren)8369 void nsDisplayFilters::PaintWithContentsPaintCallback(
8370     nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
8371     const std::function<void(gfxContext* aContext)>& aPaintChildren) {
8372   imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
8373   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8374   SVGIntegrationUtils::PaintFramesParams params(
8375       *aCtx, mFrame, mVisibleRect, borderArea, aBuilder, false, imgParams);
8376 
8377   gfxPoint userSpaceToFrameSpaceOffset =
8378       SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params);
8379 
8380   SVGIntegrationUtils::PaintFilter(
8381       params,
8382       [&](gfxContext& aContext, nsIFrame* aTarget, const gfxMatrix& aTransform,
8383           const nsIntRect* aDirtyRect, imgDrawingParams& aImgParams) {
8384         gfxContextMatrixAutoSaveRestore autoSR(&aContext);
8385         aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(
8386             -userSpaceToFrameSpaceOffset));
8387         aPaintChildren(&aContext);
8388       });
8389   nsDisplayFiltersGeometry::UpdateDrawResult(this, imgParams.result);
8390 }
8391 
CanCreateWebRenderCommands() const8392 bool nsDisplayFilters::CanCreateWebRenderCommands() const {
8393   return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame);
8394 }
8395 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)8396 bool nsDisplayFilters::CreateWebRenderCommands(
8397     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8398     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8399     nsDisplayListBuilder* aDisplayListBuilder) {
8400   float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8401 
8402   WrFiltersHolder wrFilters;
8403   Maybe<nsRect> filterClip;
8404   bool initialized = true;
8405   auto filterChain = mFrame->StyleEffects()->mFilters.AsSpan();
8406   if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
8407                                                       wrFilters) &&
8408       !SVGIntegrationUtils::BuildWebRenderFilters(
8409           mFrame, filterChain, wrFilters, filterClip, initialized)) {
8410     return false;
8411   }
8412 
8413   if (!initialized) {
8414     // draw nothing
8415     return true;
8416   }
8417 
8418   wr::WrStackingContextClip clip{};
8419   if (filterClip) {
8420     auto devPxRect = LayoutDeviceRect::FromAppUnits(
8421         filterClip.value() + ToReferenceFrame(), auPerDevPixel);
8422     wr::WrClipId clipId =
8423         aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect));
8424     clip = wr::WrStackingContextClip::ClipId(clipId);
8425   } else {
8426     clip = wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
8427   }
8428 
8429   float opacity = aBuilder.GetInheritedOpacity();
8430   aBuilder.SetInheritedOpacity(1.0f);
8431   const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain();
8432   aBuilder.SetInheritedClipChain(nullptr);
8433   wr::StackingContextParams params;
8434   params.mFilters = std::move(wrFilters.filters);
8435   params.mFilterDatas = std::move(wrFilters.filter_datas);
8436   params.opacity = opacity != 1.0f ? &opacity : nullptr;
8437   params.clip = clip;
8438   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
8439                            params);
8440 
8441   nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc,
8442                                                 aManager, aDisplayListBuilder);
8443   aBuilder.SetInheritedOpacity(opacity);
8444   aBuilder.SetInheritedClipChain(oldClipChain);
8445 
8446   return true;
8447 }
8448 
8449 #ifdef MOZ_DUMP_PAINTING
PrintEffects(nsACString & aTo)8450 void nsDisplayFilters::PrintEffects(nsACString& aTo) {
8451   nsIFrame* firstFrame =
8452       nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8453   bool first = true;
8454   aTo += " effects=(";
8455   // We may exist for a mix of CSS filter functions and/or references to SVG
8456   // filters.  If we have invalid references to SVG filters then we paint
8457   // nothing, but otherwise we will apply one or more filters.
8458   if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) !=
8459       SVGObserverUtils::eHasRefsSomeInvalid) {
8460     if (!first) {
8461       aTo += ", ";
8462     }
8463     aTo += "filter";
8464   }
8465   aTo += ")";
8466 }
8467 #endif
8468 
nsDisplaySVGWrapper(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList)8469 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder,
8470                                          nsIFrame* aFrame, nsDisplayList* aList)
8471     : nsDisplayWrapList(aBuilder, aFrame, aList) {
8472   MOZ_COUNT_CTOR(nsDisplaySVGWrapper);
8473 }
8474 
ShouldFlattenAway(nsDisplayListBuilder * aBuilder)8475 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8476   return !aBuilder->GetWidgetLayerManager();
8477 }
8478 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)8479 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
8480     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8481     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8482     nsDisplayListBuilder* aDisplayListBuilder) {
8483   return CreateWebRenderCommandsNewClipListOption(
8484       aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8485 }
8486 
nsDisplayForeignObject(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList)8487 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder* aBuilder,
8488                                                nsIFrame* aFrame,
8489                                                nsDisplayList* aList)
8490     : nsDisplayWrapList(aBuilder, aFrame, aList) {
8491   MOZ_COUNT_CTOR(nsDisplayForeignObject);
8492 }
8493 
8494 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayForeignObject()8495 nsDisplayForeignObject::~nsDisplayForeignObject() {
8496   MOZ_COUNT_DTOR(nsDisplayForeignObject);
8497 }
8498 #endif
8499 
ShouldFlattenAway(nsDisplayListBuilder * aBuilder)8500 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
8501   return !aBuilder->GetWidgetLayerManager();
8502 }
8503 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)8504 bool nsDisplayForeignObject::CreateWebRenderCommands(
8505     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
8506     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
8507     nsDisplayListBuilder* aDisplayListBuilder) {
8508   AutoRestore<bool> restoreDoGrouping(aManager->CommandBuilder().mDoGrouping);
8509   aManager->CommandBuilder().mDoGrouping = false;
8510   return CreateWebRenderCommandsNewClipListOption(
8511       aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false);
8512 }
8513 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)8514 void nsDisplayLink::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
8515   auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8516   aCtx->GetDrawTarget()->Link(
8517       mLinkSpec.get(), NSRectToRect(GetPaintRect(aBuilder, aCtx), appPerDev));
8518 }
8519 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)8520 void nsDisplayDestination::Paint(nsDisplayListBuilder* aBuilder,
8521                                  gfxContext* aCtx) {
8522   auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel();
8523   aCtx->GetDrawTarget()->Destination(
8524       mDestinationName.get(),
8525       NSPointToPoint(GetPaintRect(aBuilder, aCtx).TopLeft(), appPerDev));
8526 }
8527 
SerializeWithCorrectZOrder(nsDisplayList * aOutResultList,nsIContent * aContent)8528 void nsDisplayListCollection::SerializeWithCorrectZOrder(
8529     nsDisplayList* aOutResultList, nsIContent* aContent) {
8530   // Sort PositionedDescendants() in CSS 'z-order' order.  The list is already
8531   // in content document order and SortByZOrder is a stable sort which
8532   // guarantees that boxes produced by the same element are placed together
8533   // in the sort. Consider a position:relative inline element that breaks
8534   // across lines and has absolutely positioned children; all the abs-pos
8535   // children should be z-ordered after all the boxes for the position:relative
8536   // element itself.
8537   PositionedDescendants()->SortByZOrder();
8538 
8539   // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
8540   // 1,2: backgrounds and borders
8541   aOutResultList->AppendToTop(BorderBackground());
8542   // 3: negative z-index children.
8543   for (auto* item : PositionedDescendants()->TakeItems()) {
8544     if (item->ZIndex() < 0) {
8545       aOutResultList->AppendToTop(item);
8546     } else {
8547       PositionedDescendants()->AppendToTop(item);
8548     }
8549   }
8550 
8551   // 4: block backgrounds
8552   aOutResultList->AppendToTop(BlockBorderBackgrounds());
8553   // 5: floats
8554   aOutResultList->AppendToTop(Floats());
8555   // 7: general content
8556   aOutResultList->AppendToTop(Content());
8557   // 7.5: outlines, in content tree order. We need to sort by content order
8558   // because an element with outline that breaks and has children with outline
8559   // might have placed child outline items between its own outline items.
8560   // The element's outline items need to all come before any child outline
8561   // items.
8562   if (aContent) {
8563     Outlines()->SortByContentOrder(aContent);
8564   }
8565   aOutResultList->AppendToTop(Outlines());
8566   // 8, 9: non-negative z-index children
8567   aOutResultList->AppendToTop(PositionedDescendants());
8568 }
8569 
8570 uint32_t PaintTelemetry::sPaintLevel = 0;
8571 
AutoRecordPaint()8572 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
8573   // Don't record nested paints.
8574   if (sPaintLevel++ > 0) {
8575     return;
8576   }
8577 
8578   mStart = TimeStamp::Now();
8579 }
8580 
~AutoRecordPaint()8581 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
8582   MOZ_ASSERT(sPaintLevel != 0);
8583   if (--sPaintLevel > 0) {
8584     return;
8585   }
8586 
8587   // If we're in multi-process mode, don't include paint times for the parent
8588   // process.
8589   if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
8590     return;
8591   }
8592 
8593   double totalMs = (TimeStamp::Now() - mStart).ToMilliseconds();
8594 
8595   // Record the total time.
8596   Telemetry::Accumulate(Telemetry::CONTENT_PAINT_TIME,
8597                         static_cast<uint32_t>(totalMs));
8598 }
8599 
GetSelfOrPlaceholderFor(nsIFrame * aFrame)8600 static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
8601   if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
8602     return aFrame;
8603   }
8604 
8605   if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
8606       !aFrame->GetPrevInFlow()) {
8607     return aFrame->GetPlaceholderFrame();
8608   }
8609 
8610   return aFrame;
8611 }
8612 
GetAncestorFor(nsIFrame * aFrame)8613 static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
8614   nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
8615   MOZ_ASSERT(f);
8616   return nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
8617 }
8618 
AutoBuildingDisplayList(nsDisplayListBuilder * aBuilder,nsIFrame * aForChild,const nsRect & aVisibleRect,const nsRect & aDirtyRect,const bool aIsTransformed)8619 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
8620     nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
8621     const nsRect& aVisibleRect, const nsRect& aDirtyRect,
8622     const bool aIsTransformed)
8623     : mBuilder(aBuilder),
8624       mPrevFrame(aBuilder->mCurrentFrame),
8625       mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
8626       mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
8627       mPrevAdditionalOffset(aBuilder->mAdditionalOffset),
8628       mPrevVisibleRect(aBuilder->mVisibleRect),
8629       mPrevDirtyRect(aBuilder->mDirtyRect),
8630       mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo),
8631       mPrevAncestorHasApzAwareEventHandler(
8632           aBuilder->mAncestorHasApzAwareEventHandler),
8633       mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
8634       mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
8635   if (aIsTransformed) {
8636     aBuilder->mCurrentOffsetToReferenceFrame =
8637         aBuilder->AdditionalOffset().refOr(nsPoint());
8638     aBuilder->mCurrentReferenceFrame = aForChild;
8639   } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
8640     aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
8641   } else {
8642     aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
8643         aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
8644   }
8645 
8646   // If aForChild is being visited from a frame other than it's ancestor frame,
8647   // mInInvalidSubtree will need to be recalculated the slow way.
8648   if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) {
8649     aBuilder->mInInvalidSubtree =
8650         aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
8651   } else {
8652     aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
8653   }
8654 
8655   aBuilder->mCurrentFrame = aForChild;
8656   aBuilder->mVisibleRect = aVisibleRect;
8657   aBuilder->mDirtyRect =
8658       aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
8659 }
8660 
8661 }  // namespace mozilla
8662