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