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/dom/TabChild.h"
22 #include "mozilla/dom/KeyframeEffectReadOnly.h"
23 #include "mozilla/gfx/2D.h"
24 #include "mozilla/layers/PLayerTransaction.h"
25 #include "nsCSSRendering.h"
26 #include "nsCSSRenderingGradients.h"
27 #include "nsISelectionController.h"
28 #include "nsIPresShell.h"
29 #include "nsRegion.h"
30 #include "nsStyleStructInlines.h"
31 #include "nsStyleTransformMatrix.h"
32 #include "gfxMatrix.h"
33 #include "gfxPrefs.h"
34 #include "nsSVGIntegrationUtils.h"
35 #include "nsSVGUtils.h"
36 #include "nsLayoutUtils.h"
37 #include "nsIScrollableFrame.h"
38 #include "nsIFrameInlines.h"
39 #include "nsThemeConstants.h"
40 #include "BorderConsts.h"
41 #include "LayerTreeInvalidation.h"
42 #include "mozilla/MathAlgorithms.h"
43 
44 #include "imgIContainer.h"
45 #include "BasicLayers.h"
46 #include "nsBoxFrame.h"
47 #include "nsImageFrame.h"
48 #include "nsSubDocumentFrame.h"
49 #include "SVGObserverUtils.h"
50 #include "nsSVGElement.h"
51 #include "nsSVGClipPathFrame.h"
52 #include "GeckoProfiler.h"
53 #include "nsViewManager.h"
54 #include "ImageLayers.h"
55 #include "ImageContainer.h"
56 #include "nsCanvasFrame.h"
57 #include "StickyScrollContainer.h"
58 #include "mozilla/AnimationPerformanceWarning.h"
59 #include "mozilla/AnimationUtils.h"
60 #include "mozilla/EffectCompositor.h"
61 #include "mozilla/EffectSet.h"
62 #include "mozilla/EventStates.h"
63 #include "mozilla/LookAndFeel.h"
64 #include "mozilla/OperatorNewExtensions.h"
65 #include "mozilla/PendingAnimationTracker.h"
66 #include "mozilla/Preferences.h"
67 #include "mozilla/StyleAnimationValue.h"
68 #include "mozilla/ServoBindings.h"
69 #include "mozilla/Telemetry.h"
70 #include "mozilla/UniquePtr.h"
71 #include "mozilla/Unused.h"
72 #include "mozilla/ViewportFrame.h"
73 #include "mozilla/gfx/gfxVars.h"
74 #include "ActiveLayerTracker.h"
75 #include "nsContentUtils.h"
76 #include "nsPrintfCString.h"
77 #include "UnitTransforms.h"
78 #include "LayersLogging.h"
79 #include "FrameLayerBuilder.h"
80 #include "mozilla/EventStateManager.h"
81 #ifdef MOZ_OLD_STYLE
82 #include "mozilla/GeckoRestyleManager.h"
83 #endif
84 #include "nsCaret.h"
85 #include "nsISelection.h"
86 #include "nsDOMTokenList.h"
87 #include "mozilla/RuleNodeCacheConditions.h"
88 #include "nsCSSProps.h"
89 #include "nsSVGMaskFrame.h"
90 #include "nsTableCellFrame.h"
91 #include "nsTableColFrame.h"
92 #include "nsSliderFrame.h"
93 #include "ClientLayerManager.h"
94 #include "mozilla/layers/StackingContextHelper.h"
95 #include "mozilla/layers/WebRenderBridgeChild.h"
96 #include "mozilla/layers/WebRenderLayerManager.h"
97 #include "mozilla/layers/WebRenderMessages.h"
98 #include "mozilla/layers/WebRenderScrollData.h"
99 
100 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
101 // GetTickCount().
102 #ifdef GetCurrentTime
103 #undef GetCurrentTime
104 #endif
105 
106 using namespace mozilla;
107 using namespace mozilla::layers;
108 using namespace mozilla::dom;
109 using namespace mozilla::layout;
110 using namespace mozilla::gfx;
111 
112 typedef FrameMetrics::ViewID ViewID;
113 typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
114 
115 #ifdef DEBUG
SpammyLayoutWarningsEnabled()116 static bool SpammyLayoutWarningsEnabled() {
117   static bool sValue = false;
118   static bool sValueInitialized = false;
119 
120   if (!sValueInitialized) {
121     Preferences::GetBool("layout.spammy_warnings.enabled", &sValue);
122     sValueInitialized = true;
123   }
124 
125   return sValue;
126 }
127 #endif
128 
IsAncestor(const ActiveScrolledRoot * aAncestor,const ActiveScrolledRoot * aDescendant)129 /* static */ bool ActiveScrolledRoot::IsAncestor(
130     const ActiveScrolledRoot* aAncestor,
131     const ActiveScrolledRoot* aDescendant) {
132   if (!aAncestor) {
133     // nullptr is the root
134     return true;
135   }
136   if (Depth(aAncestor) > Depth(aDescendant)) {
137     return false;
138   }
139   const ActiveScrolledRoot* asr = aDescendant;
140   while (asr) {
141     if (asr == aAncestor) {
142       return true;
143     }
144     asr = asr->mParent;
145   }
146   return false;
147 }
148 
ToString(const ActiveScrolledRoot * aActiveScrolledRoot)149 /* static */ nsCString ActiveScrolledRoot::ToString(
150     const ActiveScrolledRoot* aActiveScrolledRoot) {
151   nsAutoCString str;
152   for (auto* asr = aActiveScrolledRoot; asr; asr = asr->mParent) {
153     str.AppendPrintf("<0x%p>", asr->mScrollableFrame);
154     if (asr->mParent) {
155       str.AppendLiteral(", ");
156     }
157   }
158   return str;
159 }
160 
MakeCSSAngle(const nsCSSValue & aValue)161 static inline CSSAngle MakeCSSAngle(const nsCSSValue& aValue) {
162   return CSSAngle(aValue.GetAngleValue(), aValue.GetUnit());
163 }
164 
AddTransformFunctions(const nsCSSValueList * aList,nsStyleContext * aContext,nsPresContext * aPresContext,TransformReferenceBox & aRefBox,InfallibleTArray<TransformFunction> & aFunctions)165 static void AddTransformFunctions(
166     const nsCSSValueList* aList, nsStyleContext* aContext,
167     nsPresContext* aPresContext, TransformReferenceBox& aRefBox,
168     InfallibleTArray<TransformFunction>& aFunctions) {
169   if (aList->mValue.GetUnit() == eCSSUnit_None) {
170     return;
171   }
172 
173   GeckoStyleContext* contextIfGecko =
174 #ifdef MOZ_OLD_STYLE
175       aContext ? aContext->GetAsGecko() : nullptr;
176 #else
177       nullptr;
178 #endif
179 
180   for (const nsCSSValueList* curr = aList; curr; curr = curr->mNext) {
181     const nsCSSValue& currElem = curr->mValue;
182     NS_ASSERTION(currElem.GetUnit() == eCSSUnit_Function,
183                  "Stream should consist solely of functions!");
184     nsCSSValue::Array* array = currElem.GetArrayValue();
185     RuleNodeCacheConditions conditions;
186     switch (nsStyleTransformMatrix::TransformFunctionOf(array)) {
187       case eCSSKeyword_rotatex: {
188         CSSAngle theta = MakeCSSAngle(array->Item(1));
189         aFunctions.AppendElement(RotationX(theta));
190         break;
191       }
192       case eCSSKeyword_rotatey: {
193         CSSAngle theta = MakeCSSAngle(array->Item(1));
194         aFunctions.AppendElement(RotationY(theta));
195         break;
196       }
197       case eCSSKeyword_rotatez: {
198         CSSAngle theta = MakeCSSAngle(array->Item(1));
199         aFunctions.AppendElement(RotationZ(theta));
200         break;
201       }
202       case eCSSKeyword_rotate: {
203         CSSAngle theta = MakeCSSAngle(array->Item(1));
204         aFunctions.AppendElement(Rotation(theta));
205         break;
206       }
207       case eCSSKeyword_rotate3d: {
208         double x = array->Item(1).GetFloatValue();
209         double y = array->Item(2).GetFloatValue();
210         double z = array->Item(3).GetFloatValue();
211         CSSAngle theta = MakeCSSAngle(array->Item(4));
212         aFunctions.AppendElement(Rotation3D(x, y, z, theta));
213         break;
214       }
215       case eCSSKeyword_scalex: {
216         double x = array->Item(1).GetFloatValue();
217         aFunctions.AppendElement(Scale(x, 1, 1));
218         break;
219       }
220       case eCSSKeyword_scaley: {
221         double y = array->Item(1).GetFloatValue();
222         aFunctions.AppendElement(Scale(1, y, 1));
223         break;
224       }
225       case eCSSKeyword_scalez: {
226         double z = array->Item(1).GetFloatValue();
227         aFunctions.AppendElement(Scale(1, 1, z));
228         break;
229       }
230       case eCSSKeyword_scale: {
231         double x = array->Item(1).GetFloatValue();
232         // scale(x) is shorthand for scale(x, x);
233         double y = array->Count() == 2 ? x : array->Item(2).GetFloatValue();
234         aFunctions.AppendElement(Scale(x, y, 1));
235         break;
236       }
237       case eCSSKeyword_scale3d: {
238         double x = array->Item(1).GetFloatValue();
239         double y = array->Item(2).GetFloatValue();
240         double z = array->Item(3).GetFloatValue();
241         aFunctions.AppendElement(Scale(x, y, z));
242         break;
243       }
244       case eCSSKeyword_translatex: {
245         double x = nsStyleTransformMatrix::ProcessTranslatePart(
246             array->Item(1), contextIfGecko, aPresContext, conditions, &aRefBox,
247             &TransformReferenceBox::Width);
248         aFunctions.AppendElement(Translation(x, 0, 0));
249         break;
250       }
251       case eCSSKeyword_translatey: {
252         double y = nsStyleTransformMatrix::ProcessTranslatePart(
253             array->Item(1), contextIfGecko, aPresContext, conditions, &aRefBox,
254             &TransformReferenceBox::Height);
255         aFunctions.AppendElement(Translation(0, y, 0));
256         break;
257       }
258       case eCSSKeyword_translatez: {
259         double z = nsStyleTransformMatrix::ProcessTranslatePart(
260             array->Item(1), contextIfGecko, aPresContext, conditions, nullptr);
261         aFunctions.AppendElement(Translation(0, 0, z));
262         break;
263       }
264       case eCSSKeyword_translate: {
265         double x = nsStyleTransformMatrix::ProcessTranslatePart(
266             array->Item(1), contextIfGecko, aPresContext, conditions, &aRefBox,
267             &TransformReferenceBox::Width);
268         // translate(x) is shorthand for translate(x, 0)
269         double y = 0;
270         if (array->Count() == 3) {
271           y = nsStyleTransformMatrix::ProcessTranslatePart(
272               array->Item(2), contextIfGecko, aPresContext, conditions,
273               &aRefBox, &TransformReferenceBox::Height);
274         }
275         aFunctions.AppendElement(Translation(x, y, 0));
276         break;
277       }
278       case eCSSKeyword_translate3d: {
279         double x = nsStyleTransformMatrix::ProcessTranslatePart(
280             array->Item(1), contextIfGecko, aPresContext, conditions, &aRefBox,
281             &TransformReferenceBox::Width);
282         double y = nsStyleTransformMatrix::ProcessTranslatePart(
283             array->Item(2), contextIfGecko, aPresContext, conditions, &aRefBox,
284             &TransformReferenceBox::Height);
285         double z = nsStyleTransformMatrix::ProcessTranslatePart(
286             array->Item(3), contextIfGecko, aPresContext, conditions, nullptr);
287 
288         aFunctions.AppendElement(Translation(x, y, z));
289         break;
290       }
291       case eCSSKeyword_skewx: {
292         CSSAngle x = MakeCSSAngle(array->Item(1));
293         aFunctions.AppendElement(SkewX(x));
294         break;
295       }
296       case eCSSKeyword_skewy: {
297         CSSAngle y = MakeCSSAngle(array->Item(1));
298         aFunctions.AppendElement(SkewY(y));
299         break;
300       }
301       case eCSSKeyword_skew: {
302         CSSAngle x = MakeCSSAngle(array->Item(1));
303         // skew(x) is shorthand for skew(x, 0)
304         CSSAngle y(0.0f, eCSSUnit_Degree);
305         if (array->Count() == 3) {
306           y = MakeCSSAngle(array->Item(2));
307         }
308         aFunctions.AppendElement(Skew(x, y));
309         break;
310       }
311       case eCSSKeyword_matrix: {
312         gfx::Matrix4x4 matrix;
313         matrix._11 = array->Item(1).GetFloatValue();
314         matrix._12 = array->Item(2).GetFloatValue();
315         matrix._13 = 0;
316         matrix._14 = 0;
317         matrix._21 = array->Item(3).GetFloatValue();
318         matrix._22 = array->Item(4).GetFloatValue();
319         matrix._23 = 0;
320         matrix._24 = 0;
321         matrix._31 = 0;
322         matrix._32 = 0;
323         matrix._33 = 1;
324         matrix._34 = 0;
325         matrix._41 = ProcessTranslatePart(array->Item(5), contextIfGecko,
326                                           aPresContext, conditions, &aRefBox,
327                                           &TransformReferenceBox::Width);
328         matrix._42 = ProcessTranslatePart(array->Item(6), contextIfGecko,
329                                           aPresContext, conditions, &aRefBox,
330                                           &TransformReferenceBox::Height);
331         matrix._43 = 0;
332         matrix._44 = 1;
333         aFunctions.AppendElement(TransformMatrix(matrix));
334         break;
335       }
336       case eCSSKeyword_matrix3d: {
337         gfx::Matrix4x4 matrix;
338         matrix._11 = array->Item(1).GetFloatValue();
339         matrix._12 = array->Item(2).GetFloatValue();
340         matrix._13 = array->Item(3).GetFloatValue();
341         matrix._14 = array->Item(4).GetFloatValue();
342         matrix._21 = array->Item(5).GetFloatValue();
343         matrix._22 = array->Item(6).GetFloatValue();
344         matrix._23 = array->Item(7).GetFloatValue();
345         matrix._24 = array->Item(8).GetFloatValue();
346         matrix._31 = array->Item(9).GetFloatValue();
347         matrix._32 = array->Item(10).GetFloatValue();
348         matrix._33 = array->Item(11).GetFloatValue();
349         matrix._34 = array->Item(12).GetFloatValue();
350         matrix._41 = ProcessTranslatePart(array->Item(13), contextIfGecko,
351                                           aPresContext, conditions, &aRefBox,
352                                           &TransformReferenceBox::Width);
353         matrix._42 = ProcessTranslatePart(array->Item(14), contextIfGecko,
354                                           aPresContext, conditions, &aRefBox,
355                                           &TransformReferenceBox::Height);
356         matrix._43 =
357             ProcessTranslatePart(array->Item(15), contextIfGecko, aPresContext,
358                                  conditions, &aRefBox, nullptr);
359         matrix._44 = array->Item(16).GetFloatValue();
360         aFunctions.AppendElement(TransformMatrix(matrix));
361         break;
362       }
363       case eCSSKeyword_interpolatematrix: {
364         bool dummy;
365         Matrix4x4 matrix;
366         nsStyleTransformMatrix::ProcessInterpolateMatrix(
367             matrix, array, contextIfGecko, aPresContext, conditions, aRefBox,
368             &dummy);
369         aFunctions.AppendElement(TransformMatrix(matrix));
370         break;
371       }
372       case eCSSKeyword_accumulatematrix: {
373         bool dummy;
374         Matrix4x4 matrix;
375         nsStyleTransformMatrix::ProcessAccumulateMatrix(
376             matrix, array, contextIfGecko, aPresContext, conditions, aRefBox,
377             &dummy);
378         aFunctions.AppendElement(TransformMatrix(matrix));
379         break;
380       }
381       case eCSSKeyword_perspective: {
382         aFunctions.AppendElement(Perspective(array->Item(1).GetFloatValue()));
383         break;
384       }
385       default:
386         NS_ERROR("Function not handled yet!");
387     }
388   }
389 }
390 
AddTransformFunctions(const nsCSSValueSharedList * aList,const nsIFrame * aFrame,TransformReferenceBox & aRefBox,layers::Animatable & aAnimatable)391 static void AddTransformFunctions(const nsCSSValueSharedList* aList,
392                                   const nsIFrame* aFrame,
393                                   TransformReferenceBox& aRefBox,
394                                   layers::Animatable& aAnimatable) {
395   MOZ_ASSERT(aList->mHead);
396   AddTransformFunctions(aList->mHead, aFrame->StyleContext(),
397                         aFrame->PresContext(), aRefBox,
398                         aAnimatable.get_ArrayOfTransformFunction());
399 }
400 
ToTimingFunction(const Maybe<ComputedTimingFunction> & aCTF)401 static TimingFunction ToTimingFunction(
402     const Maybe<ComputedTimingFunction>& aCTF) {
403   if (aCTF.isNothing()) {
404     return TimingFunction(null_t());
405   }
406 
407   if (aCTF->HasSpline()) {
408     const nsSMILKeySpline* spline = aCTF->GetFunction();
409     return TimingFunction(CubicBezierFunction(spline->X1(), spline->Y1(),
410                                               spline->X2(), spline->Y2()));
411   }
412 
413   if (aCTF->GetType() == nsTimingFunction::Type::Frames) {
414     return TimingFunction(FramesFunction(aCTF->GetFrames()));
415   }
416 
417   uint32_t type = aCTF->GetType() == nsTimingFunction::Type::StepStart ? 1 : 2;
418   return TimingFunction(StepFunction(aCTF->GetSteps(), type));
419 }
420 
SetAnimatable(nsCSSPropertyID aProperty,const AnimationValue & aAnimationValue,nsIFrame * aFrame,TransformReferenceBox & aRefBox,layers::Animatable & aAnimatable)421 static void SetAnimatable(nsCSSPropertyID aProperty,
422                           const AnimationValue& aAnimationValue,
423                           nsIFrame* aFrame, TransformReferenceBox& aRefBox,
424                           layers::Animatable& aAnimatable) {
425   MOZ_ASSERT(aFrame);
426 
427   if (aAnimationValue.IsNull()) {
428     aAnimatable = null_t();
429     return;
430   }
431 
432   switch (aProperty) {
433     case eCSSProperty_opacity:
434       aAnimatable = aAnimationValue.GetOpacity();
435       break;
436     case eCSSProperty_transform: {
437       aAnimatable = InfallibleTArray<TransformFunction>();
438       if (aAnimationValue.mServo) {
439         RefPtr<nsCSSValueSharedList> list;
440         Servo_AnimationValue_GetTransform(aAnimationValue.mServo, &list);
441         AddTransformFunctions(list, aFrame, aRefBox, aAnimatable);
442       } else {
443 #ifdef MOZ_OLD_STYLE
444         nsCSSValueSharedList* list =
445             aAnimationValue.mGecko.GetCSSValueSharedListValue();
446         AddTransformFunctions(list, aFrame, aRefBox, aAnimatable);
447 #else
448         MOZ_CRASH("old style system disabled");
449 #endif
450       }
451       break;
452     }
453     default:
454       MOZ_ASSERT_UNREACHABLE("Unsupported property");
455   }
456 }
457 
AddAnimationForProperty(nsIFrame * aFrame,const AnimationProperty & aProperty,dom::Animation * aAnimation,AnimationInfo & aAnimationInfo,AnimationData & aData,bool aPending)458 static void AddAnimationForProperty(nsIFrame* aFrame,
459                                     const AnimationProperty& aProperty,
460                                     dom::Animation* aAnimation,
461                                     AnimationInfo& aAnimationInfo,
462                                     AnimationData& aData, bool aPending) {
463   MOZ_ASSERT(aAnimation->GetEffect(),
464              "Should not be adding an animation without an effect");
465   MOZ_ASSERT(!aAnimation->GetCurrentOrPendingStartTime().IsNull() ||
466                  !aAnimation->IsPlaying() ||
467                  (aAnimation->GetTimeline() &&
468                   aAnimation->GetTimeline()->TracksWallclockTime()),
469              "If the animation has an unresolved start time it should either"
470              " be static (so we don't need a start time) or else have a"
471              " timeline capable of converting TimeStamps (so we can calculate"
472              " one later");
473 
474   layers::Animation* animation =
475       aPending ? aAnimationInfo.AddAnimationForNextTransaction()
476                : aAnimationInfo.AddAnimation();
477 
478   const TimingParams& timing = aAnimation->GetEffect()->SpecifiedTiming();
479 
480   // If we are starting a new transition that replaces an existing transition
481   // running on the compositor, it is possible that the animation on the
482   // compositor will have advanced ahead of the main thread. If we use as
483   // the starting point of the new transition, the current value of the
484   // replaced transition as calculated on the main thread using the refresh
485   // driver time, the new transition will jump when it starts. Instead, we
486   // re-calculate the starting point of the new transition by applying the
487   // current TimeStamp to the parameters of the replaced transition.
488   //
489   // We need to do this here, rather than when we generate the new transition,
490   // since after generating the new transition other requestAnimationFrame
491   // callbacks may run that introduce further lag between the main thread and
492   // the compositor.
493   if (aAnimation->AsCSSTransition() && aAnimation->GetEffect() &&
494       aAnimation->GetEffect()->AsTransition()) {
495     // We update startValue from the replaced transition only if the effect is
496     // an ElementPropertyTransition.
497     aAnimation->GetEffect()
498         ->AsTransition()
499         ->UpdateStartValueFromReplacedTransition();
500   }
501 
502   animation->originTime() =
503       !aAnimation->GetTimeline()
504           ? TimeStamp()
505           : aAnimation->GetTimeline()->ToTimeStamp(TimeDuration());
506 
507   Nullable<TimeDuration> startTime = aAnimation->GetCurrentOrPendingStartTime();
508   if (startTime.IsNull()) {
509     animation->startTime() = null_t();
510   } else {
511     animation->startTime() = startTime.Value();
512   }
513 
514   animation->holdTime() = aAnimation->GetCurrentTime().Value();
515 
516   const ComputedTiming computedTiming =
517       aAnimation->GetEffect()->GetComputedTiming();
518   animation->delay() = timing.Delay();
519   animation->endDelay() = timing.EndDelay();
520   animation->duration() = computedTiming.mDuration;
521   animation->iterations() = computedTiming.mIterations;
522   animation->iterationStart() = computedTiming.mIterationStart;
523   animation->direction() = static_cast<uint8_t>(timing.Direction());
524   animation->fillMode() = static_cast<uint8_t>(computedTiming.mFill);
525   animation->property() = aProperty.mProperty;
526   animation->playbackRate() = aAnimation->CurrentOrPendingPlaybackRate();
527   animation->previousPlaybackRate() =
528       aAnimation->HasPendingPlaybackRate()
529           ? aAnimation->PlaybackRate()
530           : std::numeric_limits<float>::quiet_NaN();
531   animation->data() = aData;
532   animation->easingFunction() = ToTimingFunction(timing.TimingFunction());
533   animation->iterationComposite() = static_cast<uint8_t>(
534       aAnimation->GetEffect()->AsKeyframeEffect()->IterationComposite());
535   animation->isNotPlaying() = !aAnimation->IsPlaying();
536 
537   TransformReferenceBox refBox(aFrame);
538 
539   // If the animation is additive or accumulates, we need to pass its base value
540   // to the compositor.
541 
542   AnimationValue baseStyle =
543       aAnimation->GetEffect()->AsKeyframeEffect()->BaseStyle(
544           aProperty.mProperty);
545   if (!baseStyle.IsNull()) {
546     SetAnimatable(aProperty.mProperty, baseStyle, aFrame, refBox,
547                   animation->baseStyle());
548   } else {
549     animation->baseStyle() = null_t();
550   }
551 
552   for (uint32_t segIdx = 0; segIdx < aProperty.mSegments.Length(); segIdx++) {
553     const AnimationPropertySegment& segment = aProperty.mSegments[segIdx];
554 
555     AnimationSegment* animSegment = animation->segments().AppendElement();
556     SetAnimatable(aProperty.mProperty, segment.mFromValue, aFrame, refBox,
557                   animSegment->startState());
558     SetAnimatable(aProperty.mProperty, segment.mToValue, aFrame, refBox,
559                   animSegment->endState());
560 
561     animSegment->startPortion() = segment.mFromKey;
562     animSegment->endPortion() = segment.mToKey;
563     animSegment->startComposite() =
564         static_cast<uint8_t>(segment.mFromComposite);
565     animSegment->endComposite() = static_cast<uint8_t>(segment.mToComposite);
566     animSegment->sampleFn() = ToTimingFunction(segment.mTimingFunction);
567   }
568 }
569 
AddAnimationsForProperty(nsIFrame * aFrame,nsDisplayListBuilder * aBuilder,nsDisplayItem * aItem,nsCSSPropertyID aProperty,AnimationInfo & aAnimationInfo,bool aPending,bool aIsForWebRender)570 static void AddAnimationsForProperty(nsIFrame* aFrame,
571                                      nsDisplayListBuilder* aBuilder,
572                                      nsDisplayItem* aItem,
573                                      nsCSSPropertyID aProperty,
574                                      AnimationInfo& aAnimationInfo,
575                                      bool aPending, bool aIsForWebRender) {
576   if (aPending) {
577     aAnimationInfo.ClearAnimationsForNextTransaction();
578   } else {
579     aAnimationInfo.ClearAnimations();
580   }
581 
582   // Update the animation generation on the layer. We need to do this before
583   // any early returns since even if we don't add any animations to the
584   // layer, we still need to mark it as up-to-date with regards to animations.
585   // Otherwise, in RestyleManager we'll notice the discrepancy between the
586   // animation generation numbers and update the layer indefinitely.
587   uint64_t animationGeneration =
588       RestyleManager::GetAnimationGenerationForFrame(aFrame);
589   aAnimationInfo.SetAnimationGeneration(animationGeneration);
590 
591   EffectCompositor::ClearIsRunningOnCompositor(aFrame, aProperty);
592   nsTArray<RefPtr<dom::Animation>> compositorAnimations =
593       EffectCompositor::GetAnimationsForCompositor(aFrame, aProperty);
594   if (compositorAnimations.IsEmpty()) {
595     return;
596   }
597 
598   // If the frame is not prerendered, bail out.
599   // Do this check only during layer construction; during updating the
600   // caller is required to check it appropriately.
601   if (aItem && !aItem->CanUseAsyncAnimations(aBuilder)) {
602     // EffectCompositor needs to know that we refused to run this animation
603     // asynchronously so that it will not throttle the main thread
604     // animation.
605     aFrame->SetProperty(nsIFrame::RefusedAsyncAnimationProperty(), true);
606 
607     // We need to schedule another refresh driver run so that EffectCompositor
608     // gets a chance to unthrottle the animation.
609     aFrame->SchedulePaint();
610     return;
611   }
612 
613   AnimationData data;
614   if (aProperty == eCSSProperty_transform) {
615     // XXX Performance here isn't ideal for SVG. We'd prefer to avoid resolving
616     // the dimensions of refBox. That said, we only get here if there are CSS
617     // animations or transitions on this element, and that is likely to be a
618     // lot rarer than transforms on SVG (the frequency of which drives the need
619     // for TransformReferenceBox).
620     TransformReferenceBox refBox(aFrame);
621     nsRect bounds(0, 0, refBox.Width(), refBox.Height());
622     // all data passed directly to the compositor should be in dev pixels
623     int32_t devPixelsToAppUnits = aFrame->PresContext()->AppUnitsPerDevPixel();
624     float scale = devPixelsToAppUnits;
625     Point3D offsetToTransformOrigin =
626         nsDisplayTransform::GetDeltaToTransformOrigin(aFrame, scale, &bounds);
627     nsPoint origin;
628     float scaleX = 1.0f;
629     float scaleY = 1.0f;
630     bool hasPerspectiveParent = false;
631     if (aIsForWebRender) {
632       // leave origin empty, because we are sending it separately on the
633       // stacking context that we are pushing to WR, and WR will automatically
634       // include it when picking up the animated transform values
635     } else if (aItem) {
636       // This branch is for display items to leverage the cache of
637       // nsDisplayListBuilder.
638       origin = aItem->ToReferenceFrame();
639     } else {
640       // This branch is running for restyling.
641       // Animations are animated at the coordination of the reference
642       // frame outside, not the given frame itself.  The given frame
643       // is also reference frame too, so the parent's reference frame
644       // are used.
645       nsIFrame* referenceFrame = nsLayoutUtils::GetReferenceFrame(
646           nsLayoutUtils::GetCrossDocParentFrame(aFrame));
647       origin = aFrame->GetOffsetToCrossDoc(referenceFrame);
648     }
649 
650     data = TransformData(origin, offsetToTransformOrigin, bounds,
651                          devPixelsToAppUnits, scaleX, scaleY,
652                          hasPerspectiveParent);
653   } else if (aProperty == eCSSProperty_opacity) {
654     data = null_t();
655   }
656 
657   MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty,
658                                       CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
659              "inconsistent property flags");
660 
661   // Add from first to last (since last overrides)
662   for (size_t animIdx = 0; animIdx < compositorAnimations.Length(); animIdx++) {
663     dom::Animation* anim = compositorAnimations[animIdx];
664     if (!anim->IsRelevant()) {
665       continue;
666     }
667 
668     dom::KeyframeEffectReadOnly* keyframeEffect =
669         anim->GetEffect() ? anim->GetEffect()->AsKeyframeEffect() : nullptr;
670     MOZ_ASSERT(keyframeEffect,
671                "A playing animation should have a keyframe effect");
672     const AnimationProperty* property =
673         keyframeEffect->GetEffectiveAnimationOfProperty(aProperty);
674     if (!property) {
675       continue;
676     }
677 
678     // Note that if the property is overridden by !important rules,
679     // GetEffectiveAnimationOfProperty returns null instead.
680     // This is what we want, since if we have animations overridden by
681     // !important rules, we don't want to send them to the compositor.
682     MOZ_ASSERT(
683         anim->CascadeLevel() != EffectCompositor::CascadeLevel::Animations ||
684             !EffectSet::GetEffectSet(aFrame)
685                  ->PropertiesWithImportantRules()
686                  .HasProperty(aProperty),
687         "GetEffectiveAnimationOfProperty already tested the property "
688         "is not overridden by !important rules");
689 
690     // Don't add animations that are pending if their timeline does not
691     // track wallclock time. This is because any pending animations on layers
692     // will have their start time updated with the current wallclock time.
693     // If we can't convert that wallclock time back to an equivalent timeline
694     // time, we won't be able to update the content animation and it will end
695     // up being out of sync with the layer animation.
696     //
697     // Currently this only happens when the timeline is driven by a refresh
698     // driver under test control. In this case, the next time the refresh
699     // driver is advanced it will trigger any pending animations.
700     if (anim->Pending() &&
701         (anim->GetTimeline() && !anim->GetTimeline()->TracksWallclockTime())) {
702       continue;
703     }
704 
705     AddAnimationForProperty(aFrame, *property, anim, aAnimationInfo, data,
706                             aPending);
707     keyframeEffect->SetIsRunningOnCompositor(aProperty, true);
708   }
709 }
710 
GenerateAndPushTextMask(nsIFrame * aFrame,gfxContext * aContext,const nsRect & aFillRect,nsDisplayListBuilder * aBuilder)711 static bool GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
712                                     const nsRect& aFillRect,
713                                     nsDisplayListBuilder* aBuilder) {
714   if (aBuilder->IsForGenerateGlyphMask() ||
715       aBuilder->IsForPaintingSelectionBG()) {
716     return false;
717   }
718 
719   // The main function of enabling background-clip:text property value.
720   // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
721   // this function to
722   // 1. Paint background color of the selection text if any.
723   // 2. Generate a mask by all descendant text frames
724   // 3. Push the generated mask into aContext.
725   //
726   // TBD: we actually generate display list of aFrame twice here. It's better
727   // to reuse the same display list and paint that one twice, one for selection
728   // background, one for generating text mask.
729 
730   gfxContext* sourceCtx = aContext;
731   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
732       aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel());
733 
734   {
735     // Paint text selection background into sourceCtx.
736     gfxContextMatrixAutoSaveRestore save(sourceCtx);
737     sourceCtx->SetMatrix(sourceCtx->CurrentMatrix().PreTranslate(
738         bounds.TopLeft().ToUnknownPoint()));
739 
740     nsLayoutUtils::PaintFrame(
741         aContext, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()),
742         NS_RGB(255, 255, 255),
743         nsDisplayListBuilderMode::PAINTING_SELECTION_BACKGROUND);
744   }
745 
746   // Evaluate required surface size.
747   IntRect drawRect =
748       RoundedOut(ToRect(sourceCtx->GetClipExtents(gfxContext::eDeviceSpace)));
749 
750   Matrix currentMatrix = sourceCtx->CurrentMatrix();
751   Matrix maskTransform =
752       currentMatrix * Matrix::Translation(-drawRect.x, -drawRect.y);
753   maskTransform.Invert();
754 
755   // Create a mask surface.
756   RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget();
757   RefPtr<DrawTarget> maskDT = sourceTarget->CreateClippedDrawTarget(
758       drawRect.Size(), maskTransform * currentMatrix, SurfaceFormat::A8);
759   if (!maskDT || !maskDT->IsValid()) {
760     return false;
761   }
762   RefPtr<gfxContext> maskCtx =
763       gfxContext::CreatePreservingTransformOrNull(maskDT);
764   MOZ_ASSERT(maskCtx);
765   maskCtx->SetMatrix(Matrix::Translation(bounds.TopLeft().ToUnknownPoint()) *
766                      currentMatrix * Matrix::Translation(-drawRect.TopLeft()));
767 
768   // Shade text shape into mask A8 surface.
769   nsLayoutUtils::PaintFrame(
770       maskCtx, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()),
771       NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GENERATE_GLYPH);
772 
773   // Push the generated mask into aContext, so that the caller can pop and
774   // blend with it.
775   RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
776   sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0,
777                                    maskSurface, maskTransform);
778 
779   return true;
780 }
781 
AddAnimationsAndTransitionsToLayer(Layer * aLayer,nsDisplayListBuilder * aBuilder,nsDisplayItem * aItem,nsIFrame * aFrame,nsCSSPropertyID aProperty)782 /* static */ void nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
783     Layer* aLayer, nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem,
784     nsIFrame* aFrame, nsCSSPropertyID aProperty) {
785   MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty,
786                                       CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
787              "inconsistent property flags");
788 
789   // This function can be called in two ways:  from
790   // nsDisplay*::BuildLayer while constructing a layer (with all
791   // pointers non-null), or from RestyleManager's handling of
792   // UpdateOpacityLayer/UpdateTransformLayer hints.
793   MOZ_ASSERT(!aBuilder == !aItem,
794              "should only be called in two configurations, with both "
795              "aBuilder and aItem, or with neither");
796   MOZ_ASSERT(!aItem || aFrame == aItem->Frame(), "frame mismatch");
797 
798   // Only send animations to a layer that is actually using
799   // off-main-thread compositing.
800   LayersBackend backend = aLayer->Manager()->GetBackendType();
801   if (!(backend == layers::LayersBackend::LAYERS_CLIENT ||
802         backend == layers::LayersBackend::LAYERS_WR)) {
803     return;
804   }
805 
806   bool pending = !aBuilder;
807   AnimationInfo& animationInfo = aLayer->GetAnimationInfo();
808   AddAnimationsForProperty(aFrame, aBuilder, aItem, aProperty, animationInfo,
809                            pending, false);
810   animationInfo.TransferMutatedFlagToLayer(aLayer);
811 }
812 
MergeItems(nsTArray<nsDisplayItem * > & aMergedItems)813 nsDisplayItem* nsDisplayListBuilder::MergeItems(
814     nsTArray<nsDisplayItem*>& aMergedItems) {
815   // For merging, we create a temporary item by cloning the last item of the
816   // mergeable items list. This ensures that the temporary item will have the
817   // correct frame and bounds.
818   nsDisplayItem* merged = nullptr;
819 
820   for (nsDisplayItem* item : Reversed(aMergedItems)) {
821     MOZ_ASSERT(item);
822 
823     if (!merged) {
824       // Create the temporary item.
825       merged = item->Clone(this);
826       MOZ_ASSERT(merged);
827 
828       AddTemporaryItem(merged);
829     } else {
830       // Merge the item properties (frame/bounds/etc) with the previously
831       // created temporary item.
832       MOZ_ASSERT(merged->CanMerge(item));
833       merged->Merge(item);
834     }
835 
836     // Create nsDisplayWrapList that points to the internal display list of the
837     // item we are merging. This nsDisplayWrapList is added to the display list
838     // of the temporary item.
839     merged->MergeDisplayListFromItem(this, item);
840   }
841 
842   return merged;
843 }
844 
845 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
SetCurrentActiveScrolledRoot(const ActiveScrolledRoot * aActiveScrolledRoot)846     SetCurrentActiveScrolledRoot(
847         const ActiveScrolledRoot* aActiveScrolledRoot) {
848   MOZ_ASSERT(!mUsed);
849 
850   // Set the builder's mCurrentActiveScrolledRoot.
851   mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;
852 
853   // We also need to adjust the builder's mCurrentContainerASR.
854   // mCurrentContainerASR needs to be an ASR that all the container's
855   // contents have finite bounds with respect to. If aActiveScrolledRoot
856   // is an ancestor ASR of mCurrentContainerASR, that means we need to
857   // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
858   // the items that will be created with aActiveScrolledRoot wouldn't
859   // have finite bounds with respect to mCurrentContainerASR. There's one
860   // exception, in the case where there's a content clip on the builder
861   // that is scrolled by a descendant ASR of aActiveScrolledRoot. This
862   // content clip will clip all items that are created while this
863   // AutoCurrentActiveScrolledRootSetter exists. This means that the items
864   // created during our lifetime will have finite bounds with respect to
865   // the content clip's ASR, even if the items' actual ASR is an ancestor
866   // of that. And it also means that mCurrentContainerASR only needs to be
867   // set to the content clip's ASR and not all the way to aActiveScrolledRoot.
868   // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
869   // and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
870 
871   // finiteBoundsASR is the leafmost ASR that all items created during
872   // object's lifetime have finite bounds with respect to.
873   const ActiveScrolledRoot* finiteBoundsASR =
874       ActiveScrolledRoot::PickDescendant(mContentClipASR, aActiveScrolledRoot);
875 
876   // mCurrentContainerASR is adjusted so that it's still an ancestor of
877   // finiteBoundsASR.
878   mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
879       mBuilder->mCurrentContainerASR, finiteBoundsASR);
880 
881   // If we are entering out-of-flow content inside a CSS filter, mark
882   // scroll frames wrt. which the content is fixed as containing such content.
883   if (mBuilder->mFilterASR && ActiveScrolledRoot::IsAncestor(
884                                   aActiveScrolledRoot, mBuilder->mFilterASR)) {
885     for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
886          asr && asr != aActiveScrolledRoot; asr = asr->mParent) {
887       asr->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
888     }
889   }
890 
891   mUsed = true;
892 }
893 
894 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
InsertScrollFrame(nsIScrollableFrame * aScrollableFrame)895     InsertScrollFrame(nsIScrollableFrame* aScrollableFrame) {
896   MOZ_ASSERT(!mUsed);
897   size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
898   const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot;
899   const ActiveScrolledRoot* asr =
900       mBuilder->AllocateActiveScrolledRoot(parentASR, aScrollableFrame);
901   mBuilder->mCurrentActiveScrolledRoot = asr;
902 
903   // All child ASRs of parentASR that were created while this
904   // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
905   // now. Reparent them to asr.
906   for (size_t i = mDescendantsStartIndex; i < descendantsEndIndex; i++) {
907     ActiveScrolledRoot* descendantASR = mBuilder->mActiveScrolledRoots[i];
908     if (ActiveScrolledRoot::IsAncestor(parentASR, descendantASR)) {
909       descendantASR->IncrementDepth();
910       if (descendantASR->mParent == parentASR) {
911         descendantASR->mParent = asr;
912       }
913     }
914   }
915 
916   mUsed = true;
917 }
918 
nsDisplayListBuilder(nsIFrame * aReferenceFrame,nsDisplayListBuilderMode aMode,bool aBuildCaret,bool aRetainingDisplayList)919 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
920                                            nsDisplayListBuilderMode aMode,
921                                            bool aBuildCaret,
922                                            bool aRetainingDisplayList)
923     : mReferenceFrame(aReferenceFrame),
924       mIgnoreScrollFrame(nullptr),
925       mLayerEventRegions(nullptr),
926       mCompositorHitTestInfo(nullptr),
927       mCurrentTableItem(nullptr),
928       mCurrentActiveScrolledRoot(nullptr),
929       mCurrentContainerASR(nullptr),
930       mCurrentFrame(aReferenceFrame),
931       mCurrentReferenceFrame(aReferenceFrame),
932       mRootAGR(AnimatedGeometryRoot::CreateAGRForFrame(
933           aReferenceFrame, nullptr, true, aRetainingDisplayList)),
934       mCurrentAGR(mRootAGR),
935       mUsedAGRBudget(0),
936       mDirtyRect(-1, -1, -1, -1),
937       mGlassDisplayItem(nullptr),
938       mScrollInfoItemsForHoisting(nullptr),
939       mActiveScrolledRootForRootScrollframe(nullptr),
940       mMode(aMode),
941       mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
942       mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
943       mCurrentScrollbarFlags(nsDisplayOwnLayerFlags::eNone),
944       mPerspectiveItemIndex(0),
945       mSVGEffectsBuildingDepth(0),
946       mFilterASR(nullptr),
947       mContainsBlendMode(false),
948       mIsBuildingScrollbar(false),
949       mCurrentScrollbarWillHaveLayer(false),
950       mBuildCaret(aBuildCaret),
951       mRetainingDisplayList(aRetainingDisplayList),
952       mPartialUpdate(false),
953       mIgnoreSuppression(false),
954       mIsAtRootOfPseudoStackingContext(false),
955       mIncludeAllOutOfFlows(false),
956       mDescendIntoSubdocuments(true),
957       mSelectedFramesOnly(false),
958       mAllowMergingAndFlattening(true),
959       mWillComputePluginGeometry(false),
960       mInTransform(false),
961       mIsInChromePresContext(false),
962       mSyncDecodeImages(false),
963       mIsPaintingToWindow(false),
964       mIsCompositingCheap(false),
965       mContainsPluginItem(false),
966       mAncestorHasApzAwareEventHandler(false),
967       mHaveScrollableDisplayPort(false),
968       mWindowDraggingAllowed(false),
969       mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
970       mForceLayerForScrollParent(false),
971       mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
972       mBuildingInvisibleItems(false),
973       mHitTestIsForVisibility(false),
974       mIsBuilding(false),
975       mInInvalidSubtree(false) {
976   MOZ_COUNT_CTOR(nsDisplayListBuilder);
977 
978   const bool useWRHitTest =
979       gfxPrefs::WebRenderHitTest() && gfxVars::UseWebRender();
980 
981   mBuildCompositorHitTestInfo =
982       mAsyncPanZoomEnabled && IsForPainting() &&
983       (useWRHitTest || gfxPrefs::SimpleEventRegionItems());
984 
985   mLessEventRegionItems = gfxPrefs::LessEventRegionItems();
986 
987   nsPresContext* pc = aReferenceFrame->PresContext();
988   nsIPresShell* shell = pc->PresShell();
989   if (pc->IsRenderingOnlySelection()) {
990     nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(shell));
991     if (selcon) {
992       selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
993                            getter_AddRefs(mBoundingSelection));
994     }
995   }
996 
997   static_assert(
998       static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
999       "Check TYPE_MAX should not overflow");
1000 }
1001 
BeginFrame()1002 void nsDisplayListBuilder::BeginFrame() {
1003   nsCSSRendering::BeginFrameTreesLocked();
1004   mCurrentAGR = mRootAGR;
1005   mFrameToAnimatedGeometryRootMap.Put(mReferenceFrame, mRootAGR);
1006 
1007   mIsPaintingToWindow = false;
1008   mIgnoreSuppression = false;
1009   mInTransform = false;
1010   mSyncDecodeImages = false;
1011 }
1012 
EndFrame()1013 void nsDisplayListBuilder::EndFrame() {
1014   NS_ASSERTION(!mInInvalidSubtree,
1015                "Someone forgot to cleanup mInInvalidSubtree!");
1016   mFrameToAnimatedGeometryRootMap.Clear();
1017   mActiveScrolledRoots.Clear();
1018   FreeClipChains();
1019   FreeTemporaryItems();
1020   nsCSSRendering::EndFrameTreesLocked();
1021 
1022   MOZ_ASSERT(!mLayerEventRegions);
1023   MOZ_ASSERT(!mCompositorHitTestInfo);
1024 }
1025 
MarkFrameForDisplay(nsIFrame * aFrame,nsIFrame * aStopAtFrame)1026 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
1027                                                nsIFrame* aStopAtFrame) {
1028   mFramesMarkedForDisplay.AppendElement(aFrame);
1029   for (nsIFrame* f = aFrame; f;
1030        f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
1031     if (f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) return;
1032     f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
1033     if (f == aStopAtFrame) {
1034       // we've reached a frame that we know will be painted, so we can stop.
1035       break;
1036     }
1037   }
1038 }
1039 
MarkFrameForDisplayIfVisible(nsIFrame * aFrame,nsIFrame * aStopAtFrame)1040 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
1041     nsIFrame* aFrame, nsIFrame* aStopAtFrame) {
1042   mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
1043   for (nsIFrame* f = aFrame; f;
1044        f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
1045     if (f->ForceDescendIntoIfVisible()) return;
1046     f->SetForceDescendIntoIfVisible(true);
1047     if (f == aStopAtFrame) {
1048       // we've reached a frame that we know will be painted, so we can stop.
1049       break;
1050     }
1051   }
1052 }
1053 
NeedToForceTransparentSurfaceForItem(nsDisplayItem * aItem)1054 bool nsDisplayListBuilder::NeedToForceTransparentSurfaceForItem(
1055     nsDisplayItem* aItem) {
1056   return aItem == mGlassDisplayItem || aItem->ClearsBackground();
1057 }
1058 
WrapAGRForFrame(nsIFrame * aAnimatedGeometryRoot,bool aIsAsync,AnimatedGeometryRoot * aParent)1059 AnimatedGeometryRoot* nsDisplayListBuilder::WrapAGRForFrame(
1060     nsIFrame* aAnimatedGeometryRoot, bool aIsAsync,
1061     AnimatedGeometryRoot* aParent /* = nullptr */) {
1062   DebugOnly<bool> dummy;
1063   MOZ_ASSERT(IsAnimatedGeometryRoot(aAnimatedGeometryRoot, dummy) == AGR_YES);
1064 
1065   RefPtr<AnimatedGeometryRoot> result;
1066   if (!mFrameToAnimatedGeometryRootMap.Get(aAnimatedGeometryRoot, &result)) {
1067     MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(RootReferenceFrame(),
1068                                                       aAnimatedGeometryRoot));
1069     RefPtr<AnimatedGeometryRoot> parent = aParent;
1070     if (!parent) {
1071       nsIFrame* parentFrame =
1072           nsLayoutUtils::GetCrossDocParentFrame(aAnimatedGeometryRoot);
1073       if (parentFrame) {
1074         bool isAsync;
1075         nsIFrame* parentAGRFrame =
1076             FindAnimatedGeometryRootFrameFor(parentFrame, isAsync);
1077         parent = WrapAGRForFrame(parentAGRFrame, isAsync);
1078       }
1079     }
1080     result = AnimatedGeometryRoot::CreateAGRForFrame(
1081         aAnimatedGeometryRoot, parent, aIsAsync, IsRetainingDisplayList());
1082     mFrameToAnimatedGeometryRootMap.Put(aAnimatedGeometryRoot, result);
1083   }
1084   MOZ_ASSERT(!aParent || result->mParentAGR == aParent);
1085   return result;
1086 }
1087 
AnimatedGeometryRootForASR(const ActiveScrolledRoot * aASR)1088 AnimatedGeometryRoot* nsDisplayListBuilder::AnimatedGeometryRootForASR(
1089     const ActiveScrolledRoot* aASR) {
1090   if (!aASR) {
1091     return GetRootAnimatedGeometryRoot();
1092   }
1093   nsIFrame* scrolledFrame = aASR->mScrollableFrame->GetScrolledFrame();
1094   return FindAnimatedGeometryRootFor(scrolledFrame);
1095 }
1096 
FindAnimatedGeometryRootFor(nsIFrame * aFrame)1097 AnimatedGeometryRoot* nsDisplayListBuilder::FindAnimatedGeometryRootFor(
1098     nsIFrame* aFrame) {
1099   if (!IsPaintingToWindow()) {
1100     return mRootAGR;
1101   }
1102   if (aFrame == mCurrentFrame) {
1103     return mCurrentAGR;
1104   }
1105   RefPtr<AnimatedGeometryRoot> result;
1106   if (mFrameToAnimatedGeometryRootMap.Get(aFrame, &result)) {
1107     return result;
1108   }
1109 
1110   bool isAsync;
1111   nsIFrame* agrFrame = FindAnimatedGeometryRootFrameFor(aFrame, isAsync);
1112   result = WrapAGRForFrame(agrFrame, isAsync);
1113   mFrameToAnimatedGeometryRootMap.Put(aFrame, result);
1114   return result;
1115 }
1116 
FindAnimatedGeometryRootFor(nsDisplayItem * aItem)1117 AnimatedGeometryRoot* nsDisplayListBuilder::FindAnimatedGeometryRootFor(
1118     nsDisplayItem* aItem) {
1119   if (aItem->ShouldFixToViewport(this)) {
1120     // Make its active scrolled root be the active scrolled root of
1121     // the enclosing viewport, since it shouldn't be scrolled by scrolled
1122     // frames in its document. InvalidateFixedBackgroundFramesFromList in
1123     // nsGfxScrollFrame will not repaint this item when scrolling occurs.
1124     nsIFrame* viewportFrame = nsLayoutUtils::GetClosestFrameOfType(
1125         aItem->Frame(), LayoutFrameType::Viewport, RootReferenceFrame());
1126     if (viewportFrame) {
1127       return FindAnimatedGeometryRootFor(viewportFrame);
1128     }
1129   }
1130   return FindAnimatedGeometryRootFor(aItem->Frame());
1131 }
1132 
MarkOutOfFlowFrameForDisplay(nsIFrame * aDirtyFrame,nsIFrame * aFrame)1133 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame,
1134                                                         nsIFrame* aFrame) {
1135   MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
1136   nsRect dirty;
1137   nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
1138       this, aFrame, GetVisibleRect(), GetDirtyRect(), &dirty);
1139   if (!(aFrame->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
1140       visible.IsEmpty()) {
1141     return false;
1142   }
1143 
1144   // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
1145   // frame, then it will also mark any outer frames to ensure that building
1146   // reaches the dirty feame.
1147   if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
1148     MarkFrameForDisplay(aFrame, aDirtyFrame);
1149   }
1150 
1151   return true;
1152 }
1153 
UnmarkFrameForDisplay(nsIFrame * aFrame,nsIFrame * aStopAtFrame)1154 static void UnmarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame) {
1155   for (nsIFrame* f = aFrame; f;
1156        f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
1157     if (!(f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) return;
1158     f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
1159     if (f == aStopAtFrame) {
1160       // we've reached a frame that we know will be painted, so we can stop.
1161       break;
1162     }
1163   }
1164 }
1165 
UnmarkFrameForDisplayIfVisible(nsIFrame * aFrame)1166 static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
1167   for (nsIFrame* f = aFrame; f;
1168        f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
1169     if (!f->ForceDescendIntoIfVisible()) return;
1170     f->SetForceDescendIntoIfVisible(false);
1171   }
1172 }
1173 
~nsDisplayListBuilder()1174 nsDisplayListBuilder::~nsDisplayListBuilder() {
1175   NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
1176                "All frames should have been unmarked");
1177   NS_ASSERTION(mFramesWithOOFData.Length() == 0,
1178                "All OOF data should have been removed");
1179   NS_ASSERTION(mPresShellStates.Length() == 0,
1180                "All presshells should have been exited");
1181   NS_ASSERTION(!mCurrentTableItem, "No table item should be active");
1182 
1183   for (DisplayItemClipChain* c : mClipChainsToDestroy) {
1184     c->DisplayItemClipChain::~DisplayItemClipChain();
1185   }
1186 
1187   MOZ_COUNT_DTOR(nsDisplayListBuilder);
1188 }
1189 
GetBackgroundPaintFlags()1190 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
1191   uint32_t flags = 0;
1192   if (mSyncDecodeImages) {
1193     flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
1194   }
1195   if (mIsPaintingToWindow) {
1196     flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
1197   }
1198   return flags;
1199 }
1200 
SubtractFromVisibleRegion(nsRegion * aVisibleRegion,const nsRegion & aRegion)1201 void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
1202                                                      const nsRegion& aRegion) {
1203   if (aRegion.IsEmpty()) return;
1204 
1205   nsRegion tmp;
1206   tmp.Sub(*aVisibleRegion, aRegion);
1207   // Don't let *aVisibleRegion get too complex, but don't let it fluff out
1208   // to its bounds either, which can be very bad (see bug 516740).
1209   // Do let aVisibleRegion get more complex if by doing so we reduce its
1210   // area by at least half.
1211   if (GetAccurateVisibleRegions() || tmp.GetNumRects() <= 15 ||
1212       tmp.Area() <= aVisibleRegion->Area() / 2) {
1213     *aVisibleRegion = tmp;
1214   }
1215 }
1216 
GetCaret()1217 nsCaret* nsDisplayListBuilder::GetCaret() {
1218   RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
1219   return caret;
1220 }
1221 
IncrementPresShellPaintCount(nsIPresShell * aPresShell)1222 void nsDisplayListBuilder::IncrementPresShellPaintCount(
1223     nsIPresShell* aPresShell) {
1224   if (mIsPaintingToWindow) {
1225     mReferenceFrame->AddPaintedPresShell(aPresShell);
1226     aPresShell->IncrementPaintCount();
1227   }
1228 }
1229 
EnterPresShell(nsIFrame * aReferenceFrame,bool aPointerEventsNoneDoc)1230 void nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
1231                                           bool aPointerEventsNoneDoc) {
1232   PresShellState* state = mPresShellStates.AppendElement();
1233   state->mPresShell = aReferenceFrame->PresShell();
1234   state->mCaretFrame = nullptr;
1235   state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
1236   state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
1237 
1238   nsIScrollableFrame* sf = state->mPresShell->GetRootScrollFrameAsScrollable();
1239   if (sf) {
1240     // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
1241     // that the canvas background color will be set correctly, and that only one
1242     // unscrollable item will be created.
1243     // This is done to avoid, for example, a case where only scrollbar frames
1244     // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
1245     // and possibly end up with an extra nsDisplaySolidColor item.
1246     nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
1247     if (canvasFrame) {
1248       MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
1249     }
1250   }
1251 
1252 #ifdef DEBUG
1253   state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
1254                                   eLayoutPhase_DisplayListBuilding);
1255 #endif
1256 
1257   state->mPresShell->UpdateCanvasBackground();
1258 
1259   bool buildCaret = mBuildCaret;
1260   if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
1261     state->mIsBackgroundOnly = false;
1262   } else {
1263     state->mIsBackgroundOnly = true;
1264     buildCaret = false;
1265   }
1266 
1267   bool pointerEventsNone = aPointerEventsNoneDoc;
1268   if (IsInSubdocument()) {
1269     pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
1270                              .mInsidePointerEventsNoneDoc;
1271   }
1272   state->mInsidePointerEventsNoneDoc = pointerEventsNone;
1273 
1274   if (!buildCaret) return;
1275 
1276   RefPtr<nsCaret> caret = state->mPresShell->GetCaret();
1277   state->mCaretFrame = caret->GetPaintGeometry(&state->mCaretRect);
1278   if (state->mCaretFrame) {
1279     MarkFrameForDisplay(state->mCaretFrame, aReferenceFrame);
1280   }
1281 
1282   nsPresContext* pc = aReferenceFrame->PresContext();
1283   nsCOMPtr<nsIDocShell> docShell = pc->GetDocShell();
1284   if (docShell) {
1285     docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1286   }
1287   mIsInChromePresContext = pc->IsChrome();
1288 }
1289 
1290 // A non-blank paint is a paint that does not just contain the canvas
1291 // background.
DisplayListIsNonBlank(nsDisplayList * aList)1292 static bool DisplayListIsNonBlank(nsDisplayList* aList) {
1293   for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) {
1294     switch (i->GetType()) {
1295       case DisplayItemType::TYPE_LAYER_EVENT_REGIONS:
1296       case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
1297       case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
1298       case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
1299         continue;
1300       case DisplayItemType::TYPE_SOLID_COLOR:
1301       case DisplayItemType::TYPE_BACKGROUND:
1302       case DisplayItemType::TYPE_BACKGROUND_COLOR:
1303         if (i->Frame()->IsCanvasFrame()) {
1304           continue;
1305         }
1306         return true;
1307       default:
1308         return true;
1309     }
1310   }
1311   return false;
1312 }
1313 
LeavePresShell(nsIFrame * aReferenceFrame,nsDisplayList * aPaintedContents)1314 void nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame,
1315                                           nsDisplayList* aPaintedContents) {
1316   NS_ASSERTION(
1317       CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
1318       "Presshell mismatch");
1319 
1320   if (mIsPaintingToWindow) {
1321     nsPresContext* pc = aReferenceFrame->PresContext();
1322     if (!pc->HadNonBlankPaint()) {
1323       if (!CurrentPresShellState()->mIsBackgroundOnly &&
1324           DisplayListIsNonBlank(aPaintedContents)) {
1325         pc->NotifyNonBlankPaint();
1326       }
1327     }
1328   }
1329 
1330   ResetMarkedFramesForDisplayList(aReferenceFrame);
1331   mPresShellStates.SetLength(mPresShellStates.Length() - 1);
1332 
1333   if (!mPresShellStates.IsEmpty()) {
1334     nsPresContext* pc = CurrentPresContext();
1335     nsCOMPtr<nsIDocShell> docShell = pc->GetDocShell();
1336     if (docShell) {
1337       docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
1338     }
1339     mIsInChromePresContext = pc->IsChrome();
1340   } else {
1341     mCurrentAGR = mRootAGR;
1342 
1343     for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
1344       UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
1345     }
1346     mFramesMarkedForDisplayIfVisible.SetLength(0);
1347   }
1348 }
1349 
FreeClipChains()1350 void nsDisplayListBuilder::FreeClipChains() {
1351   // Iterate the clip chains from newest to oldest (forward
1352   // iteration), so that we destroy descendants first which
1353   // will drop the ref count on their ancestors.
1354   auto it = mClipChainsToDestroy.begin();
1355 
1356   while (it != mClipChainsToDestroy.end()) {
1357     DisplayItemClipChain* clip = *it;
1358 
1359     if (!clip->mRefCount) {
1360       mClipDeduplicator.erase(clip);
1361       it = mClipChainsToDestroy.erase(it);
1362       clip->DisplayItemClipChain::~DisplayItemClipChain();
1363       Destroy(DisplayItemType::TYPE_ZERO, clip);
1364     } else {
1365       ++it;
1366     }
1367   }
1368 }
1369 
FreeTemporaryItems()1370 void nsDisplayListBuilder::FreeTemporaryItems() {
1371   for (nsDisplayItem* i : mTemporaryItems) {
1372     // Temporary display items are not added to the frames.
1373     MOZ_ASSERT(i->Frame());
1374     i->RemoveFrame(i->Frame());
1375     i->Destroy(this);
1376   }
1377 
1378   mTemporaryItems.Clear();
1379 }
1380 
ResetMarkedFramesForDisplayList(nsIFrame * aReferenceFrame)1381 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
1382     nsIFrame* aReferenceFrame) {
1383   // Unmark and pop off the frames marked for display in this pres shell.
1384   uint32_t firstFrameForShell =
1385       CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1386   for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
1387        ++i) {
1388     UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
1389   }
1390   mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1391 
1392   firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
1393   for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
1394     mFramesWithOOFData[i]->DeleteProperty(OutOfFlowDisplayDataProperty());
1395   }
1396   mFramesWithOOFData.SetLength(firstFrameForShell);
1397 }
1398 
ClearFixedBackgroundDisplayData()1399 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
1400   CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
1401 }
1402 
MarkFramesForDisplayList(nsIFrame * aDirtyFrame,const nsFrameList & aFrames)1403 void nsDisplayListBuilder::MarkFramesForDisplayList(
1404     nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
1405   bool markedFrames = false;
1406   for (nsIFrame* e : aFrames) {
1407     // Skip the AccessibleCaret frame when building no caret.
1408     if (!IsBuildingCaret()) {
1409       nsIContent* content = e->GetContent();
1410       if (content && content->IsInNativeAnonymousSubtree() &&
1411           content->IsElement()) {
1412         auto classList = content->AsElement()->ClassList();
1413         if (classList->Contains(NS_LITERAL_STRING("moz-accessiblecaret"))) {
1414           continue;
1415         }
1416       }
1417     }
1418     if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e)) {
1419       markedFrames = true;
1420     }
1421   }
1422 
1423   if (markedFrames) {
1424     // mClipState.GetClipChainForContainingBlockDescendants can return pointers
1425     // to objects on the stack, so we need to clone the chain.
1426     const DisplayItemClipChain* clipChain =
1427         CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1428     const DisplayItemClipChain* combinedClipChain =
1429         mClipState.GetCurrentCombinedClipChain(this);
1430     const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1431     OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
1432         clipChain, combinedClipChain, asr, GetVisibleRect(), GetDirtyRect());
1433     aDirtyFrame->SetProperty(
1434         nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
1435     mFramesWithOOFData.AppendElement(aDirtyFrame);
1436   }
1437 
1438   if (!aDirtyFrame->GetParent()) {
1439     // This is the viewport frame of aDirtyFrame's presshell.
1440     // Store the current display data so that it can be used for fixed
1441     // background images.
1442     NS_ASSERTION(
1443         CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(),
1444         "Presshell mismatch");
1445     MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData,
1446                "already traversed this presshell's root frame?");
1447 
1448     const DisplayItemClipChain* clipChain =
1449         CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
1450     const DisplayItemClipChain* combinedClipChain =
1451         mClipState.GetCurrentCombinedClipChain(this);
1452     const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
1453     CurrentPresShellState()->mFixedBackgroundDisplayData.emplace(
1454         clipChain, combinedClipChain, asr, GetVisibleRect(), GetDirtyRect());
1455   }
1456 }
1457 
1458 /**
1459  * Mark all preserve-3d children with
1460  * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
1461  * nsFrame::BuildDisplayListForChild() would visit them.  Also compute
1462  * dirty rect for preserve-3d children.
1463  *
1464  * @param aDirtyFrame is the frame to mark children extending context.
1465  */
MarkPreserve3DFramesForDisplayList(nsIFrame * aDirtyFrame)1466 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
1467     nsIFrame* aDirtyFrame) {
1468   AutoTArray<nsIFrame::ChildList, 4> childListArray;
1469   aDirtyFrame->GetChildLists(&childListArray);
1470   nsIFrame::ChildListArrayIterator lists(childListArray);
1471   for (; !lists.IsDone(); lists.Next()) {
1472     nsFrameList::Enumerator childFrames(lists.CurrentList());
1473     for (; !childFrames.AtEnd(); childFrames.Next()) {
1474       nsIFrame* child = childFrames.get();
1475       if (child->Combines3DTransformWithAncestors()) {
1476         MarkFrameForDisplay(child, aDirtyFrame);
1477       }
1478     }
1479   }
1480 }
1481 
1482 uint32_t gDisplayItemSizes[static_cast<uint32_t>(DisplayItemType::TYPE_MAX)] = {
1483     0};
1484 
Allocate(size_t aSize,DisplayItemType aType)1485 void* nsDisplayListBuilder::Allocate(size_t aSize, DisplayItemType aType) {
1486   size_t roundedUpSize = RoundUpPow2(aSize);
1487   uint_fast8_t type = FloorLog2Size(roundedUpSize);
1488 
1489   MOZ_RELEASE_ASSERT(gDisplayItemSizes[static_cast<uint32_t>(aType)] == type ||
1490                      gDisplayItemSizes[static_cast<uint32_t>(aType)] == 0);
1491   gDisplayItemSizes[static_cast<uint32_t>(aType)] = type;
1492   return mPool.AllocateByCustomID(type, roundedUpSize);
1493 }
1494 
Destroy(DisplayItemType aType,void * aPtr)1495 void nsDisplayListBuilder::Destroy(DisplayItemType aType, void* aPtr) {
1496   mPool.FreeByCustomID(gDisplayItemSizes[static_cast<uint32_t>(aType)], aPtr);
1497 }
1498 
AllocateActiveScrolledRoot(const ActiveScrolledRoot * aParent,nsIScrollableFrame * aScrollableFrame)1499 ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot(
1500     const ActiveScrolledRoot* aParent, nsIScrollableFrame* aScrollableFrame) {
1501   RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForFrame(
1502       aParent, aScrollableFrame, IsRetainingDisplayList());
1503   mActiveScrolledRoots.AppendElement(asr);
1504   return asr;
1505 }
1506 
AllocateDisplayItemClipChain(const DisplayItemClip & aClip,const ActiveScrolledRoot * aASR,const DisplayItemClipChain * aParent)1507 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain(
1508     const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR,
1509     const DisplayItemClipChain* aParent) {
1510   MOZ_ASSERT(!(aParent && aParent->mOnStack));
1511   void* p = Allocate(sizeof(DisplayItemClipChain), DisplayItemType::TYPE_ZERO);
1512   DisplayItemClipChain* c =
1513       new (KnownNotNull, p) DisplayItemClipChain(aClip, aASR, aParent);
1514 #ifdef DEBUG
1515   c->mOnStack = false;
1516 #endif
1517   auto result = mClipDeduplicator.insert(c);
1518   if (!result.second) {
1519     // An equivalent clip chain item was already created, so let's return that
1520     // instead. Destroy the one we just created.
1521     // Note that this can cause clip chains from different coordinate systems to
1522     // collapse into the same clip chain object, because clip chains do not keep
1523     // track of the reference frame that they were created in.
1524     c->DisplayItemClipChain::~DisplayItemClipChain();
1525     Destroy(DisplayItemType::TYPE_ZERO, c);
1526     return *(result.first);
1527   }
1528   mClipChainsToDestroy.emplace_front(c);
1529   return c;
1530 }
1531 
1532 struct ClipChainItem {
1533   DisplayItemClip clip;
1534   const ActiveScrolledRoot* asr;
1535 };
1536 
CreateClipChainIntersection(const DisplayItemClipChain * aAncestor,const DisplayItemClipChain * aLeafClip1,const DisplayItemClipChain * aLeafClip2)1537 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection(
1538     const DisplayItemClipChain* aAncestor,
1539     const DisplayItemClipChain* aLeafClip1,
1540     const DisplayItemClipChain* aLeafClip2) {
1541   AutoTArray<ClipChainItem, 8> intersectedClips;
1542 
1543   const DisplayItemClipChain* clip1 = aLeafClip1;
1544   const DisplayItemClipChain* clip2 = aLeafClip2;
1545 
1546   const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant(
1547       clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr);
1548 
1549   // Build up the intersection from the leaf to the root and put it into
1550   // intersectedClips. The loop below will convert intersectedClips into an
1551   // actual DisplayItemClipChain.
1552   // (We need to do this in two passes because we need the parent clip in order
1553   // to create the DisplayItemClipChain object, but the parent clip has not
1554   // been created at that point.)
1555   while (!aAncestor || asr != aAncestor->mASR) {
1556     if (clip1 && clip1->mASR == asr) {
1557       if (clip2 && clip2->mASR == asr) {
1558         DisplayItemClip intersection = clip1->mClip;
1559         intersection.IntersectWith(clip2->mClip);
1560         intersectedClips.AppendElement(ClipChainItem{intersection, asr});
1561         clip2 = clip2->mParent;
1562       } else {
1563         intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
1564       }
1565       clip1 = clip1->mParent;
1566     } else if (clip2 && clip2->mASR == asr) {
1567       intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
1568       clip2 = clip2->mParent;
1569     }
1570     if (!asr) {
1571       MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier");
1572       break;
1573     }
1574     asr = asr->mParent;
1575   }
1576 
1577   // Convert intersectedClips into a DisplayItemClipChain.
1578   const DisplayItemClipChain* parentSC = aAncestor;
1579   for (auto& sc : Reversed(intersectedClips)) {
1580     parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
1581   }
1582   return parentSC;
1583 }
1584 
CopyWholeChain(const DisplayItemClipChain * aClipChain)1585 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain(
1586     const DisplayItemClipChain* aClipChain) {
1587   return CreateClipChainIntersection(nullptr, aClipChain, nullptr);
1588 }
1589 
FindReferenceFrameFor(const nsIFrame * aFrame,nsPoint * aOffset) const1590 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor(
1591     const nsIFrame* aFrame, nsPoint* aOffset) const {
1592   if (aFrame == mCurrentFrame) {
1593     if (aOffset) {
1594       *aOffset = mCurrentOffsetToReferenceFrame;
1595     }
1596     return mCurrentReferenceFrame;
1597   }
1598   for (const nsIFrame* f = aFrame; f;
1599        f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
1600     if (f == mReferenceFrame || f->IsTransformed()) {
1601       if (aOffset) {
1602         *aOffset = aFrame->GetOffsetToCrossDoc(f);
1603       }
1604       return f;
1605     }
1606   }
1607   if (aOffset) {
1608     *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1609   }
1610   return mReferenceFrame;
1611 }
1612 
1613 // Sticky frames are active if their nearest scrollable frame is also active.
IsStickyFrameActive(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsIFrame * aParent)1614 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
1615                                 nsIFrame* aFrame, nsIFrame* aParent) {
1616   MOZ_ASSERT(aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY);
1617 
1618   // Find the nearest scrollframe.
1619   nsIFrame* cursor = aFrame;
1620   nsIFrame* parent = aParent;
1621   if (!parent) {
1622     parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
1623   }
1624   while (!parent->IsScrollFrame()) {
1625     cursor = parent;
1626     if ((parent = nsLayoutUtils::GetCrossDocParentFrame(cursor)) == nullptr) {
1627       return false;
1628     }
1629   }
1630 
1631   nsIScrollableFrame* sf = do_QueryFrame(parent);
1632   return sf->IsScrollingActive(aBuilder) && sf->GetScrolledFrame() == cursor;
1633 }
1634 
IsAnimatedGeometryRoot(nsIFrame * aFrame,bool & aIsAsync,nsIFrame ** aParent)1635 nsDisplayListBuilder::AGRState nsDisplayListBuilder::IsAnimatedGeometryRoot(
1636     nsIFrame* aFrame, bool& aIsAsync, nsIFrame** aParent) {
1637   aIsAsync = false;
1638   if (aFrame == mReferenceFrame) {
1639     aIsAsync = true;
1640     return AGR_YES;
1641   }
1642   if (!IsPaintingToWindow()) {
1643     if (aParent) {
1644       *aParent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
1645     }
1646     return AGR_NO;
1647   }
1648 
1649   nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
1650   if (!parent) {
1651     aIsAsync = true;
1652     return AGR_YES;
1653   }
1654 
1655   AGRState result = AGR_NO;  // Possible to transition from not being an AGR
1656                              // to being an AGR without a style change.
1657 
1658   LayoutFrameType parentType = parent->Type();
1659 
1660   if (aFrame->IsTransformed()) {
1661     aIsAsync = EffectCompositor::HasAnimationsForCompositor(
1662         aFrame, eCSSProperty_transform);
1663     result = AGR_YES;
1664   }
1665 
1666   if (parentType == LayoutFrameType::Scroll ||
1667       parentType == LayoutFrameType::ListControl) {
1668     nsIScrollableFrame* sf = do_QueryFrame(parent);
1669     if (sf->GetScrolledFrame() == aFrame) {
1670       if (sf->IsScrollingActive(this)) {
1671         aIsAsync = aIsAsync || sf->IsMaybeAsynchronouslyScrolled();
1672         result = AGR_YES;
1673       } else {
1674         result = AGR_MAYBE;
1675       }
1676     }
1677   }
1678 
1679   // Finished checking all conditions that might set aIsAsync, so we can
1680   // early return now.
1681   if (result == AGR_YES) {
1682     return result;
1683   }
1684 
1685   if (nsLayoutUtils::IsPopup(aFrame)) return AGR_YES;
1686   if (ActiveLayerTracker::IsOffsetStyleAnimated(aFrame)) {
1687     const bool inBudget = AddToAGRBudget(aFrame);
1688     if (inBudget) {
1689       return AGR_YES;
1690     }
1691   }
1692   if (!aFrame->GetParent() &&
1693       nsLayoutUtils::ViewportHasDisplayPort(aFrame->PresContext())) {
1694     // Viewport frames in a display port need to be animated geometry roots
1695     // for background-attachment:fixed elements.
1696     return AGR_YES;
1697   }
1698 
1699   // Treat the slider thumb as being as an active scrolled root when it wants
1700   // its own layer so that it can move without repainting.
1701   if (parentType == LayoutFrameType::Slider) {
1702     nsIScrollableFrame* sf =
1703         static_cast<nsSliderFrame*>(parent)->GetScrollFrame();
1704     // The word "Maybe" in IsMaybeScrollingActive might be confusing but we do
1705     // indeed need to always consider scroll thumbs as AGRs if
1706     // IsMaybeScrollingActive is true because that is the same condition we use
1707     // in ScrollFrameHelper::AppendScrollPartsTo to layerize scroll thumbs.
1708     if (sf && sf->IsMaybeScrollingActive()) {
1709       return AGR_YES;
1710     }
1711     result = AGR_MAYBE;
1712   }
1713 
1714   if (aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY) {
1715     if (IsStickyFrameActive(this, aFrame, parent)) {
1716       return AGR_YES;
1717     }
1718     result = AGR_MAYBE;
1719   }
1720 
1721   // Fixed-pos frames are parented by the viewport frame, which has no parent.
1722   if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame)) {
1723     return AGR_YES;
1724   }
1725 
1726   if ((aFrame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED) &&
1727       aFrame->IsFrameOfType(nsIFrame::eSVG)) {
1728     // For SVG containers, they always have
1729     // NS_FRAME_MAY_BE_TRANSFORMED bit.  However, they would be
1730     // affected by the fragement identifiers in the svgView form at
1731     // runtime without a new style context.
1732     // For example, layout/reftests/svg/fragmentIdentifier-01.xhtml
1733     //
1734     // see https://www.w3.org/TR/SVG/linking.html#SVGFragmentIdentifiers
1735     result = AGR_MAYBE;
1736   }
1737 
1738   if (aParent) {
1739     *aParent = parent;
1740   }
1741   return result;
1742 }
1743 
FindAnimatedGeometryRootFrameFor(nsIFrame * aFrame,bool & aIsAsync)1744 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(
1745     nsIFrame* aFrame, bool& aIsAsync) {
1746   MOZ_ASSERT(
1747       nsLayoutUtils::IsAncestorFrameCrossDoc(RootReferenceFrame(), aFrame));
1748   nsIFrame* cursor = aFrame;
1749   while (cursor != RootReferenceFrame()) {
1750     nsIFrame* next;
1751     if (IsAnimatedGeometryRoot(cursor, aIsAsync, &next) == AGR_YES)
1752       return cursor;
1753     cursor = next;
1754   }
1755   // Root frame is always an async agr.
1756   aIsAsync = true;
1757   return cursor;
1758 }
1759 
RecomputeCurrentAnimatedGeometryRoot()1760 void nsDisplayListBuilder::RecomputeCurrentAnimatedGeometryRoot() {
1761   bool isAsync;
1762   if (*mCurrentAGR != mCurrentFrame &&
1763       IsAnimatedGeometryRoot(const_cast<nsIFrame*>(mCurrentFrame), isAsync) ==
1764           AGR_YES) {
1765     AnimatedGeometryRoot* oldAGR = mCurrentAGR;
1766     mCurrentAGR = WrapAGRForFrame(const_cast<nsIFrame*>(mCurrentFrame), isAsync,
1767                                   mCurrentAGR);
1768 
1769     // Iterate the AGR cache and look for any objects that reference the old AGR
1770     // and check to see if they need to be updated. AGRs can be in the cache
1771     // multiple times, so we may end up doing the work multiple times for AGRs
1772     // that don't change.
1773     for (auto iter = mFrameToAnimatedGeometryRootMap.Iter(); !iter.Done();
1774          iter.Next()) {
1775       RefPtr<AnimatedGeometryRoot> cached = iter.UserData();
1776       if (cached->mParentAGR == oldAGR && cached != mCurrentAGR) {
1777         // It's possible that this cached AGR struct that has the old AGR as a
1778         // parent should instead have mCurrentFrame has a parent.
1779         nsIFrame* parent = FindAnimatedGeometryRootFrameFor(*cached, isAsync);
1780         MOZ_ASSERT(parent == mCurrentFrame || parent == *oldAGR);
1781         if (parent == mCurrentFrame) {
1782           cached->mParentAGR = mCurrentAGR;
1783         }
1784       }
1785     }
1786   }
1787 }
1788 
ApplyAllClipNonRoundedIntersection(const DisplayItemClipChain * aClipChain,const nsRect & aRect)1789 static nsRect ApplyAllClipNonRoundedIntersection(
1790     const DisplayItemClipChain* aClipChain, const nsRect& aRect) {
1791   nsRect result = aRect;
1792   while (aClipChain) {
1793     result = aClipChain->mClip.ApplyNonRoundedIntersection(result);
1794     aClipChain = aClipChain->mParent;
1795   }
1796   return result;
1797 }
1798 
AdjustWindowDraggingRegion(nsIFrame * aFrame)1799 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) {
1800   if (!mWindowDraggingAllowed || !IsForPainting()) {
1801     return;
1802   }
1803 
1804   const nsStyleUIReset* styleUI = aFrame->StyleUIReset();
1805   if (styleUI->mWindowDragging == StyleWindowDragging::Default) {
1806     // This frame has the default value and doesn't influence the window
1807     // dragging region.
1808     return;
1809   }
1810 
1811   LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame;
1812 
1813   // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1814   nsIFrame* referenceFrame =
1815       const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1816 
1817   if (IsInTransform()) {
1818     // Only support 2d rectilinear transforms. Transform support is needed for
1819     // the horizontal flip transform that's applied to the urlbar textbox in
1820     // RTL mode - it should be able to exclude itself from the draggable region.
1821     referenceFrameToRootReferenceFrame =
1822         ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
1823             nsLayoutUtils::GetTransformToAncestor(referenceFrame,
1824                                                   mReferenceFrame)
1825                 .GetMatrix());
1826     Matrix referenceFrameToRootReferenceFrame2d;
1827     if (!referenceFrameToRootReferenceFrame.Is2D(
1828             &referenceFrameToRootReferenceFrame2d) ||
1829         !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1830       return;
1831     }
1832   } else {
1833     MOZ_ASSERT(referenceFrame == mReferenceFrame,
1834                "referenceFrameToRootReferenceFrame needs to be adjusted");
1835   }
1836 
1837   // We do some basic visibility checking on the frame's border box here.
1838   // We intersect it both with the current dirty rect and with the current
1839   // clip. Either one is just a conservative approximation on its own, but
1840   // their intersection luckily works well enough for our purposes, so that
1841   // we don't have to do full-blown visibility computations.
1842   // The most important case we need to handle is the scrolled-off tab:
1843   // If the tab bar overflows, tab parts that are clipped by the scrollbox
1844   // should not be allowed to interfere with the window dragging region. Using
1845   // just the current DisplayItemClip is not enough to cover this case
1846   // completely because clips are reset while building stacking context
1847   // contents, so for example we'd fail to clip frames that have a clip path
1848   // applied to them. But the current dirty rect doesn't get reset in that
1849   // case, so we use it to make this case work.
1850   nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
1851   borderBox += ToReferenceFrame(aFrame);
1852   const DisplayItemClipChain* clip =
1853       ClipState().GetCurrentCombinedClipChain(this);
1854   borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox);
1855   if (borderBox.IsEmpty()) {
1856     return;
1857   }
1858 
1859   LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits(
1860       borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1861 
1862   LayoutDeviceRect transformedDevPixelBorderBox =
1863       TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1864   transformedDevPixelBorderBox.Round();
1865   LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1866 
1867   if (!transformedDevPixelBorderBox.ToIntRect(
1868           &transformedDevPixelBorderBoxInt)) {
1869     return;
1870   }
1871 
1872   LayoutDeviceIntRegion& region =
1873       styleUI->mWindowDragging == StyleWindowDragging::Drag
1874           ? mWindowDraggingRegion
1875           : mWindowNoDraggingRegion;
1876 
1877   if (!IsRetainingDisplayList()) {
1878     region.OrWith(transformedDevPixelBorderBoxInt);
1879     return;
1880   }
1881 
1882   mozilla::gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect());
1883   if (styleUI->mWindowDragging == StyleWindowDragging::Drag) {
1884     mRetainedWindowDraggingRegion.Add(aFrame, rect);
1885   } else {
1886     mRetainedWindowNoDraggingRegion.Add(aFrame, rect);
1887   }
1888 }
1889 
GetWindowDraggingRegion() const1890 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const {
1891   LayoutDeviceIntRegion result;
1892   if (!IsRetainingDisplayList()) {
1893     result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);
1894     return result;
1895   }
1896 
1897   LayoutDeviceIntRegion dragRegion =
1898       mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion();
1899 
1900   LayoutDeviceIntRegion noDragRegion =
1901       mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion();
1902 
1903   result.Sub(dragRegion, noDragRegion);
1904   return result;
1905 }
1906 
1907 /**
1908  * Removes modified frames and rects from |aRegion|.
1909  */
RemoveModifiedFramesAndRects(nsDisplayListBuilder::WeakFrameRegion & aRegion)1910 static void RemoveModifiedFramesAndRects(
1911     nsDisplayListBuilder::WeakFrameRegion& aRegion) {
1912   std::vector<WeakFrame>& frames = aRegion.mFrames;
1913   nsTArray<pixman_box32_t>& rects = aRegion.mRects;
1914 
1915   MOZ_ASSERT(frames.size() == rects.Length());
1916 
1917   uint32_t i = 0;
1918   uint32_t length = frames.size();
1919 
1920   while (i < length) {
1921     WeakFrame& frame = frames[i];
1922 
1923     if (!frame.IsAlive() || frame->IsFrameModified()) {
1924       // To avoid O(n) shifts in the array, move the last element of the array
1925       // to the current position and decrease the array length. Moving WeakFrame
1926       // inside of the array causes a new WeakFrame to be created and registered
1927       // with PresShell. We could avoid this by, for example, using a wrapper
1928       // class for WeakFrame, or by storing raw  WeakFrame pointers.
1929       frames[i] = frames[length - 1];
1930       rects[i] = rects[length - 1];
1931       length--;
1932     } else {
1933       i++;
1934     }
1935   }
1936 
1937   frames.resize(length);
1938   rects.TruncateLength(length);
1939 }
1940 
RemoveModifiedWindowRegions()1941 void nsDisplayListBuilder::RemoveModifiedWindowRegions() {
1942   RemoveModifiedFramesAndRects(mRetainedWindowDraggingRegion);
1943   RemoveModifiedFramesAndRects(mRetainedWindowNoDraggingRegion);
1944   RemoveModifiedFramesAndRects(mWindowExcludeGlassRegion);
1945 }
1946 
ClearRetainedWindowRegions()1947 void nsDisplayListBuilder::ClearRetainedWindowRegions() {
1948   mRetainedWindowDraggingRegion.Clear();
1949   mRetainedWindowNoDraggingRegion.Clear();
1950   mWindowExcludeGlassRegion.Clear();
1951 }
1952 
1953 const uint32_t gWillChangeAreaMultiplier = 3;
GetLayerizationCost(const nsSize & aSize)1954 static uint32_t GetLayerizationCost(const nsSize& aSize) {
1955   // There's significant overhead for each layer created from Gecko
1956   // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1957   // Therefore we set a minimum cost threshold of a 64x64 area.
1958   int minBudgetCost = 64 * 64;
1959 
1960   uint32_t budgetCost = std::max(
1961       minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) *
1962                          nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
1963 
1964   return budgetCost;
1965 }
1966 
AddToWillChangeBudget(nsIFrame * aFrame,const nsSize & aSize)1967 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
1968                                                  const nsSize& aSize) {
1969   if (mWillChangeBudgetSet.Get(aFrame, nullptr)) {
1970     return true;  // Already accounted
1971   }
1972 
1973   nsPresContext* key = aFrame->PresContext();
1974   DocumentWillChangeBudget budget;
1975   auto willChangeBudgetEntry = mWillChangeBudget.LookupForAdd(key);
1976   if (willChangeBudgetEntry) {
1977     // We have an existing entry.
1978     budget = willChangeBudgetEntry.Data();
1979   } else {
1980     budget = DocumentWillChangeBudget();
1981     willChangeBudgetEntry.OrInsert([&budget]() { return budget; });
1982   }
1983 
1984   nsRect area = aFrame->PresContext()->GetVisibleArea();
1985   uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1986                          nsPresContext::AppUnitsToIntCSSPixels(area.height);
1987 
1988   uint32_t cost = GetLayerizationCost(aSize);
1989   bool onBudget =
1990       (budget.mBudget + cost) / gWillChangeAreaMultiplier < budgetLimit;
1991 
1992   if (onBudget) {
1993     budget.mBudget += cost;
1994     willChangeBudgetEntry.Data() = budget;
1995     mWillChangeBudgetSet.Put(aFrame, cost);
1996     aFrame->SetMayHaveWillChangeBudget(true);
1997   }
1998 
1999   return onBudget;
2000 }
2001 
IsInWillChangeBudget(nsIFrame * aFrame,const nsSize & aSize)2002 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
2003                                                 const nsSize& aSize) {
2004   bool onBudget = AddToWillChangeBudget(aFrame, aSize);
2005 
2006   if (!onBudget) {
2007     nsString usageStr;
2008     usageStr.AppendInt(GetLayerizationCost(aSize));
2009 
2010     nsString multiplierStr;
2011     multiplierStr.AppendInt(gWillChangeAreaMultiplier);
2012 
2013     nsString limitStr;
2014     nsRect area = aFrame->PresContext()->GetVisibleArea();
2015     uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
2016                            nsPresContext::AppUnitsToIntCSSPixels(area.height);
2017     limitStr.AppendInt(budgetLimit);
2018 
2019     const char16_t* params[] = {multiplierStr.get(), limitStr.get()};
2020     aFrame->PresContext()->Document()->WarnOnceAbout(
2021         nsIDocument::eIgnoringWillChangeOverBudget, false, params,
2022         ArrayLength(params));
2023   }
2024   return onBudget;
2025 }
2026 
ClearWillChangeBudget(nsIFrame * aFrame)2027 void nsDisplayListBuilder::ClearWillChangeBudget(nsIFrame* aFrame) {
2028   if (!aFrame->MayHaveWillChangeBudget()) {
2029     return;
2030   }
2031   aFrame->SetMayHaveWillChangeBudget(false);
2032 
2033   uint32_t cost = 0;
2034   if (!mWillChangeBudgetSet.Get(aFrame, &cost)) {
2035     return;
2036   }
2037   mWillChangeBudgetSet.Remove(aFrame);
2038 
2039   DocumentWillChangeBudget& budget =
2040       mWillChangeBudget.GetOrInsert(aFrame->PresContext());
2041   MOZ_ASSERT(budget.mBudget >= cost);
2042   budget.mBudget -= cost;
2043 }
2044 
2045 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
2046 const float gAGRBudgetAreaMultiplier = 0.3;
2047 #else
2048 const float gAGRBudgetAreaMultiplier = 3.0;
2049 #endif
2050 
AddToAGRBudget(nsIFrame * aFrame)2051 bool nsDisplayListBuilder::AddToAGRBudget(nsIFrame* aFrame) {
2052   if (mAGRBudgetSet.Contains(aFrame)) {
2053     return true;
2054   }
2055 
2056   const nsPresContext* presContext =
2057       aFrame->PresContext()->GetRootPresContext();
2058   if (!presContext) {
2059     return false;
2060   }
2061 
2062   const nsRect area = presContext->GetVisibleArea();
2063   const uint32_t budgetLimit =
2064       gAGRBudgetAreaMultiplier *
2065       nsPresContext::AppUnitsToIntCSSPixels(area.width) *
2066       nsPresContext::AppUnitsToIntCSSPixels(area.height);
2067 
2068   const uint32_t cost = GetLayerizationCost(aFrame->GetSize());
2069   const bool onBudget = mUsedAGRBudget + cost < budgetLimit;
2070 
2071   if (onBudget) {
2072     mUsedAGRBudget += cost;
2073     mAGRBudgetSet.PutEntry(aFrame);
2074   }
2075 
2076   return onBudget;
2077 }
2078 
EnterSVGEffectsContents(nsDisplayList * aHoistedItemsStorage)2079 void nsDisplayListBuilder::EnterSVGEffectsContents(
2080     nsDisplayList* aHoistedItemsStorage) {
2081   MOZ_ASSERT(mSVGEffectsBuildingDepth >= 0);
2082   MOZ_ASSERT(aHoistedItemsStorage);
2083   if (mSVGEffectsBuildingDepth == 0) {
2084     MOZ_ASSERT(!mScrollInfoItemsForHoisting);
2085     mScrollInfoItemsForHoisting = aHoistedItemsStorage;
2086   }
2087   mSVGEffectsBuildingDepth++;
2088 }
2089 
ExitSVGEffectsContents()2090 void nsDisplayListBuilder::ExitSVGEffectsContents() {
2091   mSVGEffectsBuildingDepth--;
2092   MOZ_ASSERT(mSVGEffectsBuildingDepth >= 0);
2093   MOZ_ASSERT(mScrollInfoItemsForHoisting);
2094   if (mSVGEffectsBuildingDepth == 0) {
2095     mScrollInfoItemsForHoisting = nullptr;
2096   }
2097 }
2098 
AppendNewScrollInfoItemForHoisting(nsDisplayScrollInfoLayer * aScrollInfoItem)2099 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(
2100     nsDisplayScrollInfoLayer* aScrollInfoItem) {
2101   MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
2102   MOZ_ASSERT(mScrollInfoItemsForHoisting);
2103   mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem);
2104 }
2105 
GetFrameArea(const nsDisplayListBuilder * aBuilder,const nsIFrame * aFrame)2106 static nsRect GetFrameArea(const nsDisplayListBuilder* aBuilder,
2107                            const nsIFrame* aFrame) {
2108   nsRect area;
2109 
2110   nsIScrollableFrame* scrollFrame =
2111       nsLayoutUtils::GetScrollableFrameFor(aFrame);
2112   if (scrollFrame) {
2113     // If the frame is content of a scrollframe, then we need to pick up the
2114     // area corresponding to the overflow rect as well. Otherwise the parts of
2115     // the overflow that are not occupied by descendants get skipped and the
2116     // APZ code sends touch events to the content underneath instead.
2117     // See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
2118     area = aFrame->GetScrollableOverflowRect();
2119   } else {
2120     area = nsRect(nsPoint(0, 0), aFrame->GetSize());
2121   }
2122 
2123   if (!area.IsEmpty()) {
2124     return area + aBuilder->ToReferenceFrame(aFrame);
2125   }
2126 
2127   return area;
2128 }
2129 
BuildCompositorHitTestInfoIfNeeded(nsIFrame * aFrame,nsDisplayList * aList,const bool aBuildNew)2130 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
2131     nsIFrame* aFrame, nsDisplayList* aList, const bool aBuildNew) {
2132   MOZ_ASSERT(aFrame);
2133   MOZ_ASSERT(aList);
2134 
2135   if (!BuildCompositorHitTestInfo()) {
2136     return;
2137   }
2138 
2139   CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this);
2140   if (!ShouldBuildCompositorHitTestInfo(aFrame, info, aBuildNew)) {
2141     // Either the parent hit test info can be reused, or this frame has no hit
2142     // test flags set.
2143     return;
2144   }
2145 
2146   nsDisplayCompositorHitTestInfo* item =
2147       MakeDisplayItem<nsDisplayCompositorHitTestInfo>(this, aFrame, info);
2148 
2149   SetCompositorHitTestInfo(item);
2150   aList->AppendToTop(item);
2151 }
2152 
ShouldBuildCompositorHitTestInfo(const nsIFrame * aFrame,const CompositorHitTestInfo & aInfo,const bool aBuildNew) const2153 bool nsDisplayListBuilder::ShouldBuildCompositorHitTestInfo(
2154     const nsIFrame* aFrame, const CompositorHitTestInfo& aInfo,
2155     const bool aBuildNew) const {
2156   MOZ_ASSERT(mBuildCompositorHitTestInfo);
2157 
2158   if (aInfo == CompositorHitTestInfo::eInvisibleToHitTest) {
2159     return false;
2160   }
2161 
2162   if (!mCompositorHitTestInfo || !mLessEventRegionItems || aBuildNew) {
2163     return true;
2164   }
2165 
2166   if (mCompositorHitTestInfo->HitTestInfo() != aInfo) {
2167     // Hit test flags are different.
2168     return true;
2169   }
2170 
2171   // Create a new item if the parent does not contain the child completely.
2172   return !mCompositorHitTestInfo->Area().Contains(GetFrameArea(this, aFrame));
2173 }
2174 
IsBuildingLayerEventRegions()2175 bool nsDisplayListBuilder::IsBuildingLayerEventRegions() {
2176   if (mBuildCompositorHitTestInfo) {
2177     // If we have webrender hit-testing enabled, then we will build the
2178     // nsDisplayCompositorHitTestInfo items and use those instead of event
2179     // regions, so we don't need to build the event regions.
2180     return false;
2181   }
2182   if (IsPaintingToWindow()) {
2183     // Note: this function and LayerEventRegionsEnabled are the only places
2184     // that get to query LayoutEventRegionsEnabled 'directly' - other code
2185     // should call this function.
2186     return gfxPrefs::LayoutEventRegionsEnabledDoNotUseDirectly() ||
2187            mAsyncPanZoomEnabled;
2188   }
2189   return false;
2190 }
2191 
LayerEventRegionsEnabled()2192 /* static */ bool nsDisplayListBuilder::LayerEventRegionsEnabled() {
2193   // Note: this function and IsBuildingLayerEventRegions are the only places
2194   // that get to query LayoutEventRegionsEnabled 'directly' - other code
2195   // should call this function.
2196   return gfxPrefs::LayoutEventRegionsEnabledDoNotUseDirectly() ||
2197          gfxPlatform::AsyncPanZoomEnabled();
2198 }
2199 
MoveTo(const nsDisplayListSet & aDestination) const2200 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
2201   aDestination.BorderBackground()->AppendToTop(BorderBackground());
2202   aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
2203   aDestination.Floats()->AppendToTop(Floats());
2204   aDestination.Content()->AppendToTop(Content());
2205   aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
2206   aDestination.Outlines()->AppendToTop(Outlines());
2207 }
2208 
MoveListTo(nsDisplayList * aList,nsTArray<nsDisplayItem * > * aElements)2209 static void MoveListTo(nsDisplayList* aList,
2210                        nsTArray<nsDisplayItem*>* aElements) {
2211   nsDisplayItem* item;
2212   while ((item = aList->RemoveBottom()) != nullptr) {
2213     aElements->AppendElement(item);
2214   }
2215 }
2216 
GetBounds(nsDisplayListBuilder * aBuilder) const2217 nsRect nsDisplayList::GetBounds(nsDisplayListBuilder* aBuilder) const {
2218   nsRect bounds;
2219   for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) {
2220     bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
2221   }
2222   return bounds;
2223 }
2224 
GetClippedBoundsWithRespectToASR(nsDisplayListBuilder * aBuilder,const ActiveScrolledRoot * aASR,nsRect * aVisibleRect) const2225 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR(
2226     nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
2227     nsRect* aVisibleRect) const {
2228   nsRect bounds;
2229   for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) {
2230     nsRect r = i->GetClippedBounds(aBuilder);
2231     if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) {
2232       if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) {
2233         r = clip.ref();
2234       }
2235     }
2236     if (aVisibleRect) {
2237       aVisibleRect->UnionRect(*aVisibleRect, i->GetVisibleRect());
2238     }
2239     bounds.UnionRect(bounds, r);
2240   }
2241   return bounds;
2242 }
2243 
GetVisibleRect() const2244 nsRect nsDisplayList::GetVisibleRect() const {
2245   nsRect result;
2246   for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) {
2247     result.UnionRect(result, i->GetVisibleRect());
2248   }
2249   return result;
2250 }
2251 
ComputeVisibilityForRoot(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)2252 bool nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder,
2253                                              nsRegion* aVisibleRegion) {
2254   AUTO_PROFILER_LABEL("nsDisplayList::ComputeVisibilityForRoot", GRAPHICS);
2255 
2256   nsRegion r;
2257   const ActiveScrolledRoot* rootASR = nullptr;
2258   if (gfxPrefs::LayoutUseContainersForRootFrames()) {
2259     rootASR = aBuilder->ActiveScrolledRootForRootScrollframe();
2260   }
2261   r.And(*aVisibleRegion, GetClippedBoundsWithRespectToASR(aBuilder, rootASR));
2262   return ComputeVisibilityForSublist(aBuilder, aVisibleRegion, r.GetBounds());
2263 }
2264 
TreatAsOpaque(nsDisplayItem * aItem,nsDisplayListBuilder * aBuilder)2265 static nsRegion TreatAsOpaque(nsDisplayItem* aItem,
2266                               nsDisplayListBuilder* aBuilder) {
2267   bool snap;
2268   nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap);
2269   if (aBuilder->IsForPluginGeometry() &&
2270       aItem->GetType() != DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
2271       aItem->GetType() != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
2272     // Treat all leaf chrome items as opaque, unless their frames are opacity:0.
2273     // Since opacity:0 frames generate an nsDisplayOpacity, that item will
2274     // not be treated as opaque here, so opacity:0 chrome content will be
2275     // effectively ignored, as it should be.
2276     // We treat leaf chrome items as opaque to ensure that they cover
2277     // content plugins, for security reasons.
2278     // Non-leaf chrome items don't render contents of their own so shouldn't
2279     // be treated as opaque (and their bounds is just the union of their
2280     // children, which might be a large area their contents don't really cover).
2281     nsIFrame* f = aItem->Frame();
2282     if (f->PresContext()->IsChrome() && !aItem->GetChildren() &&
2283         f->StyleEffects()->mOpacity != 0.0) {
2284       opaque = aItem->GetBounds(aBuilder, &snap);
2285     }
2286   }
2287   if (opaque.IsEmpty()) {
2288     return opaque;
2289   }
2290   nsRegion opaqueClipped;
2291   for (auto iter = opaque.RectIter(); !iter.Done(); iter.Next()) {
2292     opaqueClipped.Or(opaqueClipped,
2293                      aItem->GetClip().ApproximateIntersectInward(iter.Get()));
2294   }
2295   return opaqueClipped;
2296 }
2297 
ComputeVisibilityForSublist(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion,const nsRect & aListVisibleBounds)2298 bool nsDisplayList::ComputeVisibilityForSublist(
2299     nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion,
2300     const nsRect& aListVisibleBounds) {
2301 #ifdef DEBUG
2302   nsRegion r;
2303   r.And(*aVisibleRegion, GetBounds(aBuilder));
2304   NS_ASSERTION(r.GetBounds().IsEqualInterior(aListVisibleBounds),
2305                "bad aListVisibleBounds");
2306 #endif
2307 
2308   bool anyVisible = false;
2309 
2310   AutoTArray<nsDisplayItem*, 512> elements;
2311   MoveListTo(this, &elements);
2312 
2313   for (int32_t i = elements.Length() - 1; i >= 0; --i) {
2314     nsDisplayItem* item = elements[i];
2315 
2316     if (item->ForceNotVisible() && !item->GetSameCoordinateSystemChildren()) {
2317       NS_ASSERTION(item->GetVisibleRect().IsEmpty(),
2318                    "invisible items should have empty vis rect");
2319     } else {
2320       nsRect bounds = item->GetClippedBounds(aBuilder);
2321 
2322       nsRegion itemVisible;
2323       itemVisible.And(*aVisibleRegion, bounds);
2324       item->SetVisibleRect(itemVisible.GetBounds(), false);
2325     }
2326 
2327     if (item->ComputeVisibility(aBuilder, aVisibleRegion)) {
2328       anyVisible = true;
2329 
2330       nsRegion opaque = TreatAsOpaque(item, aBuilder);
2331       // Subtract opaque item from the visible region
2332       aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque);
2333     }
2334     AppendToBottom(item);
2335   }
2336 
2337   mIsOpaque = !aVisibleRegion->Intersects(aListVisibleBounds);
2338   return anyVisible;
2339 }
2340 
TriggerPendingAnimationsOnSubDocuments(nsIDocument * aDocument,void * aReadyTime)2341 static bool TriggerPendingAnimationsOnSubDocuments(nsIDocument* aDocument,
2342                                                    void* aReadyTime) {
2343   PendingAnimationTracker* tracker = aDocument->GetPendingAnimationTracker();
2344   if (tracker) {
2345     nsIPresShell* shell = aDocument->GetShell();
2346     // If paint-suppression is in effect then we haven't finished painting
2347     // this document yet so we shouldn't start animations
2348     if (!shell || !shell->IsPaintingSuppressed()) {
2349       const TimeStamp& readyTime = *static_cast<TimeStamp*>(aReadyTime);
2350       tracker->TriggerPendingAnimationsOnNextTick(readyTime);
2351     }
2352   }
2353   aDocument->EnumerateSubDocuments(TriggerPendingAnimationsOnSubDocuments,
2354                                    aReadyTime);
2355   return true;
2356 }
2357 
TriggerPendingAnimations(nsIDocument * aDocument,const TimeStamp & aReadyTime)2358 static void TriggerPendingAnimations(nsIDocument* aDocument,
2359                                      const TimeStamp& aReadyTime) {
2360   MOZ_ASSERT(!aReadyTime.IsNull(),
2361              "Animation ready time is not set. Perhaps we're using a layer"
2362              " manager that doesn't update it");
2363   TriggerPendingAnimationsOnSubDocuments(aDocument,
2364                                          const_cast<TimeStamp*>(&aReadyTime));
2365 }
2366 
GetWidgetLayerManager(nsView ** aView)2367 LayerManager* nsDisplayListBuilder::GetWidgetLayerManager(nsView** aView) {
2368   if (aView) {
2369     *aView = RootReferenceFrame()->GetView();
2370   }
2371   if (RootReferenceFrame() !=
2372       nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
2373     return nullptr;
2374   }
2375   nsIWidget* window = RootReferenceFrame()->GetNearestWidget();
2376   if (window) {
2377     return window->GetLayerManager();
2378   }
2379   return nullptr;
2380 }
2381 
BuildLayers(nsDisplayListBuilder * aBuilder,LayerManager * aLayerManager,uint32_t aFlags,bool aIsWidgetTransaction)2382 FrameLayerBuilder* nsDisplayList::BuildLayers(nsDisplayListBuilder* aBuilder,
2383                                               LayerManager* aLayerManager,
2384                                               uint32_t aFlags,
2385                                               bool aIsWidgetTransaction) {
2386   nsIFrame* frame = aBuilder->RootReferenceFrame();
2387   nsPresContext* presContext = frame->PresContext();
2388   nsIPresShell* presShell = presContext->PresShell();
2389 
2390   FrameLayerBuilder* layerBuilder = new FrameLayerBuilder();
2391   layerBuilder->Init(aBuilder, aLayerManager);
2392 
2393   if (aFlags & PAINT_COMPRESSED) {
2394     layerBuilder->SetLayerTreeCompressionMode();
2395   }
2396 
2397   RefPtr<ContainerLayer> root;
2398   {
2399     AUTO_PROFILER_TRACING("Paint", "LayerBuilding");
2400 
2401     if (XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) {
2402       FrameLayerBuilder::InvalidateAllLayers(aLayerManager);
2403     }
2404 
2405     if (aIsWidgetTransaction) {
2406       layerBuilder->DidBeginRetainedLayerTransaction(aLayerManager);
2407     }
2408 
2409     // Clear any ScrollMetadata that may have been set on the root layer on a
2410     // previous paint. This paint will set new metrics if necessary, and if we
2411     // don't clear the old one here, we may be left with extra metrics.
2412     if (Layer* rootLayer = aLayerManager->GetRoot()) {
2413       rootLayer->SetScrollMetadata(nsTArray<ScrollMetadata>());
2414     }
2415 
2416     ContainerLayerParameters containerParameters(presShell->GetResolution(),
2417                                                  presShell->GetResolution());
2418 
2419     {
2420       PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Layerization);
2421 
2422       root = layerBuilder->BuildContainerLayerFor(aBuilder, aLayerManager,
2423                                                   frame, nullptr, this,
2424                                                   containerParameters, nullptr);
2425 
2426       if (!record.GetStart().IsNull() && gfxPrefs::LayersDrawFPS()) {
2427         if (PaintTiming* pt =
2428                 ClientLayerManager::MaybeGetPaintTiming(aLayerManager)) {
2429           pt->flbMs() = (TimeStamp::Now() - record.GetStart()).ToMilliseconds();
2430         }
2431       }
2432     }
2433 
2434     if (!root) {
2435       return nullptr;
2436     }
2437     // Root is being scaled up by the X/Y resolution. Scale it back down.
2438     root->SetPostScale(1.0f / containerParameters.mXScale,
2439                        1.0f / containerParameters.mYScale);
2440     root->SetScaleToResolution(presShell->ScaleToResolution(),
2441                                containerParameters.mXScale);
2442 
2443     auto callback = [root](FrameMetrics::ViewID aScrollId) -> bool {
2444       return nsLayoutUtils::ContainsMetricsWithId(root, aScrollId);
2445     };
2446     if (Maybe<ScrollMetadata> rootMetadata = nsLayoutUtils::GetRootMetadata(
2447             aBuilder, root->Manager(), containerParameters, callback)) {
2448       root->SetScrollMetadata(rootMetadata.value());
2449     }
2450 
2451       // NS_WARNING is debug-only, so don't even bother checking the conditions
2452       // in a release build.
2453 #ifdef DEBUG
2454     bool usingDisplayport = false;
2455     if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
2456       nsIContent* content = rootScrollFrame->GetContent();
2457       if (content) {
2458         usingDisplayport = nsLayoutUtils::HasDisplayPort(content);
2459       }
2460     }
2461     if (usingDisplayport &&
2462         !(root->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
2463         SpammyLayoutWarningsEnabled()) {
2464       // See bug 693938, attachment 567017
2465       NS_WARNING("Transparent content with displayports can be expensive.");
2466     }
2467 #endif
2468 
2469     aLayerManager->SetRoot(root);
2470     layerBuilder->WillEndTransaction();
2471   }
2472   return layerBuilder;
2473 }
2474 
2475 /**
2476  * We paint by executing a layer manager transaction, constructing a
2477  * single layer representing the display list, and then making it the
2478  * root of the layer manager, drawing into the PaintedLayers.
2479  */
PaintRoot(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,uint32_t aFlags)2480 already_AddRefed<LayerManager> nsDisplayList::PaintRoot(
2481     nsDisplayListBuilder* aBuilder, gfxContext* aCtx, uint32_t aFlags) {
2482   AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS);
2483 
2484   RefPtr<LayerManager> layerManager;
2485   bool widgetTransaction = false;
2486   bool doBeginTransaction = true;
2487   nsView* view = nullptr;
2488   if (aFlags & PAINT_USE_WIDGET_LAYERS) {
2489     layerManager = aBuilder->GetWidgetLayerManager(&view);
2490     if (layerManager) {
2491       doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
2492       widgetTransaction = true;
2493     }
2494   }
2495   if (!layerManager) {
2496     if (!aCtx) {
2497       NS_WARNING("Nowhere to paint into");
2498       return nullptr;
2499     }
2500     layerManager = new BasicLayerManager(BasicLayerManager::BLM_OFFSCREEN);
2501   }
2502 
2503   nsIFrame* frame = aBuilder->RootReferenceFrame();
2504   nsPresContext* presContext = frame->PresContext();
2505   nsIPresShell* presShell = presContext->PresShell();
2506   nsIDocument* document = presShell->GetDocument();
2507 
2508   if (layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
2509     if (doBeginTransaction) {
2510       if (aCtx) {
2511         if (!layerManager->BeginTransactionWithTarget(aCtx)) {
2512           return nullptr;
2513         }
2514       } else {
2515         if (!layerManager->BeginTransaction()) {
2516           return nullptr;
2517         }
2518       }
2519     }
2520 
2521     // Windowed plugins are not supported with WebRender enabled.
2522     // But PluginGeometry needs to be updated to show plugin.
2523     // Windowed plugins are going to be removed by Bug 1296400.
2524     nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
2525     if (rootPresContext && XRE_IsContentProcess()) {
2526       if (aBuilder->WillComputePluginGeometry()) {
2527         rootPresContext->ComputePluginGeometryUpdates(
2528             aBuilder->RootReferenceFrame(), aBuilder, this);
2529       }
2530       // This must be called even if PluginGeometryUpdates were not computed.
2531       rootPresContext->CollectPluginGeometryUpdates(layerManager);
2532     }
2533 
2534     WebRenderLayerManager* wrManager =
2535         static_cast<WebRenderLayerManager*>(layerManager.get());
2536 
2537     nsIDocShell* docShell = presContext->GetDocShell();
2538     nsTArray<wr::WrFilterOp> wrFilters;
2539     gfx::Matrix5x4* colorMatrix = nsDocShell::Cast(docShell)->GetColorMatrix();
2540     if (colorMatrix) {
2541       wr::WrFilterOp gs = {wr::WrFilterOpType::ColorMatrix};
2542       MOZ_ASSERT(sizeof(gs.matrix) == sizeof(colorMatrix->components));
2543       memcpy(&(gs.matrix), colorMatrix->components, sizeof(gs.matrix));
2544       wrFilters.AppendElement(gs);
2545     }
2546 
2547     MaybeSetupTransactionIdAllocator(layerManager, presContext);
2548     bool temp =
2549         aBuilder->SetIsCompositingCheap(layerManager->IsCompositingCheap());
2550     wrManager->EndTransactionWithoutLayer(this, aBuilder, wrFilters);
2551 
2552     // For layers-free mode, we check the invalidation state bits in the
2553     // EndTransaction. So we clear the invalidation state bits after
2554     // EndTransaction.
2555     if (widgetTransaction ||
2556         // SVG-as-an-image docs don't paint as part of the retained layer tree,
2557         // but they still need the invalidation state bits cleared in order for
2558         // invalidation for CSS/SMIL animation to work properly.
2559         (document && document->IsBeingUsedAsImage())) {
2560       frame->ClearInvalidationStateBits();
2561     }
2562 
2563     aBuilder->SetIsCompositingCheap(temp);
2564     if (document && widgetTransaction) {
2565       TriggerPendingAnimations(document, layerManager->GetAnimationReadyTime());
2566     }
2567 
2568     if (presContext->RefreshDriver()->HasScheduleFlush()) {
2569       presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2570                                       frame->GetRect());
2571     }
2572 
2573     return layerManager.forget();
2574   }
2575 
2576   NotifySubDocInvalidationFunc computeInvalidFunc =
2577       presContext->MayHavePaintEventListenerInSubDocument()
2578           ? nsPresContext::NotifySubDocInvalidation
2579           : 0;
2580 
2581   UniquePtr<LayerProperties> props;
2582 
2583   bool computeInvalidRect =
2584       (computeInvalidFunc || (!layerManager->IsCompositingCheap() &&
2585                               layerManager->NeedsWidgetInvalidation())) &&
2586       widgetTransaction;
2587 
2588   if (computeInvalidRect) {
2589     props = Move(LayerProperties::CloneFrom(layerManager->GetRoot()));
2590   }
2591 
2592   if (doBeginTransaction) {
2593     if (aCtx) {
2594       if (!layerManager->BeginTransactionWithTarget(aCtx)) {
2595         return nullptr;
2596       }
2597     } else {
2598       if (!layerManager->BeginTransaction()) {
2599         return nullptr;
2600       }
2601     }
2602   }
2603 
2604   bool temp =
2605       aBuilder->SetIsCompositingCheap(layerManager->IsCompositingCheap());
2606   LayerManager::EndTransactionFlags flags = LayerManager::END_DEFAULT;
2607   if (layerManager->NeedsWidgetInvalidation()) {
2608     if (aFlags & PAINT_NO_COMPOSITE) {
2609       flags = LayerManager::END_NO_COMPOSITE;
2610     }
2611   } else {
2612     // Client layer managers never composite directly, so
2613     // we don't need to worry about END_NO_COMPOSITE.
2614     if (aBuilder->WillComputePluginGeometry()) {
2615       flags = LayerManager::END_NO_REMOTE_COMPOSITE;
2616     }
2617   }
2618 
2619   MaybeSetupTransactionIdAllocator(layerManager, presContext);
2620 
2621   // Store the existing layer builder to reinstate it on return.
2622   FrameLayerBuilder* oldBuilder = layerManager->GetLayerBuilder();
2623   FrameLayerBuilder* layerBuilder = nullptr;
2624 
2625   bool sent = false;
2626   if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
2627     sent = layerManager->EndEmptyTransaction(flags);
2628   }
2629 
2630   if (!sent) {
2631     layerBuilder =
2632         BuildLayers(aBuilder, layerManager, aFlags, widgetTransaction);
2633 
2634     if (!layerBuilder) {
2635       layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
2636       return nullptr;
2637     }
2638 
2639     // If this is the content process, we ship plugin geometry updates over with
2640     // layer updates, so calculate that now before we call EndTransaction.
2641     nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
2642     if (rootPresContext && XRE_IsContentProcess()) {
2643       if (aBuilder->WillComputePluginGeometry()) {
2644         rootPresContext->ComputePluginGeometryUpdates(
2645             aBuilder->RootReferenceFrame(), aBuilder, this);
2646       }
2647       // The layer system caches plugin configuration information for forwarding
2648       // with layer updates which needs to get set during reflow. This must be
2649       // called even if there are no windowed plugins in the page.
2650       rootPresContext->CollectPluginGeometryUpdates(layerManager);
2651     }
2652 
2653     layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aBuilder,
2654                                  flags);
2655     layerBuilder->DidEndTransaction();
2656   }
2657 
2658   if (widgetTransaction ||
2659       // SVG-as-an-image docs don't paint as part of the retained layer tree,
2660       // but they still need the invalidation state bits cleared in order for
2661       // invalidation for CSS/SMIL animation to work properly.
2662       (document && document->IsBeingUsedAsImage())) {
2663     frame->ClearInvalidationStateBits();
2664   }
2665 
2666   aBuilder->SetIsCompositingCheap(temp);
2667 
2668   if (document && widgetTransaction) {
2669     TriggerPendingAnimations(document, layerManager->GetAnimationReadyTime());
2670   }
2671 
2672   nsIntRegion invalid;
2673   bool areaOverflowed = false;
2674   if (props) {
2675     if (!props->ComputeDifferences(layerManager->GetRoot(), invalid,
2676                                    computeInvalidFunc)) {
2677       areaOverflowed = true;
2678     }
2679   } else if (widgetTransaction) {
2680     LayerProperties::ClearInvalidations(layerManager->GetRoot());
2681   }
2682 
2683   bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
2684   if (view) {
2685     if (props && !areaOverflowed) {
2686       if (!invalid.IsEmpty()) {
2687         nsIntRect bounds = invalid.GetBounds();
2688         nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
2689                     presContext->DevPixelsToAppUnits(bounds.y),
2690                     presContext->DevPixelsToAppUnits(bounds.width),
2691                     presContext->DevPixelsToAppUnits(bounds.height));
2692         if (shouldInvalidate) {
2693           view->GetViewManager()->InvalidateViewNoSuppression(view, rect);
2694         }
2695         presContext->NotifyInvalidation(layerManager->GetLastTransactionId(),
2696                                         bounds);
2697       }
2698     } else if (shouldInvalidate) {
2699       view->GetViewManager()->InvalidateView(view);
2700     }
2701   }
2702 
2703   layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
2704   return layerManager.forget();
2705 }
2706 
RemoveBottom()2707 nsDisplayItem* nsDisplayList::RemoveBottom() {
2708   nsDisplayItem* item = mSentinel.mAbove;
2709   if (!item) return nullptr;
2710   mSentinel.mAbove = item->mAbove;
2711   if (item == mTop) {
2712     // must have been the only item
2713     mTop = &mSentinel;
2714   }
2715   item->mAbove = nullptr;
2716   mLength--;
2717   return item;
2718 }
2719 
DeleteAll(nsDisplayListBuilder * aBuilder)2720 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) {
2721   nsDisplayItem* item;
2722   while ((item = RemoveBottom()) != nullptr) {
2723     item->Destroy(aBuilder);
2724   }
2725 }
2726 
GetMouseThrough(const nsIFrame * aFrame)2727 static bool GetMouseThrough(const nsIFrame* aFrame) {
2728   if (!aFrame->IsXULBoxFrame()) return false;
2729 
2730   const nsIFrame* frame = aFrame;
2731   while (frame) {
2732     if (frame->GetStateBits() & NS_FRAME_MOUSE_THROUGH_ALWAYS) {
2733       return true;
2734     } else if (frame->GetStateBits() & NS_FRAME_MOUSE_THROUGH_NEVER) {
2735       return false;
2736     }
2737     frame = nsBox::GetParentXULBox(frame);
2738   }
2739   return false;
2740 }
2741 
IsFrameReceivingPointerEvents(nsIFrame * aFrame)2742 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) {
2743   return NS_STYLE_POINTER_EVENTS_NONE !=
2744          aFrame->StyleUserInterface()->GetEffectivePointerEvents(aFrame);
2745 }
2746 
2747 // A list of frames, and their z depth. Used for sorting
2748 // the results of hit testing.
2749 struct FramesWithDepth {
FramesWithDepthFramesWithDepth2750   explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {}
2751 
operator <FramesWithDepth2752   bool operator<(const FramesWithDepth& aOther) const {
2753     if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
2754       // We want to sort so that the shallowest item (highest depth value) is
2755       // first
2756       return mDepth > aOther.mDepth;
2757     }
2758     return this < &aOther;
2759   }
operator ==FramesWithDepth2760   bool operator==(const FramesWithDepth& aOther) const {
2761     return this == &aOther;
2762   }
2763 
2764   float mDepth;
2765   nsTArray<nsIFrame*> mFrames;
2766 };
2767 
2768 // Sort the frames by depth and then moves all the contained frames to the
2769 // destination
FlushFramesArray(nsTArray<FramesWithDepth> & aSource,nsTArray<nsIFrame * > * aDest)2770 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource,
2771                              nsTArray<nsIFrame*>* aDest) {
2772   if (aSource.IsEmpty()) {
2773     return;
2774   }
2775   aSource.Sort();
2776   uint32_t length = aSource.Length();
2777   for (uint32_t i = 0; i < length; i++) {
2778     aDest->AppendElements(Move(aSource[i].mFrames));
2779   }
2780   aSource.Clear();
2781 }
2782 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,nsDisplayItem::HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames) const2783 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2784                             nsDisplayItem::HitTestState* aState,
2785                             nsTArray<nsIFrame*>* aOutFrames) const {
2786   nsDisplayItem* item;
2787 
2788   if (aState->mInPreserves3D) {
2789     // Collect leaves of the current 3D rendering context.
2790     for (item = GetBottom(); item; item = item->GetAbove()) {
2791       auto itemType = item->GetType();
2792       if (itemType != DisplayItemType::TYPE_TRANSFORM ||
2793           !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2794         item->HitTest(aBuilder, aRect, aState, aOutFrames);
2795       } else {
2796         // One of leaves in the current 3D rendering context.
2797         aState->mItemBuffer.AppendElement(item);
2798       }
2799     }
2800     return;
2801   }
2802 
2803   int32_t itemBufferStart = aState->mItemBuffer.Length();
2804   for (item = GetBottom(); item; item = item->GetAbove()) {
2805     aState->mItemBuffer.AppendElement(item);
2806   }
2807 
2808   AutoTArray<FramesWithDepth, 16> temp;
2809   for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart;
2810        --i) {
2811     // Pop element off the end of the buffer. We want to shorten the buffer
2812     // so that recursive calls to HitTest have more buffer space.
2813     item = aState->mItemBuffer[i];
2814     aState->mItemBuffer.SetLength(i);
2815 
2816     bool snap;
2817     nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
2818     auto itemType = item->GetType();
2819     bool same3DContext =
2820         (itemType == DisplayItemType::TYPE_TRANSFORM &&
2821          static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
2822         (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
2823          static_cast<nsDisplayPerspective*>(item)
2824              ->TransformFrame()
2825              ->Extend3DContext());
2826     if (same3DContext &&
2827         (itemType != DisplayItemType::TYPE_TRANSFORM ||
2828          !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
2829       if (!item->GetClip().MayIntersect(aRect)) {
2830         continue;
2831       }
2832       AutoTArray<nsIFrame*, 1> neverUsed;
2833       // Start gethering leaves of the 3D rendering context, and
2834       // append leaves at the end of mItemBuffer.  Leaves are
2835       // processed at following iterations.
2836       aState->mInPreserves3D = true;
2837       item->HitTest(aBuilder, aRect, aState, &neverUsed);
2838       aState->mInPreserves3D = false;
2839       i = aState->mItemBuffer.Length();
2840       continue;
2841     }
2842     if (same3DContext || item->GetClip().MayIntersect(r)) {
2843       AutoTArray<nsIFrame*, 16> outFrames;
2844       item->HitTest(aBuilder, aRect, aState, &outFrames);
2845 
2846       // For 3d transforms with preserve-3d we add hit frames into the temp list
2847       // so we can sort them later, otherwise we add them directly to the output
2848       // list.
2849       nsTArray<nsIFrame*>* writeFrames = aOutFrames;
2850       if (item->GetType() == DisplayItemType::TYPE_TRANSFORM &&
2851           static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
2852         if (outFrames.Length()) {
2853           nsDisplayTransform* transform =
2854               static_cast<nsDisplayTransform*>(item);
2855           nsPoint point = aRect.TopLeft();
2856           // A 1x1 rect means a point, otherwise use the center of the rect
2857           if (aRect.width != 1 || aRect.height != 1) {
2858             point = aRect.Center();
2859           }
2860           temp.AppendElement(
2861               FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
2862           writeFrames = &temp[temp.Length() - 1].mFrames;
2863         }
2864       } else {
2865         // We may have just finished a run of consecutive preserve-3d
2866         // transforms, so flush these into the destination array before
2867         // processing our frame list.
2868         FlushFramesArray(temp, aOutFrames);
2869       }
2870 
2871       for (uint32_t j = 0; j < outFrames.Length(); j++) {
2872         nsIFrame* f = outFrames.ElementAt(j);
2873         // Filter out some frames depending on the type of hittest
2874         // we are doing. For visibility tests, pass through all frames.
2875         // For pointer tests, only pass through frames that are styled
2876         // to receive pointer events.
2877         if (aBuilder->HitTestIsForVisibility() ||
2878             (!GetMouseThrough(f) && IsFrameReceivingPointerEvents(f))) {
2879           writeFrames->AppendElement(f);
2880         }
2881       }
2882 
2883       if (aBuilder->HitTestIsForVisibility() &&
2884           item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) {
2885         // We're exiting early, so pop the remaining items off the buffer.
2886         aState->mItemBuffer.SetLength(itemBufferStart);
2887         break;
2888       }
2889     }
2890   }
2891   // Clear any remaining preserve-3d transforms.
2892   FlushFramesArray(temp, aOutFrames);
2893   NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
2894                "How did we forget to pop some elements?");
2895 }
2896 
FindContentInDocument(nsDisplayItem * aItem,nsIDocument * aDoc)2897 static nsIContent* FindContentInDocument(nsDisplayItem* aItem,
2898                                          nsIDocument* aDoc) {
2899   nsIFrame* f = aItem->Frame();
2900   while (f) {
2901     nsPresContext* pc = f->PresContext();
2902     if (pc->Document() == aDoc) {
2903       return f->GetContent();
2904     }
2905     f = nsLayoutUtils::GetCrossDocParentFrame(pc->PresShell()->GetRootFrame());
2906   }
2907   return nullptr;
2908 }
2909 
2910 struct ZSortItem {
2911   nsDisplayItem* item;
2912   int32_t zIndex;
2913 
ZSortItemZSortItem2914   explicit ZSortItem(nsDisplayItem* aItem)
2915       : item(aItem), zIndex(aItem->ZIndex()) {}
2916 
operator nsDisplayItem*ZSortItem2917   operator nsDisplayItem*() { return item; }
2918 };
2919 
2920 struct ZOrderComparator {
operator ()ZOrderComparator2921   bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const {
2922     // Note that we can't just take the difference of the two
2923     // z-indices here, because that might overflow a 32-bit int.
2924     return aLeft.zIndex < aRight.zIndex;
2925   }
2926 };
2927 
SortByZOrder()2928 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); }
2929 
2930 struct ContentComparator {
2931   nsIContent* mCommonAncestor;
2932 
ContentComparatorContentComparator2933   explicit ContentComparator(nsIContent* aCommonAncestor)
2934       : mCommonAncestor(aCommonAncestor) {}
2935 
operator ()ContentComparator2936   bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const {
2937     // It's possible that the nsIContent for aItem1 or aItem2 is in a
2938     // subdocument of commonAncestor, because display items for subdocuments
2939     // have been mixed into the same list. Ensure that we're looking at content
2940     // in commonAncestor's document.
2941     nsIDocument* commonAncestorDoc = mCommonAncestor->OwnerDoc();
2942     nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc);
2943     nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc);
2944     if (!content1 || !content2) {
2945       NS_ERROR("Document trees are mixed up!");
2946       // Something weird going on
2947       return true;
2948     }
2949     return nsLayoutUtils::CompareTreePosition(content1, content2,
2950                                               mCommonAncestor) < 0;
2951   }
2952 };
2953 
SortByContentOrder(nsIContent * aCommonAncestor)2954 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) {
2955   Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor));
2956 }
2957 
nsDisplayItem(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)2958 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2959     : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
2960 
nsDisplayItem(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const ActiveScrolledRoot * aActiveScrolledRoot)2961 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
2962                              const ActiveScrolledRoot* aActiveScrolledRoot)
2963     : mFrame(aFrame),
2964       mActiveScrolledRoot(aActiveScrolledRoot),
2965       mAnimatedGeometryRoot(nullptr),
2966       mForceNotVisible(aBuilder->IsBuildingInvisibleItems()),
2967       mDisableSubpixelAA(false),
2968       mReusedItem(false),
2969       mBackfaceHidden(mFrame->In3DContextAndBackfaceIsHidden())
2970 #ifdef MOZ_DUMP_PAINTING
2971       ,
2972       mPainted(false)
2973 #endif
2974 {
2975   MOZ_COUNT_CTOR(nsDisplayItem);
2976   if (aBuilder->IsRetainingDisplayList()) {
2977     mFrame->AddDisplayItem(this);
2978   }
2979   mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
2980   // This can return the wrong result if the item override
2981   // ShouldFixToViewport(), the item needs to set it again in its constructor.
2982   mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(aFrame);
2983   MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(
2984                  aBuilder->RootReferenceFrame(), *mAnimatedGeometryRoot),
2985              "Bad");
2986   NS_ASSERTION(
2987       aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(),
2988       "visible rect not set");
2989 
2990   SetClipChain(aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder),
2991                true);
2992 
2993   // The visible rect is for mCurrentFrame, so we have to use
2994   // mCurrentOffsetToReferenceFrame
2995   nsRect visible = aBuilder->GetVisibleRect() +
2996                    aBuilder->GetCurrentFrameOffsetToReferenceFrame();
2997   SetVisibleRect(visible, true);
2998 }
2999 
ForceActiveLayers()3000 /* static */ bool nsDisplayItem::ForceActiveLayers() {
3001   static bool sForce = false;
3002   static bool sForceCached = false;
3003 
3004   if (!sForceCached) {
3005     Preferences::AddBoolVarCache(&sForce, "layers.force-active", false);
3006     sForceCached = true;
3007   }
3008 
3009   return sForce;
3010 }
3011 
ZIndexForFrame(nsIFrame * aFrame)3012 static int32_t ZIndexForFrame(nsIFrame* aFrame) {
3013   if (!aFrame->IsAbsPosContainingBlock() && !aFrame->IsFlexOrGridItem())
3014     return 0;
3015 
3016   const nsStylePosition* position = aFrame->StylePosition();
3017   if (position->mZIndex.GetUnit() == eStyleUnit_Integer)
3018     return position->mZIndex.GetIntValue();
3019 
3020   // sort the auto and 0 elements together
3021   return 0;
3022 }
3023 
ZIndex() const3024 int32_t nsDisplayItem::ZIndex() const { return ZIndexForFrame(mFrame); }
3025 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)3026 bool nsDisplayItem::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3027                                       nsRegion* aVisibleRegion) {
3028   return !mVisibleRect.IsEmpty() &&
3029          !IsInvisibleInRect(aVisibleRegion->GetBounds());
3030 }
3031 
RecomputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion,bool aUseClipBounds)3032 bool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
3033                                         nsRegion* aVisibleRegion,
3034                                         bool aUseClipBounds) {
3035   if (mForceNotVisible && !GetSameCoordinateSystemChildren()) {
3036     // mForceNotVisible wants to ensure that this display item doesn't render
3037     // anything itself. If this item has contents, then we obviously want to
3038     // render those, so we don't need this check in that case.
3039     NS_ASSERTION(mVisibleRect.IsEmpty(),
3040                  "invisible items without children should have empty vis rect");
3041   } else {
3042     nsRect bounds;
3043     if (aUseClipBounds) {
3044       bounds = GetClippedBounds(aBuilder);
3045     } else {
3046       bool snap;
3047       bounds = GetBounds(aBuilder, &snap);
3048     }
3049 
3050     nsRegion itemVisible;
3051     itemVisible.And(*aVisibleRegion, bounds);
3052     SetVisibleRect(itemVisible.GetBounds(), false);
3053   }
3054 
3055   // When we recompute visibility within layers we don't need to
3056   // expand the visible region for content behind plugins (the plugin
3057   // is not in the layer).
3058   if (!ComputeVisibility(aBuilder, aVisibleRegion)) {
3059     SetVisibleRect(nsRect(), false);
3060     return false;
3061   }
3062 
3063   nsRegion opaque = TreatAsOpaque(this, aBuilder);
3064   aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque);
3065   return true;
3066 }
3067 
SetClipChain(const DisplayItemClipChain * aClipChain,bool aStore)3068 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain,
3069                                  bool aStore) {
3070   mClipChain = aClipChain;
3071   mClip = DisplayItemClipChain::ClipForASR(aClipChain, mActiveScrolledRoot);
3072 
3073   if (aStore) {
3074     mState.mClipChain = mClipChain;
3075     mState.mClip = mClip;
3076   }
3077 }
3078 
GetClipWithRespectToASR(nsDisplayListBuilder * aBuilder,const ActiveScrolledRoot * aASR) const3079 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR(
3080     nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
3081   if (const DisplayItemClip* clip =
3082           DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
3083     return Some(clip->GetClipRect());
3084   }
3085 #ifdef DEBUG
3086   if (!gfxPrefs::LayoutUseContainersForRootFrames()) {
3087     MOZ_ASSERT(false, "item should have finite clip with respect to aASR");
3088   }
3089 #endif
3090   return Nothing();
3091 }
3092 
FuseClipChainUpTo(nsDisplayListBuilder * aBuilder,const ActiveScrolledRoot * aASR)3093 void nsDisplayItem::FuseClipChainUpTo(nsDisplayListBuilder* aBuilder,
3094                                       const ActiveScrolledRoot* aASR) {
3095   const DisplayItemClipChain* sc = mClipChain;
3096   DisplayItemClip mergedClip;
3097   while (sc && ActiveScrolledRoot::PickDescendant(aASR, sc->mASR) == sc->mASR) {
3098     mergedClip.IntersectWith(sc->mClip);
3099     sc = sc->mParent;
3100   }
3101   if (mergedClip.HasClip()) {
3102     mClipChain = aBuilder->AllocateDisplayItemClipChain(mergedClip, aASR, sc);
3103     mClip = &mClipChain->mClip;
3104   } else {
3105     mClipChain = nullptr;
3106     mClip = nullptr;
3107   }
3108 }
3109 
ShouldUseAdvancedLayer(LayerManager * aManager,PrefFunc aFunc) const3110 bool nsDisplayItem::ShouldUseAdvancedLayer(LayerManager* aManager,
3111                                            PrefFunc aFunc) const {
3112   return CanUseAdvancedLayer(aManager) ? aFunc() : false;
3113 }
3114 
CanUseAdvancedLayer(LayerManager * aManager) const3115 bool nsDisplayItem::CanUseAdvancedLayer(LayerManager* aManager) const {
3116   if (!gfxPrefs::LayersAdvancedBasicLayerEnabled() && aManager &&
3117       aManager->GetBackendType() != layers::LayersBackend::LAYERS_WR) {
3118     return false;
3119   }
3120 
3121   return true;
3122 }
3123 
FindCommonAncestorClipForIntersection(const DisplayItemClipChain * aOne,const DisplayItemClipChain * aTwo)3124 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection(
3125     const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) {
3126   for (const ActiveScrolledRoot* asr =
3127            ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
3128        asr; asr = asr->mParent) {
3129     if (aOne == aTwo) {
3130       return aOne;
3131     }
3132     if (aOne->mASR == asr) {
3133       aOne = aOne->mParent;
3134     }
3135     if (aTwo->mASR == asr) {
3136       aTwo = aTwo->mParent;
3137     }
3138     if (!aOne) {
3139       return aTwo;
3140     }
3141     if (!aTwo) {
3142       return aOne;
3143     }
3144   }
3145   return nullptr;
3146 }
3147 
IntersectClip(nsDisplayListBuilder * aBuilder,const DisplayItemClipChain * aOther,bool aStore)3148 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder,
3149                                   const DisplayItemClipChain* aOther,
3150                                   bool aStore) {
3151   if (!aOther || mClipChain == aOther) {
3152     return;
3153   }
3154 
3155   // aOther might be a reference to a clip on the stack. We need to make sure
3156   // that CreateClipChainIntersection will allocate the actual intersected
3157   // clip in the builder's arena, so for the mClipChain == nullptr case,
3158   // we supply nullptr as the common ancestor so that
3159   // CreateClipChainIntersection clones the whole chain.
3160   const DisplayItemClipChain* ancestorClip =
3161       mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther)
3162                  : nullptr;
3163 
3164   SetClipChain(
3165       aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther),
3166       aStore);
3167 }
3168 
GetClippedBounds(nsDisplayListBuilder * aBuilder) const3169 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const {
3170   bool snap;
3171   nsRect r = GetBounds(aBuilder, &snap);
3172   return GetClip().ApplyNonRoundedIntersection(r);
3173 }
3174 
BuildDisplayItemLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)3175 already_AddRefed<Layer> nsDisplayItem::BuildDisplayItemLayer(
3176     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
3177     const ContainerLayerParameters& aContainerParameters) {
3178   RefPtr<Layer> oldLayer =
3179       aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this);
3180   RefPtr<DisplayItemLayer> layer =
3181       oldLayer ? oldLayer->AsDisplayItemLayer() : nullptr;
3182 
3183   if (!layer) {
3184     layer = aManager->CreateDisplayItemLayer();
3185 
3186     if (!layer) {
3187       return nullptr;
3188     }
3189   }
3190 
3191   aManager->TrackDisplayItemLayer(layer);
3192   layer->SetDisplayItem(this, aBuilder);
3193   layer->SetBaseTransform(gfx::Matrix4x4::Translation(
3194       aContainerParameters.mOffset.x, aContainerParameters.mOffset.y, 0));
3195   return layer.forget();
3196 }
3197 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const3198 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder,
3199                                       bool* aSnap) const {
3200   *aSnap = true;
3201   return mBounds;
3202 }
3203 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)3204 LayerState nsDisplaySolidColor::GetLayerState(
3205     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
3206     const ContainerLayerParameters& aParameters) {
3207   if (ForceActiveLayers() ||
3208       ShouldUseAdvancedLayer(aManager, gfxPrefs::LayersAllowSolidColorLayers)) {
3209     return LAYER_ACTIVE;
3210   }
3211   return LAYER_NONE;
3212 }
3213 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)3214 already_AddRefed<Layer> nsDisplaySolidColor::BuildLayer(
3215     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
3216     const ContainerLayerParameters& aContainerParameters) {
3217   RefPtr<ColorLayer> layer = static_cast<ColorLayer*>(
3218       aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
3219   if (!layer) {
3220     layer = aManager->CreateColorLayer();
3221     if (!layer) {
3222       return nullptr;
3223     }
3224   }
3225   layer->SetColor(gfx::Color::FromABGR(mColor));
3226 
3227   const int32_t appUnitsPerDevPixel =
3228       mFrame->PresContext()->AppUnitsPerDevPixel();
3229   layer->SetBounds(mBounds.ToNearestPixels(appUnitsPerDevPixel));
3230   layer->SetBaseTransform(gfx::Matrix4x4::Translation(
3231       aContainerParameters.mOffset.x, aContainerParameters.mOffset.y, 0));
3232 
3233   return layer.forget();
3234 }
3235 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)3236 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
3237                                 gfxContext* aCtx) {
3238   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
3239   DrawTarget* drawTarget = aCtx->GetDrawTarget();
3240   Rect rect =
3241       NSRectToSnappedRect(mVisibleRect, appUnitsPerDevPixel, *drawTarget);
3242   drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
3243 }
3244 
WriteDebugInfo(std::stringstream & aStream)3245 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) {
3246   aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
3247           << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
3248           << ")";
3249 }
3250 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)3251 bool nsDisplaySolidColor::CreateWebRenderCommands(
3252     mozilla::wr::DisplayListBuilder& aBuilder,
3253     mozilla::wr::IpcResourceUpdateQueue& aResources,
3254     const StackingContextHelper& aSc,
3255     mozilla::layers::WebRenderLayerManager* aManager,
3256     nsDisplayListBuilder* aDisplayListBuilder) {
3257   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
3258       mVisibleRect, mFrame->PresContext()->AppUnitsPerDevPixel());
3259   wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(bounds);
3260 
3261   aBuilder.PushRect(transformedRect, transformedRect, !BackfaceIsHidden(),
3262                     wr::ToColorF(ToDeviceColor(mColor)));
3263 
3264   return true;
3265 }
3266 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const3267 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
3268                                             bool* aSnap) const {
3269   *aSnap = true;
3270   return mRegion.GetBounds();
3271 }
3272 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)3273 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
3274                                       gfxContext* aCtx) {
3275   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
3276   DrawTarget* drawTarget = aCtx->GetDrawTarget();
3277   ColorPattern color(mColor);
3278   for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
3279     Rect rect =
3280         NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
3281     drawTarget->FillRect(rect, color);
3282   }
3283 }
3284 
WriteDebugInfo(std::stringstream & aStream)3285 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) {
3286   aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255)
3287           << "," << int(mColor.b * 255) << "," << mColor.a << ")";
3288 }
3289 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)3290 bool nsDisplaySolidColorRegion::CreateWebRenderCommands(
3291     mozilla::wr::DisplayListBuilder& aBuilder,
3292     mozilla::wr::IpcResourceUpdateQueue& aResources,
3293     const StackingContextHelper& aSc,
3294     mozilla::layers::WebRenderLayerManager* aManager,
3295     nsDisplayListBuilder* aDisplayListBuilder) {
3296   for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
3297     nsRect rect = iter.Get();
3298     LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
3299         rect, mFrame->PresContext()->AppUnitsPerDevPixel());
3300     wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(layerRects);
3301     aBuilder.PushRect(transformedRect, transformedRect, !BackfaceIsHidden(),
3302                       wr::ToColorF(ToDeviceColor(mColor)));
3303   }
3304 
3305   return true;
3306 }
3307 
RegisterThemeGeometry(nsDisplayListBuilder * aBuilder,nsDisplayItem * aItem,nsIFrame * aFrame,nsITheme::ThemeGeometryType aType)3308 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder,
3309                                   nsDisplayItem* aItem, nsIFrame* aFrame,
3310                                   nsITheme::ThemeGeometryType aType) {
3311   if (aBuilder->IsInChromeDocumentOrPopup() && !aBuilder->IsInTransform()) {
3312     nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
3313     nsPoint offset = aBuilder->IsInSubdocument()
3314                          ? aBuilder->ToReferenceFrame(aFrame)
3315                          : aFrame->GetOffsetTo(displayRoot);
3316     nsRect borderBox = nsRect(offset, aFrame->GetSize());
3317     aBuilder->RegisterThemeGeometry(
3318         aType, aItem,
3319         LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels(
3320             aFrame->PresContext()->AppUnitsPerDevPixel())));
3321   }
3322 }
3323 
3324 // Return the bounds of the viewport relative to |aFrame|'s reference frame.
3325 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails.
GetViewportRectRelativeToReferenceFrame(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)3326 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
3327     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
3328   nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
3329   nsRect rootRect = rootFrame->GetRectRelativeToSelf();
3330   if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) ==
3331       nsLayoutUtils::TRANSFORM_SUCCEEDED) {
3332     return Some(rootRect + aBuilder->ToReferenceFrame(aFrame));
3333   }
3334   return Nothing();
3335 }
3336 
3337 /* static */ nsDisplayBackgroundImage::InitData
GetInitData(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,uint32_t aLayer,const nsRect & aBackgroundRect,const nsStyleBackground * aBackgroundStyle)3338 nsDisplayBackgroundImage::GetInitData(
3339     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, uint32_t aLayer,
3340     const nsRect& aBackgroundRect, const nsStyleBackground* aBackgroundStyle) {
3341   nsPresContext* presContext = aFrame->PresContext();
3342   uint32_t flags = aBuilder->GetBackgroundPaintFlags();
3343   const nsStyleImageLayers::Layer& layer =
3344       aBackgroundStyle->mImage.mLayers[aLayer];
3345 
3346   bool isTransformedFixed;
3347   nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
3348       presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer,
3349       &isTransformedFixed);
3350 
3351   // background-attachment:fixed is treated as background-attachment:scroll
3352   // if it's affected by a transform.
3353   // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
3354   bool shouldTreatAsFixed =
3355       layer.mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED &&
3356       !isTransformedFixed;
3357 
3358   bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsEmpty();
3359   bool isRasterImage = state.mImageRenderer.IsRasterImage();
3360   nsCOMPtr<imgIContainer> image;
3361   if (isRasterImage) {
3362     image = state.mImageRenderer.GetImage();
3363   }
3364   return InitData{aBuilder,         aFrame,
3365                   aBackgroundStyle, image,
3366                   aBackgroundRect,  state.mFillArea,
3367                   state.mDestArea,  aLayer,
3368                   isRasterImage,    shouldFixToViewport};
3369 }
3370 
nsDisplayBackgroundImage(nsDisplayListBuilder * aBuilder,const InitData & aInitData,nsIFrame * aFrameForBounds)3371 nsDisplayBackgroundImage::nsDisplayBackgroundImage(
3372     nsDisplayListBuilder* aBuilder, const InitData& aInitData,
3373     nsIFrame* aFrameForBounds)
3374     : nsDisplayImageContainer(aBuilder, aInitData.frame),
3375       mBackgroundStyle(aInitData.backgroundStyle),
3376       mImage(aInitData.image),
3377       mDependentFrame(nullptr),
3378       mBackgroundRect(aInitData.backgroundRect),
3379       mFillRect(aInitData.fillArea),
3380       mDestRect(aInitData.destArea),
3381       mLayer(aInitData.layer),
3382       mIsRasterImage(aInitData.isRasterImage),
3383       mShouldFixToViewport(aInitData.shouldFixToViewport),
3384       mImageFlags(0) {
3385   MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
3386 
3387   mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds);
3388   if (mShouldFixToViewport) {
3389     mAnimatedGeometryRoot =
3390         aInitData.builder->FindAnimatedGeometryRootFor(this);
3391 
3392     // Expand the item's visible rect to cover the entire bounds, limited to the
3393     // viewport rect. This is necessary because the background's clip can move
3394     // asynchronously.
3395     if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame(
3396             aInitData.builder, mFrame)) {
3397       SetVisibleRect(mBounds.Intersect(*viewportRect), true);
3398     }
3399   }
3400 }
3401 
~nsDisplayBackgroundImage()3402 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
3403 #ifdef NS_BUILD_REFCNT_LOGGING
3404   MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
3405 #endif
3406   if (mDependentFrame) {
3407     mDependentFrame->RemoveDisplayItem(this);
3408   }
3409 }
3410 
GetBackgroundStyleContextFrame(nsIFrame * aFrame)3411 static nsIFrame* GetBackgroundStyleContextFrame(nsIFrame* aFrame) {
3412   nsIFrame* f;
3413   if (!nsCSSRendering::FindBackgroundFrame(aFrame, &f)) {
3414     // We don't want to bail out if moz-appearance is set on a root
3415     // node. If it has a parent content node, bail because it's not
3416     // a root, other wise keep going in order to let the theme stuff
3417     // draw the background. The canvas really should be drawing the
3418     // bg, but there's no way to hook that up via css.
3419     if (!aFrame->StyleDisplay()->mAppearance) {
3420       return nullptr;
3421     }
3422 
3423     nsIContent* content = aFrame->GetContent();
3424     if (!content || content->GetParent()) {
3425       return nullptr;
3426     }
3427 
3428     f = aFrame;
3429   }
3430   return f;
3431 }
3432 
SetBackgroundClipRegion(DisplayListClipState::AutoSaveRestore & aClipState,nsIFrame * aFrame,const nsPoint & aToReferenceFrame,const nsStyleImageLayers::Layer & aLayer,const nsRect & aBackgroundRect,bool aWillPaintBorder)3433 static void SetBackgroundClipRegion(
3434     DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame,
3435     const nsPoint& aToReferenceFrame, const nsStyleImageLayers::Layer& aLayer,
3436     const nsRect& aBackgroundRect, bool aWillPaintBorder) {
3437   nsCSSRendering::ImageLayerClipState clip;
3438   nsCSSRendering::GetImageLayerClip(
3439       aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect,
3440       aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3441 
3442   if (clip.mHasAdditionalBGClipArea) {
3443     aClipState.ClipContentDescendants(
3444         clip.mAdditionalBGClipArea, clip.mBGClipArea,
3445         clip.mHasRoundedCorners ? clip.mRadii : nullptr);
3446   } else {
3447     aClipState.ClipContentDescendants(
3448         clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
3449   }
3450 }
3451 
3452 /**
3453  * This is used for the find bar highlighter overlay. It's only accessible
3454  * through the AnonymousContent API, so it's not exposed to general web pages.
3455  */
SpecialCutoutRegionCase(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsRect & aBackgroundRect,nsDisplayList * aList,nscolor aColor)3456 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
3457                                     nsIFrame* aFrame,
3458                                     const nsRect& aBackgroundRect,
3459                                     nsDisplayList* aList, nscolor aColor) {
3460   nsIContent* content = aFrame->GetContent();
3461   if (!content) {
3462     return false;
3463   }
3464 
3465   void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
3466   if (!cutoutRegion) {
3467     return false;
3468   }
3469 
3470   if (NS_GET_A(aColor) == 0) {
3471     return true;
3472   }
3473 
3474   nsRegion region;
3475   region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
3476   region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
3477   aList->AppendToTop(MakeDisplayItem<nsDisplaySolidColorRegion>(
3478       aBuilder, aFrame, region, aColor));
3479 
3480   return true;
3481 }
3482 
AppendBackgroundItemsToTop(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsRect & aBackgroundRect,nsDisplayList * aList,bool aAllowWillPaintBorderOptimization,nsStyleContext * aStyleContext,const nsRect & aBackgroundOriginRect,nsIFrame * aSecondaryReferenceFrame)3483 /*static*/ bool nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
3484     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3485     const nsRect& aBackgroundRect, nsDisplayList* aList,
3486     bool aAllowWillPaintBorderOptimization, nsStyleContext* aStyleContext,
3487     const nsRect& aBackgroundOriginRect, nsIFrame* aSecondaryReferenceFrame) {
3488   nsStyleContext* bgSC = aStyleContext;
3489   const nsStyleBackground* bg = nullptr;
3490   nsRect bgRect = aBackgroundRect + aBuilder->ToReferenceFrame(aFrame);
3491   nsRect bgOriginRect = bgRect;
3492   if (!aBackgroundOriginRect.IsEmpty()) {
3493     bgOriginRect = aBackgroundOriginRect + aBuilder->ToReferenceFrame(aFrame);
3494   }
3495   nsPresContext* presContext = aFrame->PresContext();
3496   bool isThemed = aFrame->IsThemed();
3497   nsIFrame* dependentFrame = nullptr;
3498   if (!isThemed) {
3499     if (!bgSC) {
3500       dependentFrame = GetBackgroundStyleContextFrame(aFrame);
3501       if (dependentFrame) {
3502         bgSC = dependentFrame->StyleContext();
3503         if (dependentFrame == aFrame) {
3504           dependentFrame = nullptr;
3505         }
3506       }
3507     }
3508     if (bgSC) {
3509       bg = bgSC->StyleBackground();
3510     }
3511   }
3512 
3513   bool drawBackgroundColor = false;
3514   // Dummy initialisation to keep Valgrind/Memcheck happy.
3515   // See bug 1122375 comment 1.
3516   nscolor color = NS_RGBA(0, 0, 0, 0);
3517   if (!nsCSSRendering::IsCanvasFrame(aFrame) && bg) {
3518     bool drawBackgroundImage;
3519     color = nsCSSRendering::DetermineBackgroundColor(
3520         presContext, bgSC, aFrame, drawBackgroundImage, drawBackgroundColor);
3521   }
3522 
3523   if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList,
3524                               color)) {
3525     return false;
3526   }
3527 
3528   const nsStyleBorder* borderStyle = aFrame->StyleBorder();
3529   const nsStyleEffects* effectsStyle = aFrame->StyleEffects();
3530   bool hasInsetShadow = effectsStyle->mBoxShadow &&
3531                         effectsStyle->mBoxShadow->HasShadowWithInset(true);
3532   bool willPaintBorder = aAllowWillPaintBorderOptimization && !isThemed &&
3533                          !hasInsetShadow && borderStyle->HasBorder();
3534 
3535   nsPoint toRef = aBuilder->ToReferenceFrame(aFrame);
3536 
3537   // An auxiliary list is necessary in case we have background blending; if that
3538   // is the case, background items need to be wrapped by a blend container to
3539   // isolate blending to the background
3540   nsDisplayList bgItemList;
3541   // Even if we don't actually have a background color to paint, we may still
3542   // need to create an item for hit testing.
3543   if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) ||
3544       aBuilder->IsForEventDelivery()) {
3545     Maybe<DisplayListClipState::AutoSaveRestore> clipState;
3546     nsRect bgColorRect = bgRect;
3547     if (bg && !aBuilder->IsForEventDelivery()) {
3548       // Disable the will-paint-border optimization for background
3549       // colors with no border-radius. Enabling it for background colors
3550       // doesn't help much (there are no tiling issues) and clipping the
3551       // background breaks detection of the element's border-box being
3552       // opaque. For nonzero border-radius we still need it because we
3553       // want to inset the background if possible to avoid antialiasing
3554       // artifacts along the rounded corners.
3555       bool useWillPaintBorderOptimization =
3556           willPaintBorder &&
3557           nsLayoutUtils::HasNonZeroCorner(borderStyle->mBorderRadius);
3558 
3559       nsCSSRendering::ImageLayerClipState clip;
3560       nsCSSRendering::GetImageLayerClip(
3561           bg->BottomLayer(), aFrame, *aFrame->StyleBorder(), bgRect, bgRect,
3562           useWillPaintBorderOptimization,
3563           aFrame->PresContext()->AppUnitsPerDevPixel(), &clip);
3564 
3565       bgColorRect = bgColorRect.Intersect(clip.mBGClipArea);
3566       if (clip.mHasAdditionalBGClipArea) {
3567         bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea);
3568       }
3569       if (clip.mHasRoundedCorners) {
3570         clipState.emplace(aBuilder);
3571         clipState->ClipContentDescendants(clip.mBGClipArea, clip.mRadii);
3572       }
3573     }
3574     nsDisplayBackgroundColor* bgItem;
3575     if (aSecondaryReferenceFrame) {
3576       bgItem = MakeDisplayItem<nsDisplayTableBackgroundColor>(
3577           aBuilder, aSecondaryReferenceFrame, bgColorRect, bg,
3578           drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0), aFrame);
3579     } else {
3580       bgItem = MakeDisplayItem<nsDisplayBackgroundColor>(
3581           aBuilder, aFrame, bgColorRect, bg,
3582           drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0));
3583     }
3584     bgItem->SetDependentFrame(aBuilder, dependentFrame);
3585     bgItemList.AppendToTop(bgItem);
3586   }
3587 
3588   if (isThemed) {
3589     nsITheme* theme = presContext->GetTheme();
3590     if (theme->NeedToClearBackgroundBehindWidget(
3591             aFrame, aFrame->StyleDisplay()->mAppearance) &&
3592         aBuilder->IsInChromeDocumentOrPopup() && !aBuilder->IsInTransform()) {
3593       bgItemList.AppendToTop(
3594           MakeDisplayItem<nsDisplayClearBackground>(aBuilder, aFrame));
3595     }
3596     if (aSecondaryReferenceFrame) {
3597       nsDisplayTableThemedBackground* bgItem =
3598           MakeDisplayItem<nsDisplayTableThemedBackground>(
3599               aBuilder, aSecondaryReferenceFrame, bgRect, aFrame);
3600       bgItem->Init(aBuilder);
3601       bgItemList.AppendToTop(bgItem);
3602     } else {
3603       nsDisplayThemedBackground* bgItem =
3604           MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, bgRect);
3605       bgItem->Init(aBuilder);
3606       bgItemList.AppendToTop(bgItem);
3607     }
3608     aList->AppendToTop(&bgItemList);
3609     return true;
3610   }
3611 
3612   if (!bg) {
3613     aList->AppendToTop(&bgItemList);
3614     return false;
3615   }
3616 
3617   const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
3618 
3619   bool needBlendContainer = false;
3620 
3621   // Passing bg == nullptr in this macro will result in one iteration with
3622   // i = 0.
3623   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) {
3624     if (bg->mImage.mLayers[i].mImage.IsEmpty()) {
3625       continue;
3626     }
3627 
3628     if (bg->mImage.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
3629       needBlendContainer = true;
3630     }
3631 
3632     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3633     if (!aBuilder->IsForEventDelivery()) {
3634       const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
3635       SetBackgroundClipRegion(clipState, aFrame, toRef, layer, bgRect,
3636                               willPaintBorder);
3637     }
3638 
3639     nsDisplayList thisItemList;
3640     nsDisplayBackgroundImage::InitData bgData =
3641         nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect,
3642                                               bg);
3643 
3644     if (bgData.shouldFixToViewport) {
3645       auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
3646       nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3647           aBuilder, aFrame, aBuilder->GetVisibleRect(),
3648           aBuilder->GetDirtyRect(), false);
3649 
3650       nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
3651           aBuilder);
3652       if (displayData) {
3653         asrSetter.SetCurrentActiveScrolledRoot(
3654             displayData->mContainingBlockActiveScrolledRoot);
3655         if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) {
3656           // Override the dirty rect on the builder to be the dirty rect of
3657           // the viewport.
3658           // displayData->mDirtyRect is relative to the presshell's viewport
3659           // frame (the root frame), and we need it to be relative to aFrame.
3660           nsIFrame* rootFrame =
3661               aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame();
3662           // There cannot be any transforms between aFrame and rootFrame
3663           // because then bgData.shouldFixToViewport would have been false.
3664           nsRect visibleRect =
3665               displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame);
3666           aBuilder->SetVisibleRect(visibleRect);
3667           nsRect dirtyRect =
3668               displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame);
3669           aBuilder->SetDirtyRect(dirtyRect);
3670         }
3671       }
3672       nsDisplayBackgroundImage* bgItem = nullptr;
3673       {
3674         // The clip is captured by the nsDisplayFixedPosition, so clear the
3675         // clip for the nsDisplayBackgroundImage inside.
3676         DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
3677         bgImageClip.Clear();
3678         if (aSecondaryReferenceFrame) {
3679           nsDisplayBackgroundImage::InitData tableData = bgData;
3680           nsIFrame* styleFrame = tableData.frame;
3681           tableData.frame = aSecondaryReferenceFrame;
3682           bgItem = MakeDisplayItem<nsDisplayTableBackgroundImage>(
3683               aBuilder, tableData, styleFrame);
3684         } else {
3685           bgItem = MakeDisplayItem<nsDisplayBackgroundImage>(aBuilder, bgData);
3686         }
3687       }
3688       bgItem->SetDependentFrame(aBuilder, dependentFrame);
3689       if (aSecondaryReferenceFrame) {
3690         thisItemList.AppendToTop(
3691             nsDisplayTableFixedPosition::CreateForFixedBackground(
3692                 aBuilder, aSecondaryReferenceFrame, bgItem, i, aFrame));
3693       } else {
3694         thisItemList.AppendToTop(
3695             nsDisplayFixedPosition::CreateForFixedBackground(aBuilder, aFrame,
3696                                                              bgItem, i));
3697       }
3698 
3699     } else {
3700       nsDisplayBackgroundImage* bgItem;
3701       if (aSecondaryReferenceFrame) {
3702         nsDisplayBackgroundImage::InitData tableData = bgData;
3703         nsIFrame* styleFrame = tableData.frame;
3704         tableData.frame = aSecondaryReferenceFrame;
3705 
3706         bgItem = MakeDisplayItem<nsDisplayTableBackgroundImage>(
3707             aBuilder, tableData, styleFrame);
3708       } else {
3709         bgItem = MakeDisplayItem<nsDisplayBackgroundImage>(aBuilder, bgData);
3710       }
3711       bgItem->SetDependentFrame(aBuilder, dependentFrame);
3712       thisItemList.AppendToTop(bgItem);
3713     }
3714 
3715     if (bg->mImage.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
3716       DisplayListClipState::AutoSaveRestore blendClip(aBuilder);
3717       // asr is scrolled. Even if we wrap a fixed background layer, that's
3718       // fine, because the item will have a scrolled clip that limits the
3719       // item with respect to asr.
3720       if (aSecondaryReferenceFrame) {
3721         thisItemList.AppendToTop(MakeDisplayItem<nsDisplayTableBlendMode>(
3722             aBuilder, aSecondaryReferenceFrame, &thisItemList,
3723             bg->mImage.mLayers[i].mBlendMode, asr, i + 1, aFrame));
3724       } else {
3725         thisItemList.AppendToTop(MakeDisplayItem<nsDisplayBlendMode>(
3726             aBuilder, aFrame, &thisItemList, bg->mImage.mLayers[i].mBlendMode,
3727             asr, i + 1));
3728       }
3729     }
3730     bgItemList.AppendToTop(&thisItemList);
3731   }
3732 
3733   if (needBlendContainer) {
3734     DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder);
3735     if (aSecondaryReferenceFrame) {
3736       bgItemList.AppendToTop(
3737           nsDisplayTableBlendContainer::CreateForBackgroundBlendMode(
3738               aBuilder, aSecondaryReferenceFrame, &bgItemList, asr, aFrame));
3739     } else {
3740       bgItemList.AppendToTop(
3741           nsDisplayBlendContainer::CreateForBackgroundBlendMode(
3742               aBuilder, aFrame, &bgItemList, asr));
3743     }
3744   }
3745 
3746   aList->AppendToTop(&bgItemList);
3747   return false;
3748 }
3749 
3750 // Check that the rounded border of aFrame, added to aToReferenceFrame,
3751 // intersects aRect.  Assumes that the unrounded border has already
3752 // been checked for intersection.
RoundedBorderIntersectsRect(nsIFrame * aFrame,const nsPoint & aFrameToReferenceFrame,const nsRect & aTestRect)3753 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame,
3754                                         const nsPoint& aFrameToReferenceFrame,
3755                                         const nsRect& aTestRect) {
3756   if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize()).Intersects(aTestRect))
3757     return false;
3758 
3759   nscoord radii[8];
3760   return !aFrame->GetBorderRadii(radii) ||
3761          nsLayoutUtils::RoundedRectIntersectsRect(
3762              nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii,
3763              aTestRect);
3764 }
3765 
3766 // Returns TRUE if aContainedRect is guaranteed to be contained in
3767 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
3768 // handled conservatively by returning FALSE in some situations where
3769 // a more thorough analysis could return TRUE.
3770 //
3771 // See also RoundedRectIntersectsRect.
RoundedRectContainsRect(const nsRect & aRoundedRect,const nscoord aRadii[8],const nsRect & aContainedRect)3772 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
3773                                     const nscoord aRadii[8],
3774                                     const nsRect& aContainedRect) {
3775   nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii,
3776                                                          aContainedRect);
3777   return rgn.Contains(aContainedRect);
3778 }
3779 
CanOptimizeToImageLayer(LayerManager * aManager,nsDisplayListBuilder * aBuilder)3780 bool nsDisplayBackgroundImage::CanOptimizeToImageLayer(
3781     LayerManager* aManager, nsDisplayListBuilder* aBuilder) {
3782   if (!mBackgroundStyle) {
3783     return false;
3784   }
3785 
3786   // We currently can't handle tiled backgrounds.
3787   if (!mDestRect.Contains(mFillRect)) {
3788     return false;
3789   }
3790 
3791   // For 'contain' and 'cover', we allow any pixel of the image to be sampled
3792   // because there isn't going to be any spriting/atlasing going on.
3793   const nsStyleImageLayers::Layer& layer =
3794       mBackgroundStyle->mImage.mLayers[mLayer];
3795   bool allowPartialImages =
3796       (layer.mSize.mWidthType == nsStyleImageLayers::Size::eContain ||
3797        layer.mSize.mWidthType == nsStyleImageLayers::Size::eCover);
3798   if (!allowPartialImages && !mFillRect.Contains(mDestRect)) {
3799     return false;
3800   }
3801 
3802   return nsDisplayImageContainer::CanOptimizeToImageLayer(aManager, aBuilder);
3803 }
3804 
GetDestRect() const3805 nsRect nsDisplayBackgroundImage::GetDestRect() const { return mDestRect; }
3806 
GetImage()3807 already_AddRefed<imgIContainer> nsDisplayBackgroundImage::GetImage() {
3808   nsCOMPtr<imgIContainer> image = mImage;
3809   return image.forget();
3810 }
3811 
3812 nsDisplayBackgroundImage::ImageLayerization
ShouldCreateOwnLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager)3813 nsDisplayBackgroundImage::ShouldCreateOwnLayer(nsDisplayListBuilder* aBuilder,
3814                                                LayerManager* aManager) {
3815   if (ForceActiveLayers()) {
3816     return WHENEVER_POSSIBLE;
3817   }
3818 
3819   nsIFrame* backgroundStyleFrame =
3820       nsCSSRendering::FindBackgroundStyleFrame(StyleFrame());
3821   if (ActiveLayerTracker::IsBackgroundPositionAnimated(aBuilder,
3822                                                        backgroundStyleFrame)) {
3823     return WHENEVER_POSSIBLE;
3824   }
3825 
3826   if (nsLayoutUtils::AnimatedImageLayersEnabled() && mBackgroundStyle) {
3827     const nsStyleImageLayers::Layer& layer =
3828         mBackgroundStyle->mImage.mLayers[mLayer];
3829     const nsStyleImage* image = &layer.mImage;
3830     if (image->GetType() == eStyleImageType_Image) {
3831       imgIRequest* imgreq = image->GetImageData();
3832       nsCOMPtr<imgIContainer> image;
3833       if (imgreq && NS_SUCCEEDED(imgreq->GetImage(getter_AddRefs(image))) &&
3834           image) {
3835         bool animated = false;
3836         if (NS_SUCCEEDED(image->GetAnimated(&animated)) && animated) {
3837           return WHENEVER_POSSIBLE;
3838         }
3839       }
3840     }
3841   }
3842 
3843   if (nsLayoutUtils::GPUImageScalingEnabled() &&
3844       aManager->IsCompositingCheap()) {
3845     return ONLY_FOR_SCALING;
3846   }
3847 
3848   return NO_LAYER_NEEDED;
3849 }
3850 
CheckForBorderItem(nsDisplayItem * aItem,uint32_t & aFlags)3851 static void CheckForBorderItem(nsDisplayItem* aItem, uint32_t& aFlags) {
3852   nsDisplayItem* nextItem = aItem->GetAbove();
3853   while (nextItem && nextItem->GetType() == DisplayItemType::TYPE_BACKGROUND) {
3854     nextItem = nextItem->GetAbove();
3855   }
3856   if (nextItem && nextItem->Frame() == aItem->Frame() &&
3857       nextItem->GetType() == DisplayItemType::TYPE_BORDER) {
3858     aFlags |= nsCSSRendering::PAINTBG_WILL_PAINT_BORDER;
3859   }
3860 }
3861 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)3862 LayerState nsDisplayBackgroundImage::GetLayerState(
3863     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
3864     const ContainerLayerParameters& aParameters) {
3865   mImageFlags = aBuilder->GetBackgroundPaintFlags();
3866   CheckForBorderItem(this, mImageFlags);
3867 
3868   if (ShouldUseAdvancedLayer(aManager, gfxPrefs::LayersAllowBackgroundImage) &&
3869       CanBuildWebRenderDisplayItems(aManager)) {
3870     return LAYER_ACTIVE;
3871   }
3872 
3873   ImageLayerization shouldLayerize = ShouldCreateOwnLayer(aBuilder, aManager);
3874   if (shouldLayerize == NO_LAYER_NEEDED) {
3875     // We can skip the call to CanOptimizeToImageLayer if we don't want a
3876     // layer anyway.
3877     return LAYER_NONE;
3878   }
3879 
3880   if (CanOptimizeToImageLayer(aManager, aBuilder)) {
3881     if (shouldLayerize == WHENEVER_POSSIBLE) {
3882       return LAYER_ACTIVE;
3883     }
3884 
3885     MOZ_ASSERT(shouldLayerize == ONLY_FOR_SCALING,
3886                "unhandled ImageLayerization value?");
3887 
3888     MOZ_ASSERT(mImage);
3889     int32_t imageWidth;
3890     int32_t imageHeight;
3891     mImage->GetWidth(&imageWidth);
3892     mImage->GetHeight(&imageHeight);
3893     NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
3894 
3895     int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
3896     LayoutDeviceRect destRect =
3897         LayoutDeviceRect::FromAppUnits(GetDestRect(), appUnitsPerDevPixel);
3898 
3899     const LayerRect destLayerRect = destRect * aParameters.Scale();
3900 
3901     // Calculate the scaling factor for the frame.
3902     const gfxSize scale = gfxSize(destLayerRect.width / imageWidth,
3903                                   destLayerRect.height / imageHeight);
3904 
3905     if ((scale.width != 1.0f || scale.height != 1.0f) &&
3906         (destLayerRect.width * destLayerRect.height >= 64 * 64)) {
3907       // Separate this image into a layer.
3908       // There's no point in doing this if we are not scaling at all or if the
3909       // target size is pretty small.
3910       return LAYER_ACTIVE;
3911     }
3912   }
3913 
3914   return LAYER_NONE;
3915 }
3916 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)3917 already_AddRefed<Layer> nsDisplayBackgroundImage::BuildLayer(
3918     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
3919     const ContainerLayerParameters& aParameters) {
3920   if (ShouldUseAdvancedLayer(aManager, gfxPrefs::LayersAllowBackgroundImage)) {
3921     return BuildDisplayItemLayer(aBuilder, aManager, aParameters);
3922   }
3923 
3924   RefPtr<ImageLayer> layer = static_cast<ImageLayer*>(
3925       aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
3926   if (!layer) {
3927     layer = aManager->CreateImageLayer();
3928     if (!layer) return nullptr;
3929   }
3930   RefPtr<ImageContainer> imageContainer = GetContainer(aManager, aBuilder);
3931   layer->SetContainer(imageContainer);
3932   ConfigureLayer(layer, aParameters);
3933   return layer.forget();
3934 }
3935 
CanBuildWebRenderDisplayItems(LayerManager * aManager)3936 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems(
3937     LayerManager* aManager) {
3938   return mBackgroundStyle->mImage.mLayers[mLayer].mClip !=
3939              StyleGeometryBox::Text &&
3940          nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
3941              aManager, *StyleFrame()->PresContext(), StyleFrame(),
3942              mBackgroundStyle, mLayer);
3943 }
3944 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)3945 bool nsDisplayBackgroundImage::CreateWebRenderCommands(
3946     mozilla::wr::DisplayListBuilder& aBuilder,
3947     mozilla::wr::IpcResourceUpdateQueue& aResources,
3948     const StackingContextHelper& aSc, WebRenderLayerManager* aManager,
3949     nsDisplayListBuilder* aDisplayListBuilder) {
3950   ContainerLayerParameters parameter;
3951   if (GetLayerState(aDisplayListBuilder, aManager, parameter) != LAYER_ACTIVE) {
3952     return false;
3953   }
3954 
3955   if (aDisplayListBuilder) {
3956     mImageFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
3957   }
3958   CheckForBorderItem(this, mImageFlags);
3959   nsCSSRendering::PaintBGParams params =
3960       nsCSSRendering::PaintBGParams::ForSingleLayer(
3961           *StyleFrame()->PresContext(), mVisibleRect, mBackgroundRect,
3962           StyleFrame(), mImageFlags, mLayer, CompositionOp::OP_OVER);
3963   params.bgClipRect = &mBounds;
3964   ImgDrawResult result =
3965       nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
3966           params, aBuilder, aResources, aSc, aManager, this);
3967   nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
3968 
3969   return true;
3970 }
3971 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)3972 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
3973                                        const nsRect& aRect,
3974                                        HitTestState* aState,
3975                                        nsTArray<nsIFrame*>* aOutFrames) {
3976   if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3977     aOutFrames->AppendElement(mFrame);
3978   }
3979 }
3980 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)3981 bool nsDisplayBackgroundImage::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3982                                                  nsRegion* aVisibleRegion) {
3983   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion)) {
3984     return false;
3985   }
3986 
3987   // Return false if the background was propagated away from this
3988   // frame. We don't want this display item to show up and confuse
3989   // anything.
3990   return mBackgroundStyle;
3991 }
3992 
GetInsideClipRegion(const nsDisplayItem * aItem,StyleGeometryBox aClip,const nsRect & aRect,const nsRect & aBackgroundRect)3993 /* static */ nsRegion nsDisplayBackgroundImage::GetInsideClipRegion(
3994     const nsDisplayItem* aItem, StyleGeometryBox aClip, const nsRect& aRect,
3995     const nsRect& aBackgroundRect) {
3996   nsRegion result;
3997   if (aRect.IsEmpty()) return result;
3998 
3999   nsIFrame* frame = aItem->Frame();
4000 
4001   nsRect clipRect = aBackgroundRect;
4002   if (frame->IsCanvasFrame()) {
4003     nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
4004     clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
4005   } else if (aClip == StyleGeometryBox::PaddingBox ||
4006              aClip == StyleGeometryBox::ContentBox) {
4007     nsMargin border = frame->GetUsedBorder();
4008     if (aClip == StyleGeometryBox::ContentBox) {
4009       border += frame->GetUsedPadding();
4010     }
4011     border.ApplySkipSides(frame->GetSkipSides());
4012     clipRect.Deflate(border);
4013   }
4014 
4015   return clipRect.Intersect(aRect);
4016 }
4017 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const4018 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion(
4019     nsDisplayListBuilder* aBuilder, bool* aSnap) const {
4020   nsRegion result;
4021   *aSnap = false;
4022 
4023   if (!mBackgroundStyle) return result;
4024 
4025   *aSnap = true;
4026 
4027   // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since
4028   // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
4029   // which expects frames to be sent to it in content order, not reverse
4030   // content order which we'll produce here.
4031   // Of course, if there's only one frame in the flow, it doesn't matter.
4032   if (mFrame->StyleBorder()->mBoxDecorationBreak ==
4033           StyleBoxDecorationBreak::Clone ||
4034       (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
4035     const nsStyleImageLayers::Layer& layer =
4036         mBackgroundStyle->mImage.mLayers[mLayer];
4037     if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL &&
4038         layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
4039         layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
4040         layer.mClip != StyleGeometryBox::Text) {
4041       result = GetInsideClipRegion(this, layer.mClip, mBounds, mBackgroundRect);
4042     }
4043   }
4044 
4045   return result;
4046 }
4047 
IsUniform(nsDisplayListBuilder * aBuilder) const4048 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform(
4049     nsDisplayListBuilder* aBuilder) const {
4050   if (!mBackgroundStyle) {
4051     return Some(NS_RGBA(0, 0, 0, 0));
4052   }
4053   return Nothing();
4054 }
4055 
GetPositioningArea() const4056 nsRect nsDisplayBackgroundImage::GetPositioningArea() const {
4057   if (!mBackgroundStyle) {
4058     return nsRect();
4059   }
4060   nsIFrame* attachedToFrame;
4061   bool transformedFixed;
4062   return nsCSSRendering::ComputeImageLayerPositioningArea(
4063              mFrame->PresContext(), mFrame, mBackgroundRect,
4064              mBackgroundStyle->mImage.mLayers[mLayer], &attachedToFrame,
4065              &transformedFixed) +
4066          ToReferenceFrame();
4067 }
4068 
RenderingMightDependOnPositioningAreaSizeChange() const4069 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
4070     const {
4071   if (!mBackgroundStyle) return false;
4072 
4073   nscoord radii[8];
4074   if (mFrame->GetBorderRadii(radii)) {
4075     // A change in the size of the positioning area might change the position
4076     // of the rounded corners.
4077     return true;
4078   }
4079 
4080   const nsStyleImageLayers::Layer& layer =
4081       mBackgroundStyle->mImage.mLayers[mLayer];
4082   if (layer.RenderingMightDependOnPositioningAreaSizeChange()) {
4083     return true;
4084   }
4085   return false;
4086 }
4087 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4088 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
4089                                      gfxContext* aCtx) {
4090   PaintInternal(aBuilder, aCtx, mVisibleRect, &mBounds);
4091 }
4092 
PaintInternal(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,const nsRect & aBounds,nsRect * aClipRect)4093 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
4094                                              gfxContext* aCtx,
4095                                              const nsRect& aBounds,
4096                                              nsRect* aClipRect) {
4097   gfxContext* ctx = aCtx;
4098   StyleGeometryBox clip = mBackgroundStyle->mImage.mLayers[mLayer].mClip;
4099 
4100   if (clip == StyleGeometryBox::Text) {
4101     if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect,
4102                                  aBuilder)) {
4103       return;
4104     }
4105   }
4106 
4107   nsCSSRendering::PaintBGParams params =
4108       nsCSSRendering::PaintBGParams::ForSingleLayer(
4109           *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(),
4110           mImageFlags, mLayer, CompositionOp::OP_OVER);
4111   params.bgClipRect = aClipRect;
4112   ImgDrawResult result = nsCSSRendering::PaintStyleImageLayer(params, *aCtx);
4113 
4114   if (clip == StyleGeometryBox::Text) {
4115     ctx->PopGroupAndBlend();
4116   }
4117 
4118   nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
4119 }
4120 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const4121 void nsDisplayBackgroundImage::ComputeInvalidationRegion(
4122     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4123     nsRegion* aInvalidRegion) const {
4124   if (!mBackgroundStyle) {
4125     return;
4126   }
4127 
4128   const nsDisplayBackgroundGeometry* geometry =
4129       static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
4130 
4131   bool snap;
4132   nsRect bounds = GetBounds(aBuilder, &snap);
4133   nsRect positioningArea = GetPositioningArea();
4134   if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
4135       (positioningArea.Size() != geometry->mPositioningArea.Size() &&
4136        RenderingMightDependOnPositioningAreaSizeChange())) {
4137     // Positioning area changed in a way that could cause everything to change,
4138     // so invalidate everything (both old and new painting areas).
4139     aInvalidRegion->Or(bounds, geometry->mBounds);
4140     return;
4141   }
4142   if (!mDestRect.IsEqualInterior(geometry->mDestRect)) {
4143     // Dest area changed in a way that could cause everything to change,
4144     // so invalidate everything (both old and new painting areas).
4145     aInvalidRegion->Or(bounds, geometry->mBounds);
4146     return;
4147   }
4148   if (aBuilder->ShouldSyncDecodeImages()) {
4149     const nsStyleImage& image = mBackgroundStyle->mImage.mLayers[mLayer].mImage;
4150     if (image.GetType() == eStyleImageType_Image &&
4151         geometry->ShouldInvalidateToSyncDecodeImages()) {
4152       aInvalidRegion->Or(*aInvalidRegion, bounds);
4153     }
4154   }
4155   if (!bounds.IsEqualInterior(geometry->mBounds)) {
4156     // Positioning area is unchanged, so invalidate just the change in the
4157     // painting area.
4158     aInvalidRegion->Xor(bounds, geometry->mBounds);
4159   }
4160 }
4161 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const4162 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder,
4163                                            bool* aSnap) const {
4164   *aSnap = true;
4165   return mBounds;
4166 }
4167 
GetBoundsInternal(nsDisplayListBuilder * aBuilder,nsIFrame * aFrameForBounds)4168 nsRect nsDisplayBackgroundImage::GetBoundsInternal(
4169     nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) {
4170   // This allows nsDisplayTableBackgroundImage to change the frame used for
4171   // bounds calculation.
4172   nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame;
4173 
4174   nsPresContext* presContext = frame->PresContext();
4175 
4176   if (!mBackgroundStyle) {
4177     return nsRect();
4178   }
4179 
4180   nsRect clipRect = mBackgroundRect;
4181   if (frame->IsCanvasFrame()) {
4182     nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
4183     clipRect = canvasFrame->CanvasArea() + ToReferenceFrame();
4184   }
4185   const nsStyleImageLayers::Layer& layer =
4186       mBackgroundStyle->mImage.mLayers[mLayer];
4187   return nsCSSRendering::GetBackgroundLayerRect(
4188       presContext, frame, mBackgroundRect, clipRect, layer,
4189       aBuilder->GetBackgroundPaintFlags());
4190 }
4191 
nsDisplayTableBackgroundImage(nsDisplayListBuilder * aBuilder,const InitData & aData,nsIFrame * aCellFrame)4192 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(
4193     nsDisplayListBuilder* aBuilder, const InitData& aData, nsIFrame* aCellFrame)
4194     : nsDisplayBackgroundImage(aBuilder, aData, aCellFrame),
4195       mStyleFrame(aCellFrame),
4196       mTableType(GetTableTypeFromFrame(mStyleFrame)) {}
4197 
IsInvalid(nsRect & aRect) const4198 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const {
4199   bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
4200   aRect += ToReferenceFrame();
4201   return result;
4202 }
4203 
nsDisplayThemedBackground(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsRect & aBackgroundRect)4204 nsDisplayThemedBackground::nsDisplayThemedBackground(
4205     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4206     const nsRect& aBackgroundRect)
4207     : nsDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) {
4208   MOZ_COUNT_CTOR(nsDisplayThemedBackground);
4209 }
4210 
~nsDisplayThemedBackground()4211 nsDisplayThemedBackground::~nsDisplayThemedBackground() {
4212 #ifdef NS_BUILD_REFCNT_LOGGING
4213   MOZ_COUNT_DTOR(nsDisplayThemedBackground);
4214 #endif
4215 }
4216 
Init(nsDisplayListBuilder * aBuilder)4217 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) {
4218   const nsStyleDisplay* disp = StyleFrame()->StyleDisplay();
4219   mAppearance = disp->mAppearance;
4220   StyleFrame()->IsThemed(disp, &mThemeTransparency);
4221 
4222   // Perform necessary RegisterThemeGeometry
4223   nsITheme* theme = StyleFrame()->PresContext()->GetTheme();
4224   nsITheme::ThemeGeometryType type =
4225       theme->ThemeGeometryTypeForWidget(StyleFrame(), disp->mAppearance);
4226   if (type != nsITheme::eThemeGeometryTypeUnknown) {
4227     RegisterThemeGeometry(aBuilder, this, StyleFrame(), type);
4228   }
4229 
4230   if (disp->mAppearance == NS_THEME_WIN_BORDERLESS_GLASS ||
4231       disp->mAppearance == NS_THEME_WIN_GLASS) {
4232     aBuilder->SetGlassDisplayItem(this);
4233   }
4234 
4235   mBounds = GetBoundsInternal();
4236 }
4237 
WriteDebugInfo(std::stringstream & aStream)4238 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) {
4239   aStream << " (themed, appearance:" << (int)mAppearance << ")";
4240 }
4241 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)4242 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
4243                                         const nsRect& aRect,
4244                                         HitTestState* aState,
4245                                         nsTArray<nsIFrame*>* aOutFrames) {
4246   // Assume that any point in our background rect is a hit.
4247   if (mBackgroundRect.Intersects(aRect)) {
4248     aOutFrames->AppendElement(mFrame);
4249   }
4250 }
4251 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const4252 nsRegion nsDisplayThemedBackground::GetOpaqueRegion(
4253     nsDisplayListBuilder* aBuilder, bool* aSnap) const {
4254   nsRegion result;
4255   *aSnap = false;
4256 
4257   if (mThemeTransparency == nsITheme::eOpaque) {
4258     result = mBackgroundRect;
4259   }
4260   return result;
4261 }
4262 
IsUniform(nsDisplayListBuilder * aBuilder) const4263 Maybe<nscolor> nsDisplayThemedBackground::IsUniform(
4264     nsDisplayListBuilder* aBuilder) const {
4265   if (mAppearance == NS_THEME_WIN_BORDERLESS_GLASS ||
4266       mAppearance == NS_THEME_WIN_GLASS) {
4267     return Some(NS_RGBA(0, 0, 0, 0));
4268   }
4269   return Nothing();
4270 }
4271 
GetPositioningArea() const4272 nsRect nsDisplayThemedBackground::GetPositioningArea() const {
4273   return mBackgroundRect;
4274 }
4275 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4276 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
4277                                       gfxContext* aCtx) {
4278   PaintInternal(aBuilder, aCtx, mVisibleRect, nullptr);
4279 }
4280 
PaintInternal(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,const nsRect & aBounds,nsRect * aClipRect)4281 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
4282                                               gfxContext* aCtx,
4283                                               const nsRect& aBounds,
4284                                               nsRect* aClipRect) {
4285   // XXXzw this ignores aClipRect.
4286   nsPresContext* presContext = StyleFrame()->PresContext();
4287   nsITheme* theme = presContext->GetTheme();
4288   nsRect drawing(mBackgroundRect);
4289   theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(),
4290                            mAppearance, &drawing);
4291   drawing.IntersectRect(drawing, aBounds);
4292   theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect,
4293                               drawing);
4294 }
4295 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4296 bool nsDisplayThemedBackground::CreateWebRenderCommands(
4297     mozilla::wr::DisplayListBuilder& aBuilder,
4298     mozilla::wr::IpcResourceUpdateQueue& aResources,
4299     const StackingContextHelper& aSc,
4300     mozilla::layers::WebRenderLayerManager* aManager,
4301     nsDisplayListBuilder* aDisplayListBuilder) {
4302   nsITheme* theme = StyleFrame()->PresContext()->GetTheme();
4303   return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc,
4304                                                  aManager, StyleFrame(),
4305                                                  mAppearance, mBackgroundRect);
4306 }
4307 
IsWindowActive() const4308 bool nsDisplayThemedBackground::IsWindowActive() const {
4309   EventStates docState = mFrame->GetContent()->OwnerDoc()->GetDocumentState();
4310   return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
4311 }
4312 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const4313 void nsDisplayThemedBackground::ComputeInvalidationRegion(
4314     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
4315     nsRegion* aInvalidRegion) const {
4316   const nsDisplayThemedBackgroundGeometry* geometry =
4317       static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
4318 
4319   bool snap;
4320   nsRect bounds = GetBounds(aBuilder, &snap);
4321   nsRect positioningArea = GetPositioningArea();
4322   if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
4323     // Invalidate everything (both old and new painting areas).
4324     aInvalidRegion->Or(bounds, geometry->mBounds);
4325     return;
4326   }
4327   if (!bounds.IsEqualInterior(geometry->mBounds)) {
4328     // Positioning area is unchanged, so invalidate just the change in the
4329     // painting area.
4330     aInvalidRegion->Xor(bounds, geometry->mBounds);
4331   }
4332   nsITheme* theme = StyleFrame()->PresContext()->GetTheme();
4333   if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
4334       IsWindowActive() != geometry->mWindowIsActive) {
4335     aInvalidRegion->Or(*aInvalidRegion, bounds);
4336   }
4337 }
4338 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const4339 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder,
4340                                             bool* aSnap) const {
4341   *aSnap = true;
4342   return mBounds;
4343 }
4344 
GetBoundsInternal()4345 nsRect nsDisplayThemedBackground::GetBoundsInternal() {
4346   nsPresContext* presContext = mFrame->PresContext();
4347 
4348   nsRect r = mBackgroundRect - ToReferenceFrame();
4349   presContext->GetTheme()->GetWidgetOverflow(
4350       presContext->DeviceContext(), mFrame, mFrame->StyleDisplay()->mAppearance,
4351       &r);
4352   return r + ToReferenceFrame();
4353 }
4354 
ConfigureLayer(ImageLayer * aLayer,const ContainerLayerParameters & aParameters)4355 void nsDisplayImageContainer::ConfigureLayer(
4356     ImageLayer* aLayer, const ContainerLayerParameters& aParameters) {
4357   aLayer->SetSamplingFilter(nsLayoutUtils::GetSamplingFilterForFrame(mFrame));
4358 
4359   nsCOMPtr<imgIContainer> image = GetImage();
4360   MOZ_ASSERT(image);
4361   int32_t imageWidth;
4362   int32_t imageHeight;
4363   image->GetWidth(&imageWidth);
4364   image->GetHeight(&imageHeight);
4365   NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
4366 
4367   if (imageWidth > 0 && imageHeight > 0) {
4368     // We're actually using the ImageContainer. Let our frame know that it
4369     // should consider itself to have painted successfully.
4370     UpdateDrawResult(ImgDrawResult::SUCCESS);
4371   }
4372 
4373   // XXX(seth): Right now we ignore aParameters.Scale() and
4374   // aParameters.Offset(), because FrameLayerBuilder already applies
4375   // aParameters.Scale() via the layer's post-transform, and
4376   // aParameters.Offset() is always zero.
4377   MOZ_ASSERT(aParameters.Offset() == LayerIntPoint(0, 0));
4378 
4379   // It's possible (for example, due to downscale-during-decode) that the
4380   // ImageContainer this ImageLayer is holding has a different size from the
4381   // intrinsic size of the image. For this reason we compute the transform using
4382   // the ImageContainer's size rather than the image's intrinsic size.
4383   // XXX(seth): In reality, since the size of the ImageContainer may change
4384   // asynchronously, this is not enough. Bug 1183378 will provide a more
4385   // complete fix, but this solution is safe in more cases than simply relying
4386   // on the intrinsic size.
4387   IntSize containerSize = aLayer->GetContainer()
4388                               ? aLayer->GetContainer()->GetCurrentSize()
4389                               : IntSize(imageWidth, imageHeight);
4390 
4391   const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
4392   const LayoutDeviceRect destRect(
4393       LayoutDeviceIntRect::FromAppUnitsToNearest(GetDestRect(), factor));
4394 
4395   const LayoutDevicePoint p = destRect.TopLeft();
4396   Matrix transform = Matrix::Translation(p.x, p.y);
4397   transform.PreScale(destRect.width / containerSize.width,
4398                      destRect.height / containerSize.height);
4399   aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
4400 }
4401 
GetContainer(LayerManager * aManager,nsDisplayListBuilder * aBuilder)4402 already_AddRefed<ImageContainer> nsDisplayImageContainer::GetContainer(
4403     LayerManager* aManager, nsDisplayListBuilder* aBuilder) {
4404   nsCOMPtr<imgIContainer> image = GetImage();
4405   if (!image) {
4406     MOZ_ASSERT_UNREACHABLE(
4407         "Must call CanOptimizeToImage() and get true "
4408         "before calling GetContainer()");
4409     return nullptr;
4410   }
4411 
4412   uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
4413   if (aBuilder->ShouldSyncDecodeImages()) {
4414     flags |= imgIContainer::FLAG_SYNC_DECODE;
4415   }
4416 
4417   RefPtr<ImageContainer> container = image->GetImageContainer(aManager, flags);
4418   if (!container || !container->HasCurrentImage()) {
4419     return nullptr;
4420   }
4421 
4422   return container.forget();
4423 }
4424 
CanOptimizeToImageLayer(LayerManager * aManager,nsDisplayListBuilder * aBuilder)4425 bool nsDisplayImageContainer::CanOptimizeToImageLayer(
4426     LayerManager* aManager, nsDisplayListBuilder* aBuilder) {
4427   uint32_t flags = aBuilder->ShouldSyncDecodeImages()
4428                        ? imgIContainer::FLAG_SYNC_DECODE
4429                        : imgIContainer::FLAG_NONE;
4430 
4431   nsCOMPtr<imgIContainer> image = GetImage();
4432   if (!image) {
4433     return false;
4434   }
4435 
4436   if (!image->IsImageContainerAvailable(aManager, flags)) {
4437     return false;
4438   }
4439 
4440   int32_t imageWidth;
4441   int32_t imageHeight;
4442   image->GetWidth(&imageWidth);
4443   image->GetHeight(&imageHeight);
4444 
4445   if (imageWidth == 0 || imageHeight == 0) {
4446     NS_ASSERTION(false, "invalid image size");
4447     return false;
4448   }
4449 
4450   const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
4451   const LayoutDeviceRect destRect(
4452       LayoutDeviceIntRect::FromAppUnitsToNearest(GetDestRect(), factor));
4453 
4454   // Calculate the scaling factor for the frame.
4455   const gfxSize scale =
4456       gfxSize(destRect.width / imageWidth, destRect.height / imageHeight);
4457 
4458   if (scale.width < 0.34 || scale.height < 0.34) {
4459     // This would look awful as long as we can't use high-quality downscaling
4460     // for image layers (bug 803703), so don't turn this into an image layer.
4461     return false;
4462   }
4463 
4464   if (mFrame->IsImageFrame()) {
4465     // Image layer doesn't support draw focus ring for image map.
4466     nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
4467     if (f->HasImageMap()) {
4468       return false;
4469     }
4470   }
4471 
4472   return true;
4473 }
4474 
ApplyOpacity(nsDisplayListBuilder * aBuilder,float aOpacity,const DisplayItemClipChain * aClip)4475 void nsDisplayBackgroundColor::ApplyOpacity(nsDisplayListBuilder* aBuilder,
4476                                             float aOpacity,
4477                                             const DisplayItemClipChain* aClip) {
4478   NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
4479   mColor.a = mColor.a * aOpacity;
4480   IntersectClip(aBuilder, aClip, false);
4481 }
4482 
CanApplyOpacity() const4483 bool nsDisplayBackgroundColor::CanApplyOpacity() const { return true; }
4484 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)4485 LayerState nsDisplayBackgroundColor::GetLayerState(
4486     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
4487     const ContainerLayerParameters& aParameters) {
4488   StyleGeometryBox clip = mBackgroundStyle->mImage.mLayers[0].mClip;
4489   if ((ForceActiveLayers() ||
4490        ShouldUseAdvancedLayer(aManager,
4491                               gfxPrefs::LayersAllowBackgroundColorLayers)) &&
4492       clip != StyleGeometryBox::Text) {
4493     return LAYER_ACTIVE;
4494   }
4495   return LAYER_NONE;
4496 }
4497 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)4498 already_AddRefed<Layer> nsDisplayBackgroundColor::BuildLayer(
4499     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
4500     const ContainerLayerParameters& aContainerParameters) {
4501   if (mColor == Color()) {
4502     return nullptr;
4503   }
4504 
4505   RefPtr<ColorLayer> layer = static_cast<ColorLayer*>(
4506       aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
4507   if (!layer) {
4508     layer = aManager->CreateColorLayer();
4509     if (!layer) return nullptr;
4510   }
4511   layer->SetColor(mColor);
4512 
4513   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4514   layer->SetBounds(mBackgroundRect.ToNearestPixels(appUnitsPerDevPixel));
4515   layer->SetBaseTransform(gfx::Matrix4x4::Translation(
4516       aContainerParameters.mOffset.x, aContainerParameters.mOffset.y, 0));
4517 
4518   return layer.forget();
4519 }
4520 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4521 bool nsDisplayBackgroundColor::CreateWebRenderCommands(
4522     mozilla::wr::DisplayListBuilder& aBuilder,
4523     mozilla::wr::IpcResourceUpdateQueue& aResources,
4524     const StackingContextHelper& aSc,
4525     mozilla::layers::WebRenderLayerManager* aManager,
4526     nsDisplayListBuilder* aDisplayListBuilder) {
4527   if (mColor == Color()) {
4528     return true;
4529   }
4530 
4531   StyleGeometryBox clip = mBackgroundStyle->mImage.mLayers[0].mClip;
4532   if (clip == StyleGeometryBox::Text) {
4533     return false;
4534   }
4535 
4536   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
4537       mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
4538   wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(bounds);
4539 
4540   aBuilder.PushRect(transformedRect, transformedRect, !BackfaceIsHidden(),
4541                     wr::ToColorF(ToDeviceColor(mColor)));
4542 
4543   return true;
4544 }
4545 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4546 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
4547                                      gfxContext* aCtx) {
4548   if (mColor == Color()) {
4549     return;
4550   }
4551 
4552 #if 0
4553   // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this
4554   // results in a precision induced rounding issue that makes the rect one
4555   // pixel shorter in rare cases. Disabled in favor of the old code for now.
4556   // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to
4557   // reproduce the bug.
4558   //
4559   // TODO:
4560   // This new path does not include support for background-clip:text; need to
4561   // be fixed if/when we switch to this new code path.
4562 
4563   DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
4564 
4565   Rect rect = NSRectToSnappedRect(mBackgroundRect,
4566                                   mFrame->PresContext()->AppUnitsPerDevPixel(),
4567                                   aDrawTarget);
4568   ColorPattern color(ToDeviceColor(mColor));
4569   aDrawTarget.FillRect(rect, color);
4570 #else
4571   gfxContext* ctx = aCtx;
4572   gfxRect bounds = nsLayoutUtils::RectToGfxRect(
4573       mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
4574 
4575   StyleGeometryBox clip = mBackgroundStyle->mImage.mLayers[0].mClip;
4576   if (clip == StyleGeometryBox::Text) {
4577     if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
4578       return;
4579     }
4580 
4581     ctx->SetColor(mColor);
4582     ctx->Rectangle(bounds, true);
4583     ctx->Fill();
4584     ctx->PopGroupAndBlend();
4585     return;
4586   }
4587 
4588   ctx->SetColor(mColor);
4589   ctx->NewPath();
4590   ctx->Rectangle(bounds, true);
4591   ctx->Fill();
4592 #endif
4593 }
4594 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const4595 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion(
4596     nsDisplayListBuilder* aBuilder, bool* aSnap) const {
4597   *aSnap = false;
4598 
4599   if (mColor.a != 1) {
4600     return nsRegion();
4601   }
4602 
4603   if (!mBackgroundStyle) return nsRegion();
4604 
4605   const nsStyleImageLayers::Layer& bottomLayer =
4606       mBackgroundStyle->BottomLayer();
4607   if (bottomLayer.mClip == StyleGeometryBox::Text) {
4608     return nsRegion();
4609   }
4610 
4611   *aSnap = true;
4612   return nsDisplayBackgroundImage::GetInsideClipRegion(
4613       this, bottomLayer.mClip, mBackgroundRect, mBackgroundRect);
4614 }
4615 
IsUniform(nsDisplayListBuilder * aBuilder) const4616 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform(
4617     nsDisplayListBuilder* aBuilder) const {
4618   return Some(mColor.ToABGR());
4619 }
4620 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)4621 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
4622                                        const nsRect& aRect,
4623                                        HitTestState* aState,
4624                                        nsTArray<nsIFrame*>* aOutFrames) {
4625   if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4626     // aRect doesn't intersect our border-radius curve.
4627     return;
4628   }
4629 
4630   aOutFrames->AppendElement(mFrame);
4631 }
4632 
WriteDebugInfo(std::stringstream & aStream)4633 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) {
4634   aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << ","
4635           << mColor.a << ")";
4636 }
4637 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)4638 already_AddRefed<Layer> nsDisplayClearBackground::BuildLayer(
4639     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
4640     const ContainerLayerParameters& aParameters) {
4641   RefPtr<ColorLayer> layer = static_cast<ColorLayer*>(
4642       aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
4643   if (!layer) {
4644     layer = aManager->CreateColorLayer();
4645     if (!layer) return nullptr;
4646   }
4647   layer->SetColor(Color());
4648   layer->SetMixBlendMode(gfx::CompositionOp::OP_SOURCE);
4649 
4650   bool snap;
4651   nsRect bounds = GetBounds(aBuilder, &snap);
4652   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
4653   layer->SetBounds(bounds.ToNearestPixels(appUnitsPerDevPixel));  // XXX Do we
4654                                                                   // need to
4655                                                                   // respect the
4656                                                                   // parent
4657                                                                   // layer's
4658                                                                   // scale here?
4659 
4660   return layer.forget();
4661 }
4662 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4663 bool nsDisplayClearBackground::CreateWebRenderCommands(
4664     mozilla::wr::DisplayListBuilder& aBuilder,
4665     mozilla::wr::IpcResourceUpdateQueue& aResources,
4666     const StackingContextHelper& aSc,
4667     mozilla::layers::WebRenderLayerManager* aManager,
4668     nsDisplayListBuilder* aDisplayListBuilder) {
4669   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
4670       nsRect(ToReferenceFrame(), mFrame->GetSize()),
4671       mFrame->PresContext()->AppUnitsPerDevPixel());
4672 
4673   aBuilder.PushClearRect(aSc.ToRelativeLayoutRect(bounds));
4674 
4675   return true;
4676 }
4677 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const4678 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
4679                                    bool* aSnap) const {
4680   *aSnap = false;
4681   return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
4682 }
4683 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)4684 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
4685   // TODO join outlines together
4686   MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(),
4687              "Should have not created a nsDisplayOutline!");
4688 
4689   nsPoint offset = ToReferenceFrame();
4690   nsCSSRendering::PaintOutline(mFrame->PresContext(), *aCtx, mFrame,
4691                                mVisibleRect, nsRect(offset, mFrame->GetSize()),
4692                                mFrame->StyleContext());
4693 }
4694 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)4695 LayerState nsDisplayOutline::GetLayerState(
4696     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
4697     const ContainerLayerParameters& aParameters) {
4698   if (!ShouldUseAdvancedLayer(aManager, gfxPrefs::LayersAllowOutlineLayers)) {
4699     return LAYER_NONE;
4700   }
4701 
4702   uint8_t outlineStyle = mFrame->StyleContext()->StyleOutline()->mOutlineStyle;
4703   if (outlineStyle == NS_STYLE_BORDER_STYLE_AUTO &&
4704       nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
4705     nsITheme* theme = mFrame->PresContext()->GetTheme();
4706     if (theme && theme->ThemeSupportsWidget(mFrame->PresContext(), mFrame,
4707                                             NS_THEME_FOCUS_OUTLINE)) {
4708       return LAYER_NONE;
4709     }
4710   }
4711 
4712   nsPoint offset = ToReferenceFrame();
4713   Maybe<nsCSSBorderRenderer> br =
4714       nsCSSRendering::CreateBorderRendererForOutline(
4715           mFrame->PresContext(), nullptr, mFrame, mVisibleRect,
4716           nsRect(offset, mFrame->GetSize()), mFrame->StyleContext());
4717 
4718   if (!br) {
4719     return LAYER_NONE;
4720   }
4721 
4722   mBorderRenderer = br;
4723 
4724   return LAYER_ACTIVE;
4725 }
4726 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)4727 already_AddRefed<Layer> nsDisplayOutline::BuildLayer(
4728     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
4729     const ContainerLayerParameters& aContainerParameters) {
4730   return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
4731 }
4732 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4733 bool nsDisplayOutline::CreateWebRenderCommands(
4734     mozilla::wr::DisplayListBuilder& aBuilder,
4735     mozilla::wr::IpcResourceUpdateQueue& aResources,
4736     const StackingContextHelper& aSc,
4737     mozilla::layers::WebRenderLayerManager* aManager,
4738     nsDisplayListBuilder* aDisplayListBuilder) {
4739   ContainerLayerParameters parameter;
4740   if (GetLayerState(aDisplayListBuilder, aManager, parameter) != LAYER_ACTIVE) {
4741     return false;
4742   }
4743 
4744   mBorderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
4745   return true;
4746 }
4747 
IsInvisibleInRect(const nsRect & aRect) const4748 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const {
4749   const nsStyleOutline* outline = mFrame->StyleOutline();
4750   nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
4751   if (borderBox.Contains(aRect) &&
4752       !nsLayoutUtils::HasNonZeroCorner(outline->mOutlineRadius)) {
4753     if (outline->mOutlineOffset >= 0) {
4754       // aRect is entirely inside the border-rect, and the outline isn't
4755       // rendered inside the border-rect, so the outline is not visible.
4756       return true;
4757     }
4758   }
4759 
4760   return false;
4761 }
4762 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)4763 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
4764                                      const nsRect& aRect, HitTestState* aState,
4765                                      nsTArray<nsIFrame*>* aOutFrames) {
4766   if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
4767     // aRect doesn't intersect our border-radius curve.
4768     return;
4769   }
4770 
4771   aOutFrames->AppendElement(mFrame);
4772 }
4773 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4774 bool nsDisplayEventReceiver::CreateWebRenderCommands(
4775     mozilla::wr::DisplayListBuilder& aBuilder,
4776     mozilla::wr::IpcResourceUpdateQueue& aResources,
4777     const StackingContextHelper& aSc,
4778     mozilla::layers::WebRenderLayerManager* aManager,
4779     nsDisplayListBuilder* aDisplayListBuilder) {
4780   // This display item should never be getting created when building a display
4781   // list for WebRender consumption, so this function should never get called.
4782   MOZ_ASSERT(false);
4783   return true;
4784 }
4785 
nsDisplayCompositorHitTestInfo(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,mozilla::gfx::CompositorHitTestInfo aHitTestInfo,uint32_t aIndex,const mozilla::Maybe<nsRect> & aArea)4786 nsDisplayCompositorHitTestInfo::nsDisplayCompositorHitTestInfo(
4787     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4788     mozilla::gfx::CompositorHitTestInfo aHitTestInfo, uint32_t aIndex,
4789     const mozilla::Maybe<nsRect>& aArea)
4790     : nsDisplayEventReceiver(aBuilder, aFrame),
4791       mHitTestInfo(aHitTestInfo),
4792       mIndex(aIndex),
4793       mAppUnitsPerDevPixel(mFrame->PresContext()->AppUnitsPerDevPixel()) {
4794   MOZ_COUNT_CTOR(nsDisplayCompositorHitTestInfo);
4795   // We should never even create this display item if we're not building
4796   // compositor hit-test info or if the computed hit info indicated the
4797   // frame is invisible to hit-testing
4798   MOZ_ASSERT(aBuilder->BuildCompositorHitTestInfo());
4799   MOZ_ASSERT(mHitTestInfo !=
4800              mozilla::gfx::CompositorHitTestInfo::eInvisibleToHitTest);
4801 
4802   if (aBuilder->GetCurrentScrollbarFlags() != nsDisplayOwnLayerFlags::eNone) {
4803     // In the case of scrollbar frames, we use the scrollbar's target
4804     // scrollframe instead of the scrollframe with which the scrollbar actually
4805     // moves.
4806     MOZ_ASSERT(mHitTestInfo & CompositorHitTestInfo::eScrollbar);
4807     mScrollTarget = Some(aBuilder->GetCurrentScrollbarTarget());
4808   }
4809 
4810   if (aArea.isSome()) {
4811     mArea = *aArea;
4812   } else {
4813     mArea = GetFrameArea(aBuilder, aFrame);
4814   }
4815 }
4816 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)4817 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(
4818     mozilla::wr::DisplayListBuilder& aBuilder,
4819     mozilla::wr::IpcResourceUpdateQueue& aResources,
4820     const StackingContextHelper& aSc,
4821     mozilla::layers::WebRenderLayerManager* aManager,
4822     nsDisplayListBuilder* aDisplayListBuilder) {
4823   if (mArea.IsEmpty()) {
4824     return true;
4825   }
4826 
4827   // XXX: eventually this scrollId computation and the SetHitTestInfo
4828   // call will get moved out into the WR display item iteration code so that
4829   // we don't need to do it as often, and so that we can do it for other
4830   // display item types as well (reducing the need for as many instances of
4831   // this display item).
4832   FrameMetrics::ViewID scrollId =
4833       mScrollTarget.valueOrFrom([&]() -> FrameMetrics::ViewID {
4834         if (const ActiveScrolledRoot* asr = GetActiveScrolledRoot()) {
4835           return asr->GetViewId();
4836         }
4837         return FrameMetrics::NULL_SCROLL_ID;
4838       });
4839 
4840   // Insert a transparent rectangle with the hit-test info
4841   aBuilder.SetHitTestInfo(scrollId, mHitTestInfo);
4842 
4843   const LayoutDeviceRect devRect =
4844       LayoutDeviceRect::FromAppUnits(mArea, mAppUnitsPerDevPixel);
4845 
4846   const wr::LayoutRect rect = aSc.ToRelativeLayoutRect(devRect);
4847 
4848   aBuilder.PushRect(rect, rect, true, wr::ToColorF(gfx::Color()));
4849   aBuilder.ClearHitTestInfo();
4850 
4851   return true;
4852 }
4853 
WriteDebugInfo(std::stringstream & aStream)4854 void nsDisplayCompositorHitTestInfo::WriteDebugInfo(
4855     std::stringstream& aStream) {
4856   aStream << nsPrintfCString(" (hitTestInfo 0x%x)", (int)mHitTestInfo).get();
4857   AppendToString(aStream, mArea, " hitTestArea");
4858 }
4859 
GetPerFrameKey() const4860 uint32_t nsDisplayCompositorHitTestInfo::GetPerFrameKey() const {
4861   return (mIndex << TYPE_BITS) | nsDisplayItem::GetPerFrameKey();
4862 }
4863 
ZIndex() const4864 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const {
4865   return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
4866 }
4867 
SetOverrideZIndex(int32_t aZIndex)4868 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) {
4869   mOverrideZIndex = Some(aZIndex);
4870 }
4871 
AddFrame(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)4872 void nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
4873                                           nsIFrame* aFrame) {
4874   NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) ==
4875                    aBuilder->FindReferenceFrameFor(mFrame),
4876                "Reference frame mismatch");
4877   CompositorHitTestInfo hitInfo = aFrame->GetCompositorHitTestInfo(aBuilder);
4878   if (hitInfo == CompositorHitTestInfo::eInvisibleToHitTest) {
4879     return;
4880   }
4881 
4882   // XXX handle other pointerEvents values for SVG
4883 
4884   // XXX Do something clever here for the common case where the border box
4885   // is obviously entirely inside mHitRegion.
4886   nsRect borderBox = GetFrameArea(aBuilder, aFrame);
4887 
4888   if (aFrame != mFrame && aBuilder->IsRetainingDisplayList()) {
4889     aFrame->AddDisplayItem(this);
4890   }
4891 
4892   bool borderBoxHasRoundedCorners = false;
4893 
4894   // use the NS_FRAME_SIMPLE_EVENT_REGIONS to avoid calling the slightly
4895   // expensive HasNonZeroCorner function if we know from a previous run that
4896   // the frame has zero corners.
4897   bool simpleRegions = aFrame->HasAnyStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
4898   if (!simpleRegions) {
4899     if (nsLayoutUtils::HasNonZeroCorner(aFrame->StyleBorder()->mBorderRadius)) {
4900       borderBoxHasRoundedCorners = true;
4901     } else {
4902       aFrame->AddStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
4903     }
4904   }
4905 
4906   const DisplayItemClip* clip = DisplayItemClipChain::ClipForASR(
4907       aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder),
4908       aBuilder->CurrentActiveScrolledRoot());
4909   if (clip) {
4910     borderBox = clip->ApplyNonRoundedIntersection(borderBox);
4911     if (clip->GetRoundedRectCount() > 0) {
4912       borderBoxHasRoundedCorners = true;
4913     }
4914   }
4915 
4916   if (borderBoxHasRoundedCorners ||
4917       (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
4918     mMaybeHitRegion.Add(aFrame, borderBox);
4919   } else {
4920     mHitRegion.Add(aFrame, borderBox);
4921   }
4922 
4923   if (hitInfo & CompositorHitTestInfo::eDispatchToContent) {
4924     mDispatchToContentHitRegion.Add(aFrame, borderBox);
4925   }
4926 
4927   // Touch action region
4928 
4929   auto touchFlags = hitInfo & CompositorHitTestInfo::eTouchActionMask;
4930   if (touchFlags) {
4931     // something was disabled
4932     if (touchFlags == CompositorHitTestInfo::eTouchActionMask) {
4933       // everything was disabled, so touch-action:none
4934       mNoActionRegion.Add(aFrame, borderBox);
4935     } else {
4936       // The event regions code does not store enough information to actually
4937       // represent all the different states. Prior to the introduction of
4938       // CompositorHitTestInfo here in bug 1389149, the following two cases
4939       // were effectively getting collapsed:
4940       //   (1) touch-action: auto
4941       //   (2) touch-action: manipulation
4942       // In both of these cases, none of {mNoActionRegion, mHorizontalPanRegion,
4943       // mVerticalPanRegion} were modified, and so the fact that case (2) should
4944       // have prevented double-tap-zooming was getting lost.
4945       // With CompositorHitTestInfo we can now represent that case correctly,
4946       // but only if we use CompositorHitTestInfo all the way to the compositor
4947       // (i.e. in the WebRender-enabled case). In the non-WebRender case where
4948       // we still use the event regions, we must collapse these two cases back
4949       // together. Or add another region to the event regions to fix this
4950       // properly.
4951       if (touchFlags ==
4952           CompositorHitTestInfo::eTouchActionDoubleTapZoomDisabled) {
4953         // the touch-action: manipulation case described above. To preserve the
4954         // existing behaviour, don't touch either mHorizontalPanRegion or
4955         // mVerticalPanRegion
4956       } else {
4957         if (!(hitInfo & CompositorHitTestInfo::eTouchActionPanXDisabled)) {
4958           // pan-x is allowed
4959           mHorizontalPanRegion.Add(aFrame, borderBox);
4960         }
4961         if (!(hitInfo & CompositorHitTestInfo::eTouchActionPanYDisabled)) {
4962           // pan-y is allowed
4963           mVerticalPanRegion.Add(aFrame, borderBox);
4964         }
4965       }
4966     }
4967   }
4968 }
4969 
RemoveFrameFromFrameRects(nsDisplayLayerEventRegions::FrameRects & aFrameRects,nsIFrame * aFrame)4970 static void RemoveFrameFromFrameRects(
4971     nsDisplayLayerEventRegions::FrameRects& aFrameRects, nsIFrame* aFrame) {
4972   uint32_t i = 0;
4973   uint32_t length = aFrameRects.mFrames.Length();
4974   while (i < length) {
4975     if (aFrameRects.mFrames[i] == aFrame) {
4976       aFrameRects.mFrames[i] = aFrameRects.mFrames[length - 1];
4977       aFrameRects.mBoxes[i] = aFrameRects.mBoxes[length - 1];
4978       length--;
4979     } else {
4980       i++;
4981     }
4982   }
4983   aFrameRects.mFrames.SetLength(length);
4984   aFrameRects.mBoxes.SetLength(length);
4985 }
4986 
RemoveFrame(nsIFrame * aFrame)4987 void nsDisplayLayerEventRegions::RemoveFrame(nsIFrame* aFrame) {
4988   RemoveFrameFromFrameRects(mHitRegion, aFrame);
4989   RemoveFrameFromFrameRects(mMaybeHitRegion, aFrame);
4990   RemoveFrameFromFrameRects(mDispatchToContentHitRegion, aFrame);
4991   RemoveFrameFromFrameRects(mNoActionRegion, aFrame);
4992   RemoveFrameFromFrameRects(mHorizontalPanRegion, aFrame);
4993   RemoveFrameFromFrameRects(mVerticalPanRegion, aFrame);
4994 
4995   nsDisplayItem::RemoveFrame(aFrame);
4996 }
4997 
AddInactiveScrollPort(nsIFrame * aFrame,const nsRect & aRect)4998 void nsDisplayLayerEventRegions::AddInactiveScrollPort(nsIFrame* aFrame,
4999                                                        const nsRect& aRect) {
5000   mHitRegion.Add(aFrame, aRect);
5001   mDispatchToContentHitRegion.Add(aFrame, aRect);
5002 }
5003 
IsEmpty() const5004 bool nsDisplayLayerEventRegions::IsEmpty() const {
5005   // If the hit region and maybe-hit region are empty, then the rest
5006   // must be empty too.
5007   if (mHitRegion.IsEmpty() && mMaybeHitRegion.IsEmpty()) {
5008     MOZ_ASSERT(mDispatchToContentHitRegion.IsEmpty());
5009     MOZ_ASSERT(mNoActionRegion.IsEmpty());
5010     MOZ_ASSERT(mHorizontalPanRegion.IsEmpty());
5011     MOZ_ASSERT(mVerticalPanRegion.IsEmpty());
5012     return true;
5013   }
5014   return false;
5015 }
5016 
CombinedTouchActionRegion()5017 nsRegion nsDisplayLayerEventRegions::CombinedTouchActionRegion() {
5018   nsRegion result;
5019   result.Or(HorizontalPanRegion(), VerticalPanRegion());
5020   result.OrWith(NoActionRegion());
5021   return result;
5022 }
5023 
ZIndex() const5024 int32_t nsDisplayLayerEventRegions::ZIndex() const {
5025   return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex();
5026 }
5027 
SetOverrideZIndex(int32_t aZIndex)5028 void nsDisplayLayerEventRegions::SetOverrideZIndex(int32_t aZIndex) {
5029   mOverrideZIndex = Some(aZIndex);
5030 }
5031 
WriteDebugInfo(std::stringstream & aStream)5032 void nsDisplayLayerEventRegions::WriteDebugInfo(std::stringstream& aStream) {
5033   if (!mHitRegion.IsEmpty()) {
5034     AppendToString(aStream, HitRegion(), " (hitRegion ", ")");
5035   }
5036   if (!mMaybeHitRegion.IsEmpty()) {
5037     AppendToString(aStream, MaybeHitRegion(), " (maybeHitRegion ", ")");
5038   }
5039   if (!mDispatchToContentHitRegion.IsEmpty()) {
5040     AppendToString(aStream, DispatchToContentHitRegion(),
5041                    " (dispatchToContentRegion ", ")");
5042   }
5043   if (!mNoActionRegion.IsEmpty()) {
5044     AppendToString(aStream, NoActionRegion(), " (noActionRegion ", ")");
5045   }
5046   if (!mHorizontalPanRegion.IsEmpty()) {
5047     AppendToString(aStream, HorizontalPanRegion(), " (horizPanRegion ", ")");
5048   }
5049   if (!mVerticalPanRegion.IsEmpty()) {
5050     AppendToString(aStream, VerticalPanRegion(), " (vertPanRegion ", ")");
5051   }
5052 }
5053 
nsDisplayCaret(nsDisplayListBuilder * aBuilder,nsIFrame * aCaretFrame)5054 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
5055                                nsIFrame* aCaretFrame)
5056     : nsDisplayItem(aBuilder, aCaretFrame),
5057       mCaret(aBuilder->GetCaret()),
5058       mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) {
5059   MOZ_COUNT_CTOR(nsDisplayCaret);
5060 }
5061 
5062 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayCaret()5063 nsDisplayCaret::~nsDisplayCaret() { MOZ_COUNT_DTOR(nsDisplayCaret); }
5064 #endif
5065 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const5066 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder,
5067                                  bool* aSnap) const {
5068   *aSnap = true;
5069   // The caret returns a rect in the coordinates of mFrame.
5070   return mBounds;
5071 }
5072 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)5073 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
5074   // Note: Because we exist, we know that the caret is visible, so we don't
5075   // need to check for the caret's visibility.
5076   mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
5077 }
5078 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5079 bool nsDisplayCaret::CreateWebRenderCommands(
5080     mozilla::wr::DisplayListBuilder& aBuilder,
5081     mozilla::wr::IpcResourceUpdateQueue& aResources,
5082     const StackingContextHelper& aSc,
5083     mozilla::layers::WebRenderLayerManager* aManager,
5084     nsDisplayListBuilder* aDisplayListBuilder) {
5085   using namespace mozilla::layers;
5086   int32_t contentOffset;
5087   nsIFrame* frame = mCaret->GetFrame(&contentOffset);
5088   if (!frame) {
5089     return true;
5090   }
5091   NS_ASSERTION(frame == mFrame, "We're referring different frame");
5092 
5093   int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
5094 
5095   nsRect caretRect;
5096   nsRect hookRect;
5097   mCaret->ComputeCaretRects(frame, contentOffset, &caretRect, &hookRect);
5098 
5099   gfx::Color color = ToDeviceColor(frame->GetCaretColorAt(contentOffset));
5100   LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
5101       caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
5102   LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
5103       hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
5104 
5105   wr::LayoutRect caret = aSc.ToRelativeLayoutRect(devCaretRect);
5106   wr::LayoutRect hook = aSc.ToRelativeLayoutRect(devHookRect);
5107 
5108   // Note, WR will pixel snap anything that is layout aligned.
5109   aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), wr::ToColorF(color));
5110 
5111   if (!devHookRect.IsEmpty()) {
5112     aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), wr::ToColorF(color));
5113   }
5114   return true;
5115 }
5116 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)5117 LayerState nsDisplayCaret::GetLayerState(
5118     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
5119     const ContainerLayerParameters& aParameters) {
5120   if (ShouldUseAdvancedLayer(aManager, gfxPrefs::LayersAllowCaretLayers)) {
5121     return LAYER_ACTIVE;
5122   }
5123 
5124   return LAYER_NONE;
5125 }
5126 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)5127 already_AddRefed<Layer> nsDisplayCaret::BuildLayer(
5128     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
5129     const ContainerLayerParameters& aContainerParameters) {
5130   return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
5131 }
5132 
nsDisplayBorder(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)5133 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder,
5134                                  nsIFrame* aFrame)
5135     : nsDisplayItem(aBuilder, aFrame), mBorderIsEmpty(false) {
5136   MOZ_COUNT_CTOR(nsDisplayBorder);
5137 
5138   mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
5139 }
5140 
IsInvisibleInRect(const nsRect & aRect) const5141 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const {
5142   nsRect paddingRect =
5143       mFrame->GetPaddingRect() - mFrame->GetPosition() + ToReferenceFrame();
5144   const nsStyleBorder* styleBorder;
5145   if (paddingRect.Contains(aRect) &&
5146       !(styleBorder = mFrame->StyleBorder())->IsBorderImageLoaded() &&
5147       !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
5148     // aRect is entirely inside the content rect, and no part
5149     // of the border is rendered inside the content rect, so we are not
5150     // visible
5151     // Skip this if there's a border-image (which draws a background
5152     // too) or if there is a border-radius (which makes the border draw
5153     // further in).
5154     return true;
5155   }
5156 
5157   return false;
5158 }
5159 
AllocateGeometry(nsDisplayListBuilder * aBuilder)5160 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry(
5161     nsDisplayListBuilder* aBuilder) {
5162   return new nsDisplayBorderGeometry(this, aBuilder);
5163 }
5164 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const5165 void nsDisplayBorder::ComputeInvalidationRegion(
5166     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
5167     nsRegion* aInvalidRegion) const {
5168   const nsDisplayBorderGeometry* geometry =
5169       static_cast<const nsDisplayBorderGeometry*>(aGeometry);
5170   bool snap;
5171 
5172   if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
5173     // We can probably get away with only invalidating the difference
5174     // between the border and padding rects, but the XUL ui at least
5175     // is apparently painting a background with this?
5176     aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
5177   }
5178 
5179   if (aBuilder->ShouldSyncDecodeImages() &&
5180       geometry->ShouldInvalidateToSyncDecodeImages()) {
5181     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
5182   }
5183 }
5184 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)5185 LayerState nsDisplayBorder::GetLayerState(
5186     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
5187     const ContainerLayerParameters& aParameters) {
5188   if (!ShouldUseAdvancedLayer(aManager, gfxPrefs::LayersAllowBorderLayers)) {
5189     return LAYER_NONE;
5190   }
5191 
5192   mBorderIsEmpty = false;
5193   nsPoint offset = ToReferenceFrame();
5194   Maybe<nsCSSBorderRenderer> br = nsCSSRendering::CreateBorderRenderer(
5195       mFrame->PresContext(), nullptr, mFrame, nsRect(),
5196       nsRect(offset, mFrame->GetSize()), mFrame->StyleContext(),
5197       &mBorderIsEmpty, mFrame->GetSkipSides());
5198 
5199   mBorderRenderer = Nothing();
5200   mBorderImageRenderer = Nothing();
5201   if (!br) {
5202     if (mBorderIsEmpty) {
5203       return LAYER_ACTIVE;
5204     }
5205     return LAYER_NONE;
5206   }
5207 
5208   if (!br->AllBordersSolid()) {
5209     return LAYER_NONE;
5210   }
5211 
5212   // We don't support this yet as we don't copy the values to
5213   // the layer, and BasicBorderLayer doesn't support it yet.
5214   if (!br->mNoBorderRadius) {
5215     return LAYER_NONE;
5216   }
5217 
5218   // We copy these values correctly to the layer, but BasicBorderLayer
5219   // won't render them
5220   if (!br->AreBorderSideFinalStylesSame(eSideBitsAll) ||
5221       !br->AllBordersSameWidth()) {
5222     return LAYER_NONE;
5223   }
5224 
5225   NS_FOR_CSS_SIDES(i) {
5226     if (br->mBorderStyles[i] == NS_STYLE_BORDER_STYLE_SOLID) {
5227       mColors[i] = ToDeviceColor(br->mBorderColors[i]);
5228       mWidths[i] = br->mBorderWidths[i];
5229       mBorderStyles[i] = br->mBorderStyles[i];
5230     } else {
5231       mWidths[i] = 0;
5232     }
5233   }
5234   NS_FOR_CSS_FULL_CORNERS(corner) {
5235     mCorners[corner] = LayerSize(br->mBorderRadii[corner].width,
5236                                  br->mBorderRadii[corner].height);
5237   }
5238 
5239   mRect = ViewAs<LayerPixel>(br->mOuterRect);
5240   return LAYER_ACTIVE;
5241 }
5242 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)5243 already_AddRefed<Layer> nsDisplayBorder::BuildLayer(
5244     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
5245     const ContainerLayerParameters& aContainerParameters) {
5246   if (mBorderIsEmpty) {
5247     return nullptr;
5248   }
5249 
5250   if (ShouldUseAdvancedLayer(aManager, gfxPrefs::LayersAllowBorderLayers)) {
5251     return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
5252   } else {
5253     RefPtr<BorderLayer> layer = static_cast<BorderLayer*>(
5254         aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
5255     if (!layer) {
5256       layer = aManager->CreateBorderLayer();
5257       if (!layer) return nullptr;
5258     }
5259     layer->SetRect(mRect);
5260     layer->SetCornerRadii(mCorners);
5261     layer->SetColors(mColors);
5262     layer->SetWidths(mWidths);
5263     layer->SetStyles(mBorderStyles);
5264     layer->SetBaseTransform(gfx::Matrix4x4::Translation(
5265         aContainerParameters.mOffset.x, aContainerParameters.mOffset.y, 0));
5266     return layer.forget();
5267   }
5268 }
5269 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5270 bool nsDisplayBorder::CreateWebRenderCommands(
5271     mozilla::wr::DisplayListBuilder& aBuilder,
5272     mozilla::wr::IpcResourceUpdateQueue& aResources,
5273     const StackingContextHelper& aSc,
5274     mozilla::layers::WebRenderLayerManager* aManager,
5275     nsDisplayListBuilder* aDisplayListBuilder) {
5276   nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
5277 
5278   return nsCSSRendering::CreateWebRenderCommandsForBorder(
5279       this, mFrame, rect, aBuilder, aResources, aSc, aManager,
5280       aDisplayListBuilder);
5281 };
5282 
CreateBorderImageWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5283 void nsDisplayBorder::CreateBorderImageWebRenderCommands(
5284     mozilla::wr::DisplayListBuilder& aBuilder,
5285     mozilla::wr::IpcResourceUpdateQueue& aResources,
5286     const StackingContextHelper& aSc,
5287     mozilla::layers::WebRenderLayerManager* aManager,
5288     nsDisplayListBuilder* aDisplayListBuilder) {
5289   MOZ_ASSERT(mBorderImageRenderer);
5290   if (!mBorderImageRenderer->mImageRenderer.IsReady()) {
5291     return;
5292   }
5293 
5294   float widths[4];
5295   float slice[4];
5296   float outset[4];
5297   const int32_t appUnitsPerDevPixel =
5298       mFrame->PresContext()->AppUnitsPerDevPixel();
5299   NS_FOR_CSS_SIDES(i) {
5300     slice[i] =
5301         (float)(mBorderImageRenderer->mSlice.Side(i)) / appUnitsPerDevPixel;
5302     widths[i] =
5303         (float)(mBorderImageRenderer->mWidths.Side(i)) / appUnitsPerDevPixel;
5304     outset[i] = (float)(mBorderImageRenderer->mImageOutset.Side(i)) /
5305                 appUnitsPerDevPixel;
5306   }
5307 
5308   LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(
5309       mBorderImageRenderer->mArea, appUnitsPerDevPixel);
5310   wr::LayoutRect dest = aSc.ToRelativeLayoutRect(destRect);
5311 
5312   wr::LayoutRect clip = dest;
5313   if (!mBorderImageRenderer->mClip.IsEmpty()) {
5314     LayoutDeviceRect clipRect = LayoutDeviceRect::FromAppUnits(
5315         mBorderImageRenderer->mClip, appUnitsPerDevPixel);
5316     clip = aSc.ToRelativeLayoutRect(clipRect);
5317   }
5318 
5319   switch (mBorderImageRenderer->mImageRenderer.GetType()) {
5320     case eStyleImageType_Image: {
5321       uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
5322       if (aDisplayListBuilder->IsPaintingToWindow()) {
5323         flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
5324       }
5325       if (aDisplayListBuilder->ShouldSyncDecodeImages()) {
5326         flags |= imgIContainer::FLAG_SYNC_DECODE;
5327       }
5328 
5329       RefPtr<imgIContainer> img =
5330           mBorderImageRenderer->mImageRenderer.GetImage();
5331       Maybe<SVGImageContext> svgContext;
5332       gfx::IntSize decodeSize =
5333           nsLayoutUtils::ComputeImageContainerDrawingParameters(
5334               img, mFrame, destRect, aSc, flags, svgContext);
5335       RefPtr<layers::ImageContainer> container =
5336           img->GetImageContainerAtSize(aManager, decodeSize, svgContext, flags);
5337       if (!container) {
5338         return;
5339       }
5340 
5341       gfx::IntSize size;
5342       Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(
5343           this, container, aBuilder, aResources, aSc, size, Nothing());
5344       if (key.isNothing()) {
5345         return;
5346       }
5347 
5348       aBuilder.PushBorderImage(
5349           dest, clip, !BackfaceIsHidden(),
5350           wr::ToBorderWidths(widths[0], widths[1], widths[2], widths[3]),
5351           key.value(),
5352           wr::ToNinePatchDescriptor(
5353               (float)(mBorderImageRenderer->mImageSize.width) /
5354                   appUnitsPerDevPixel,
5355               (float)(mBorderImageRenderer->mImageSize.height) /
5356                   appUnitsPerDevPixel,
5357               wr::ToSideOffsets2D_u32(slice[0], slice[1], slice[2], slice[3])),
5358           wr::ToSideOffsets2D_f32(outset[0], outset[1], outset[2], outset[3]),
5359           wr::ToRepeatMode(mBorderImageRenderer->mRepeatModeHorizontal),
5360           wr::ToRepeatMode(mBorderImageRenderer->mRepeatModeVertical));
5361       break;
5362     }
5363     case eStyleImageType_Gradient: {
5364       RefPtr<nsStyleGradient> gradientData =
5365           mBorderImageRenderer->mImageRenderer.GetGradientData();
5366       nsCSSGradientRenderer renderer =
5367           nsCSSGradientRenderer::Create(mFrame->PresContext(), gradientData,
5368                                         mBorderImageRenderer->mImageSize);
5369 
5370       wr::ExtendMode extendMode;
5371       nsTArray<wr::GradientStop> stops;
5372       LayoutDevicePoint lineStart;
5373       LayoutDevicePoint lineEnd;
5374       LayoutDeviceSize gradientRadius;
5375       renderer.BuildWebRenderParameters(1.0, extendMode, stops, lineStart,
5376                                         lineEnd, gradientRadius);
5377 
5378       if (gradientData->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
5379         LayoutDevicePoint startPoint =
5380             LayoutDevicePoint(dest.origin.x, dest.origin.y) + lineStart;
5381         LayoutDevicePoint endPoint =
5382             LayoutDevicePoint(dest.origin.x, dest.origin.y) + lineEnd;
5383 
5384         aBuilder.PushBorderGradient(
5385             dest, clip, !BackfaceIsHidden(),
5386             wr::ToBorderWidths(widths[0], widths[1], widths[2], widths[3]),
5387             wr::ToLayoutPoint(startPoint), wr::ToLayoutPoint(endPoint), stops,
5388             extendMode,
5389             wr::ToSideOffsets2D_f32(outset[0], outset[1], outset[2],
5390                                     outset[3]));
5391       } else {
5392         aBuilder.PushBorderRadialGradient(
5393             dest, clip, !BackfaceIsHidden(),
5394             wr::ToBorderWidths(widths[0], widths[1], widths[2], widths[3]),
5395             wr::ToLayoutPoint(lineStart), wr::ToLayoutSize(gradientRadius),
5396             stops, extendMode,
5397             wr::ToSideOffsets2D_f32(outset[0], outset[1], outset[2],
5398                                     outset[3]));
5399       }
5400       break;
5401     }
5402     default:
5403       MOZ_ASSERT_UNREACHABLE("Unsupport border image type");
5404   }
5405 }
5406 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)5407 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
5408   nsPoint offset = ToReferenceFrame();
5409 
5410   PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
5411                                ? PaintBorderFlags::SYNC_DECODE_IMAGES
5412                                : PaintBorderFlags();
5413 
5414   ImgDrawResult result = nsCSSRendering::PaintBorder(
5415       mFrame->PresContext(), *aCtx, mFrame, mVisibleRect,
5416       nsRect(offset, mFrame->GetSize()), mFrame->StyleContext(), flags,
5417       mFrame->GetSkipSides());
5418 
5419   nsDisplayBorderGeometry::UpdateDrawResult(this, result);
5420 }
5421 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const5422 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder,
5423                                   bool* aSnap) const {
5424   *aSnap = true;
5425   return mBounds;
5426 }
5427 
5428 // Given a region, compute a conservative approximation to it as a list
5429 // of rectangles that aren't vertically adjacent (i.e., vertically
5430 // adjacent or overlapping rectangles are combined).
5431 // Right now this is only approximate, some vertically overlapping rectangles
5432 // aren't guaranteed to be combined.
ComputeDisjointRectangles(const nsRegion & aRegion,nsTArray<nsRect> * aRects)5433 static void ComputeDisjointRectangles(const nsRegion& aRegion,
5434                                       nsTArray<nsRect>* aRects) {
5435   nscoord accumulationMargin = nsPresContext::CSSPixelsToAppUnits(25);
5436   nsRect accumulated;
5437 
5438   for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
5439     const nsRect& r = iter.Get();
5440     if (accumulated.IsEmpty()) {
5441       accumulated = r;
5442       continue;
5443     }
5444 
5445     if (accumulated.YMost() >= r.y - accumulationMargin) {
5446       accumulated.UnionRect(accumulated, r);
5447     } else {
5448       aRects->AppendElement(accumulated);
5449       accumulated = r;
5450     }
5451   }
5452 
5453   // Finish the in-flight rectangle, if there is one.
5454   if (!accumulated.IsEmpty()) {
5455     aRects->AppendElement(accumulated);
5456   }
5457 }
5458 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)5459 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
5460                                     gfxContext* aCtx) {
5461   nsPoint offset = ToReferenceFrame();
5462   nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
5463   nsPresContext* presContext = mFrame->PresContext();
5464   AutoTArray<nsRect, 10> rects;
5465   ComputeDisjointRectangles(mVisibleRegion, &rects);
5466 
5467   AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS);
5468 
5469   for (uint32_t i = 0; i < rects.Length(); ++i) {
5470     nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect,
5471                                         rects[i], mOpacity);
5472   }
5473 }
5474 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const5475 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
5476                                           bool* aSnap) const {
5477   *aSnap = false;
5478   return mBounds;
5479 }
5480 
GetBoundsInternal()5481 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() {
5482   return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
5483          ToReferenceFrame();
5484 }
5485 
IsInvisibleInRect(const nsRect & aRect) const5486 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const {
5487   nsPoint origin = ToReferenceFrame();
5488   nsRect frameRect(origin, mFrame->GetSize());
5489   if (!frameRect.Contains(aRect)) return false;
5490 
5491   // the visible region is entirely inside the border-rect, and box shadows
5492   // never render within the border-rect (unless there's a border radius).
5493   nscoord twipsRadii[8];
5494   bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
5495   if (!hasBorderRadii) return true;
5496 
5497   return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
5498 }
5499 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)5500 bool nsDisplayBoxShadowOuter::ComputeVisibility(nsDisplayListBuilder* aBuilder,
5501                                                 nsRegion* aVisibleRegion) {
5502   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion)) {
5503     return false;
5504   }
5505 
5506   mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
5507   return true;
5508 }
5509 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)5510 already_AddRefed<Layer> nsDisplayBoxShadowOuter::BuildLayer(
5511     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
5512     const ContainerLayerParameters& aContainerParameters) {
5513   return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
5514 }
5515 
CanBuildWebRenderDisplayItems()5516 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() {
5517   nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
5518   if (!shadows) {
5519     return false;
5520   }
5521 
5522   bool hasBorderRadius;
5523   bool nativeTheme =
5524       nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
5525 
5526   // We don't support native themed things yet like box shadows around
5527   // input buttons.
5528   if (nativeTheme) {
5529     return false;
5530   }
5531 
5532   return true;
5533 }
5534 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5535 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands(
5536     mozilla::wr::DisplayListBuilder& aBuilder,
5537     mozilla::wr::IpcResourceUpdateQueue& aResources,
5538     const StackingContextHelper& aSc,
5539     mozilla::layers::WebRenderLayerManager* aManager,
5540     nsDisplayListBuilder* aDisplayListBuilder) {
5541   if (!CanBuildWebRenderDisplayItems()) {
5542     return false;
5543   }
5544 
5545   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5546   nsPoint offset = ToReferenceFrame();
5547   nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
5548   AutoTArray<nsRect, 10> rects;
5549   bool snap;
5550   nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
5551   ComputeDisjointRectangles(bounds, &rects);
5552 
5553   bool hasBorderRadius;
5554   bool nativeTheme =
5555       nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
5556 
5557   // Don't need the full size of the shadow rect like we do in
5558   // nsCSSRendering since WR takes care of calculations for blur
5559   // and spread radius.
5560   nsRect frameRect =
5561       nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
5562 
5563   RectCornerRadii borderRadii;
5564   if (hasBorderRadius) {
5565     hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect,
5566                                                      mFrame, borderRadii);
5567   }
5568 
5569   // Everything here is in app units, change to device units.
5570   for (uint32_t i = 0; i < rects.Length(); ++i) {
5571     LayoutDeviceRect clipRect =
5572         LayoutDeviceRect::FromAppUnits(rects[i], appUnitsPerDevPixel);
5573     nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
5574     MOZ_ASSERT(shadows);
5575 
5576     for (uint32_t j = shadows->Length(); j > 0; j--) {
5577       nsCSSShadowItem* shadow = shadows->ShadowAt(j - 1);
5578       if (shadow->mInset) {
5579         continue;
5580       }
5581 
5582       float blurRadius = float(shadow->mRadius) / float(appUnitsPerDevPixel);
5583       gfx::Color shadowColor =
5584           nsCSSRendering::GetShadowColor(shadow, mFrame, mOpacity);
5585 
5586       // We don't move the shadow rect here since WR does it for us
5587       // Now translate everything to device pixels.
5588       nsRect shadowRect = frameRect;
5589       LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
5590           nsPoint(shadow->mXOffset, shadow->mYOffset), appUnitsPerDevPixel);
5591 
5592       LayoutDeviceRect deviceBox =
5593           LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
5594       wr::LayoutRect deviceBoxRect = aSc.ToRelativeLayoutRect(deviceBox);
5595       wr::LayoutRect deviceClipRect = aSc.ToRelativeLayoutRect(clipRect);
5596 
5597       LayoutDeviceSize zeroSize;
5598       wr::BorderRadius borderRadius =
5599           wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
5600       if (hasBorderRadius) {
5601         borderRadius = wr::ToBorderRadius(
5602             LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
5603             LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
5604             LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
5605             LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
5606       }
5607 
5608       float spreadRadius = float(shadow->mSpread) / float(appUnitsPerDevPixel);
5609 
5610       aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
5611                              deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
5612                              wr::ToColorF(shadowColor), blurRadius,
5613                              spreadRadius, borderRadius,
5614                              wr::BoxShadowClipMode::Outset);
5615     }
5616   }
5617 
5618   return true;
5619 }
5620 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const5621 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion(
5622     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
5623     nsRegion* aInvalidRegion) const {
5624   const nsDisplayBoxShadowOuterGeometry* geometry =
5625       static_cast<const nsDisplayBoxShadowOuterGeometry*>(aGeometry);
5626   bool snap;
5627   if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
5628       !geometry->mBorderRect.IsEqualInterior(GetBorderRect()) ||
5629       mOpacity != geometry->mOpacity) {
5630     nsRegion oldShadow, newShadow;
5631     nscoord dontCare[8];
5632     bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
5633     if (hasBorderRadius) {
5634       // If we have rounded corners then we need to invalidate the frame area
5635       // too since we paint into it.
5636       oldShadow = geometry->mBounds;
5637       newShadow = GetBounds(aBuilder, &snap);
5638     } else {
5639       oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
5640       newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
5641     }
5642     aInvalidRegion->Or(oldShadow, newShadow);
5643   }
5644 }
5645 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)5646 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
5647                                     gfxContext* aCtx) {
5648   nsPoint offset = ToReferenceFrame();
5649   nsRect borderRect = nsRect(offset, mFrame->GetSize());
5650   nsPresContext* presContext = mFrame->PresContext();
5651   AutoTArray<nsRect, 10> rects;
5652   ComputeDisjointRectangles(mVisibleRegion, &rects);
5653 
5654   AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS);
5655 
5656   DrawTarget* drawTarget = aCtx->GetDrawTarget();
5657   gfxContext* gfx = aCtx;
5658   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
5659 
5660   for (uint32_t i = 0; i < rects.Length(); ++i) {
5661     gfx->Save();
5662     gfx->Clip(NSRectToSnappedRect(rects[i], appUnitsPerDevPixel, *drawTarget));
5663     nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect);
5664     gfx->Restore();
5665   }
5666 }
5667 
CanCreateWebRenderCommands(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsPoint aReferenceOffset)5668 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands(
5669     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5670     nsPoint aReferenceOffset) {
5671   nsCSSShadowArray* shadows = aFrame->StyleEffects()->mBoxShadow;
5672   if (!shadows) {
5673     // Means we don't have to paint anything
5674     return true;
5675   }
5676 
5677   bool hasBorderRadius;
5678   bool nativeTheme =
5679       nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius);
5680 
5681   // We don't support native themed things yet like box shadows around
5682   // input buttons.
5683   if (nativeTheme) {
5684     return false;
5685   }
5686 
5687   return true;
5688 }
5689 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)5690 already_AddRefed<Layer> nsDisplayBoxShadowInner::BuildLayer(
5691     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
5692     const ContainerLayerParameters& aContainerParameters) {
5693   return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
5694 }
5695 
5696 /* static */ void
CreateInsetBoxShadowWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,const StackingContextHelper & aSc,nsRegion & aVisibleRegion,nsIFrame * aFrame,const nsRect aBorderRect)5697 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
5698     mozilla::wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc,
5699     nsRegion& aVisibleRegion, nsIFrame* aFrame, const nsRect aBorderRect) {
5700   if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) {
5701     return;
5702   }
5703 
5704   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
5705 
5706   AutoTArray<nsRect, 10> rects;
5707   ComputeDisjointRectangles(aVisibleRegion, &rects);
5708 
5709   nsCSSShadowArray* shadows = aFrame->StyleEffects()->mBoxShadow;
5710 
5711   for (uint32_t i = 0; i < rects.Length(); ++i) {
5712     LayoutDeviceRect clipRect =
5713         LayoutDeviceRect::FromAppUnits(rects[i], appUnitsPerDevPixel);
5714 
5715     for (uint32_t i = shadows->Length(); i > 0; --i) {
5716       nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
5717       if (!shadowItem->mInset) {
5718         continue;
5719       }
5720 
5721       nsRect shadowRect =
5722           nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect);
5723       RectCornerRadii innerRadii;
5724       nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii);
5725 
5726       // Now translate everything to device pixels.
5727       LayoutDeviceRect deviceBoxRect =
5728           LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
5729       wr::LayoutRect deviceClipRect = aSc.ToRelativeLayoutRect(clipRect);
5730       Color shadowColor =
5731           nsCSSRendering::GetShadowColor(shadowItem, aFrame, 1.0);
5732 
5733       LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
5734           nsPoint(shadowItem->mXOffset, shadowItem->mYOffset),
5735           appUnitsPerDevPixel);
5736 
5737       float blurRadius =
5738           float(shadowItem->mRadius) / float(appUnitsPerDevPixel);
5739 
5740       wr::BorderRadius borderRadius = wr::ToBorderRadius(
5741           LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()),
5742           LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()),
5743           LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()),
5744           LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight()));
5745       // NOTE: Any spread radius > 0 will render nothing. WR Bug.
5746       float spreadRadius =
5747           float(shadowItem->mSpread) / float(appUnitsPerDevPixel);
5748 
5749       aBuilder.PushBoxShadow(
5750           wr::ToLayoutRect(deviceBoxRect), deviceClipRect,
5751           !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect),
5752           wr::ToLayoutVector2D(shadowOffset), wr::ToColorF(shadowColor),
5753           blurRadius, spreadRadius, borderRadius, wr::BoxShadowClipMode::Inset);
5754     }
5755   }
5756 }
5757 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)5758 bool nsDisplayBoxShadowInner::CreateWebRenderCommands(
5759     mozilla::wr::DisplayListBuilder& aBuilder,
5760     mozilla::wr::IpcResourceUpdateQueue& aResources,
5761     const StackingContextHelper& aSc,
5762     mozilla::layers::WebRenderLayerManager* aManager,
5763     nsDisplayListBuilder* aDisplayListBuilder) {
5764   if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame,
5765                                   ToReferenceFrame())) {
5766     return false;
5767   }
5768 
5769   bool snap;
5770   nsRegion visible = GetBounds(aDisplayListBuilder, &snap);
5771   nsPoint offset = ToReferenceFrame();
5772   nsRect borderRect = nsRect(offset, mFrame->GetSize());
5773   nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
5774       aBuilder, aSc, visible, mFrame, borderRect);
5775 
5776   return true;
5777 }
5778 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)5779 bool nsDisplayBoxShadowInner::ComputeVisibility(nsDisplayListBuilder* aBuilder,
5780                                                 nsRegion* aVisibleRegion) {
5781   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion)) {
5782     return false;
5783   }
5784 
5785   mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
5786   return true;
5787 }
5788 
nsDisplayWrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList)5789 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
5790                                      nsIFrame* aFrame, nsDisplayList* aList)
5791     : nsDisplayWrapList(aBuilder, aFrame, aList,
5792                         aBuilder->CurrentActiveScrolledRoot()) {}
5793 
nsDisplayWrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,bool aClearClipChain)5794 nsDisplayWrapList::nsDisplayWrapList(
5795     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
5796     const ActiveScrolledRoot* aActiveScrolledRoot, bool aClearClipChain)
5797     : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot),
5798       mFrameActiveScrolledRoot(aBuilder->CurrentActiveScrolledRoot()),
5799       mOverrideZIndex(0),
5800       mHasZIndexOverride(false),
5801       mClearingClipChain(aClearClipChain) {
5802   MOZ_COUNT_CTOR(nsDisplayWrapList);
5803 
5804   mBaseVisibleRect = mVisibleRect;
5805 
5806   mListPtr = &mList;
5807   mListPtr->AppendToTop(aList);
5808   UpdateBounds(aBuilder);
5809 
5810   if (!aFrame || !aFrame->IsTransformed()) {
5811     return;
5812   }
5813 
5814   // If we're a transformed frame, then we need to find out if we're inside
5815   // the nsDisplayTransform or outside of it. Frames inside the transform
5816   // need mReferenceFrame == mFrame, outside needs the next ancestor
5817   // reference frame.
5818   // If we're inside the transform, then the nsDisplayItem constructor
5819   // will have done the right thing.
5820   // If we're outside the transform, then we should have only one child
5821   // (since nsDisplayTransform wraps all actual content), and that child
5822   // will have the correct reference frame set (since nsDisplayTransform
5823   // handles this explictly).
5824   nsDisplayItem* i = mListPtr->GetBottom();
5825   if (i &&
5826       (!i->GetAbove() || i->GetType() == DisplayItemType::TYPE_TRANSFORM) &&
5827       i->Frame() == mFrame) {
5828     mReferenceFrame = i->ReferenceFrame();
5829     mToReferenceFrame = i->ToReferenceFrame();
5830   }
5831 
5832   nsRect visible = aBuilder->GetVisibleRect() +
5833                    aBuilder->GetCurrentFrameOffsetToReferenceFrame();
5834 
5835   SetVisibleRect(visible, true);
5836 }
5837 
nsDisplayWrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayItem * aItem)5838 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
5839                                      nsIFrame* aFrame, nsDisplayItem* aItem)
5840     : nsDisplayItem(aBuilder, aFrame),
5841       mOverrideZIndex(0),
5842       mHasZIndexOverride(false) {
5843   MOZ_COUNT_CTOR(nsDisplayWrapList);
5844 
5845   mBaseVisibleRect = mVisibleRect;
5846 
5847   mListPtr = &mList;
5848   mListPtr->AppendToTop(aItem);
5849   UpdateBounds(aBuilder);
5850 
5851   if (!aFrame || !aFrame->IsTransformed()) {
5852     return;
5853   }
5854 
5855   // See the previous nsDisplayWrapList constructor
5856   if (aItem->Frame() == aFrame) {
5857     mReferenceFrame = aItem->ReferenceFrame();
5858     mToReferenceFrame = aItem->ToReferenceFrame();
5859   }
5860 
5861   nsRect visible = aBuilder->GetVisibleRect() +
5862                    aBuilder->GetCurrentFrameOffsetToReferenceFrame();
5863 
5864   SetVisibleRect(visible, true);
5865 }
5866 
~nsDisplayWrapList()5867 nsDisplayWrapList::~nsDisplayWrapList() { MOZ_COUNT_DTOR(nsDisplayWrapList); }
5868 
MergeDisplayListFromItem(nsDisplayListBuilder * aBuilder,const nsDisplayItem * aItem)5869 void nsDisplayWrapList::MergeDisplayListFromItem(nsDisplayListBuilder* aBuilder,
5870                                                  const nsDisplayItem* aItem) {
5871   const nsDisplayWrapList* wrappedItem = aItem->AsDisplayWrapList();
5872   MOZ_ASSERT(wrappedItem);
5873 
5874   // Create a new nsDisplayWrapList using a copy-constructor. This is done
5875   // to preserve the information about bounds.
5876   nsDisplayWrapList* wrapper =
5877       MakeDisplayItem<nsDisplayWrapList>(aBuilder, *wrappedItem);
5878 
5879   // Set the display list pointer of the new wrapper item to the display list
5880   // of the wrapped item.
5881   wrapper->mListPtr = wrappedItem->mListPtr;
5882 
5883   mListPtr->AppendToBottom(wrapper);
5884 }
5885 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)5886 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder,
5887                                 const nsRect& aRect, HitTestState* aState,
5888                                 nsTArray<nsIFrame*>* aOutFrames) {
5889   mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames);
5890 }
5891 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const5892 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder,
5893                                     bool* aSnap) const {
5894   *aSnap = false;
5895   return mBounds;
5896 }
5897 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)5898 bool nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder,
5899                                           nsRegion* aVisibleRegion) {
5900   // Convert the passed in visible region to our appunits.
5901   nsRegion visibleRegion;
5902   // mVisibleRect has been clipped to GetClippedBounds
5903   visibleRegion.And(*aVisibleRegion, mVisibleRect);
5904   nsRegion originalVisibleRegion = visibleRegion;
5905 
5906   bool retval = mListPtr->ComputeVisibilityForSublist(aBuilder, &visibleRegion,
5907                                                       mVisibleRect);
5908   nsRegion removed;
5909   // removed = originalVisibleRegion - visibleRegion
5910   removed.Sub(originalVisibleRegion, visibleRegion);
5911   // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications
5912   // SubtractFromVisibleRegion does)
5913   aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed);
5914 
5915   return retval;
5916 }
5917 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const5918 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5919                                             bool* aSnap) const {
5920   *aSnap = false;
5921   nsRegion result;
5922   if (mListPtr->IsOpaque()) {
5923     // Everything within GetBounds that's visible is opaque.
5924     result = GetBounds(aBuilder, aSnap);
5925   } else if (aBuilder->HitTestIsForVisibility()) {
5926     // If we care about an accurate opaque region, iterate the display list
5927     // and build up a region of opaque bounds.
5928     nsDisplayItem* item = mList.GetBottom();
5929     while (item) {
5930       result.OrWith(item->GetOpaqueRegion(aBuilder, aSnap));
5931       item = item->GetAbove();
5932     }
5933   }
5934   *aSnap = false;
5935   return result;
5936 }
5937 
IsUniform(nsDisplayListBuilder * aBuilder) const5938 Maybe<nscolor> nsDisplayWrapList::IsUniform(
5939     nsDisplayListBuilder* aBuilder) const {
5940   // We could try to do something but let's conservatively just return Nothing.
5941   return Nothing();
5942 }
5943 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)5944 void nsDisplayWrapList::Paint(nsDisplayListBuilder* aBuilder,
5945                               gfxContext* aCtx) {
5946   NS_ERROR("nsDisplayWrapList should have been flattened away for painting");
5947 }
5948 
5949 /**
5950  * Returns true if all descendant display items can be placed in the same
5951  * PaintedLayer --- GetLayerState returns LAYER_INACTIVE or LAYER_NONE,
5952  * and they all have the expected animated geometry root.
5953  */
RequiredLayerStateForChildren(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters,const nsDisplayList & aList,AnimatedGeometryRoot * aExpectedAnimatedGeometryRootForChildren)5954 static LayerState RequiredLayerStateForChildren(
5955     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
5956     const ContainerLayerParameters& aParameters, const nsDisplayList& aList,
5957     AnimatedGeometryRoot* aExpectedAnimatedGeometryRootForChildren) {
5958   LayerState result = LAYER_INACTIVE;
5959   for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
5960     if (result == LAYER_INACTIVE &&
5961         i->GetAnimatedGeometryRoot() !=
5962             aExpectedAnimatedGeometryRootForChildren) {
5963       result = LAYER_ACTIVE;
5964     }
5965 
5966     LayerState state = i->GetLayerState(aBuilder, aManager, aParameters);
5967     if (state == LAYER_ACTIVE &&
5968         (i->GetType() == DisplayItemType::TYPE_BLEND_MODE ||
5969          i->GetType() == DisplayItemType::TYPE_TABLE_BLEND_MODE)) {
5970       // nsDisplayBlendMode always returns LAYER_ACTIVE to ensure that the
5971       // blending operation happens in the intermediate surface of its parent
5972       // display item (usually an nsDisplayBlendContainer). But this does not
5973       // mean that it needs all its ancestor display items to become active.
5974       // So we ignore its layer state and look at its children instead.
5975       state = RequiredLayerStateForChildren(
5976           aBuilder, aManager, aParameters,
5977           *i->GetSameCoordinateSystemChildren(), i->GetAnimatedGeometryRoot());
5978     }
5979     if ((state == LAYER_ACTIVE || state == LAYER_ACTIVE_FORCE) &&
5980         state > result) {
5981       result = state;
5982     }
5983     if (state == LAYER_ACTIVE_EMPTY && state > result) {
5984       result = LAYER_ACTIVE_FORCE;
5985     }
5986     if (state == LAYER_NONE) {
5987       nsDisplayList* list = i->GetSameCoordinateSystemChildren();
5988       if (list) {
5989         LayerState childState = RequiredLayerStateForChildren(
5990             aBuilder, aManager, aParameters, *list,
5991             aExpectedAnimatedGeometryRootForChildren);
5992         if (childState > result) {
5993           result = childState;
5994         }
5995       }
5996     }
5997   }
5998   return result;
5999 }
6000 
GetComponentAlphaBounds(nsDisplayListBuilder * aBuilder) const6001 nsRect nsDisplayWrapList::GetComponentAlphaBounds(
6002     nsDisplayListBuilder* aBuilder) const {
6003   nsRect bounds;
6004   for (nsDisplayItem* i = mListPtr->GetBottom(); i; i = i->GetAbove()) {
6005     bounds.UnionRect(bounds, i->GetComponentAlphaBounds(aBuilder));
6006   }
6007   return bounds;
6008 }
6009 
SetReferenceFrame(const nsIFrame * aFrame)6010 void nsDisplayWrapList::SetReferenceFrame(const nsIFrame* aFrame) {
6011   mReferenceFrame = aFrame;
6012   mToReferenceFrame = mFrame->GetOffsetToCrossDoc(mReferenceFrame);
6013 }
6014 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)6015 bool nsDisplayWrapList::CreateWebRenderCommands(
6016     mozilla::wr::DisplayListBuilder& aBuilder,
6017     mozilla::wr::IpcResourceUpdateQueue& aResources,
6018     const StackingContextHelper& aSc,
6019     mozilla::layers::WebRenderLayerManager* aManager,
6020     nsDisplayListBuilder* aDisplayListBuilder) {
6021   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6022       GetChildren(), aDisplayListBuilder, aSc, aBuilder, aResources);
6023   return true;
6024 }
6025 
WrapDisplayList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,nsDisplayWrapper * aWrapper)6026 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder,
6027                                 nsIFrame* aFrame, nsDisplayList* aList,
6028                                 nsDisplayWrapper* aWrapper) {
6029   if (!aList->GetTop()) return NS_OK;
6030   nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
6031   if (!item) return NS_ERROR_OUT_OF_MEMORY;
6032   // aList was emptied
6033   aList->AppendToTop(item);
6034   return NS_OK;
6035 }
6036 
WrapEachDisplayItem(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,nsDisplayWrapper * aWrapper)6037 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
6038                                     nsDisplayList* aList,
6039                                     nsDisplayWrapper* aWrapper) {
6040   nsDisplayList newList;
6041   nsDisplayItem* item;
6042   while ((item = aList->RemoveBottom())) {
6043     item = aWrapper->WrapItem(aBuilder, item);
6044     if (!item) return NS_ERROR_OUT_OF_MEMORY;
6045     newList.AppendToTop(item);
6046   }
6047   // aList was emptied
6048   aList->AppendToTop(&newList);
6049   return NS_OK;
6050 }
6051 
WrapLists(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsDisplayListSet & aIn,const nsDisplayListSet & aOut)6052 nsresult nsDisplayWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
6053                                      nsIFrame* aFrame,
6054                                      const nsDisplayListSet& aIn,
6055                                      const nsDisplayListSet& aOut) {
6056   nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
6057   NS_ENSURE_SUCCESS(rv, rv);
6058 
6059   if (&aOut == &aIn) return NS_OK;
6060   aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
6061   aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
6062   aOut.Floats()->AppendToTop(aIn.Floats());
6063   aOut.Content()->AppendToTop(aIn.Content());
6064   aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
6065   aOut.Outlines()->AppendToTop(aIn.Outlines());
6066   return NS_OK;
6067 }
6068 
WrapListsInPlace(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsDisplayListSet & aLists)6069 nsresult nsDisplayWrapper::WrapListsInPlace(nsDisplayListBuilder* aBuilder,
6070                                             nsIFrame* aFrame,
6071                                             const nsDisplayListSet& aLists) {
6072   nsresult rv;
6073   if (WrapBorderBackground()) {
6074     // Our border-backgrounds are in-flow
6075     rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
6076     NS_ENSURE_SUCCESS(rv, rv);
6077   }
6078   // Our block border-backgrounds are in-flow
6079   rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
6080   NS_ENSURE_SUCCESS(rv, rv);
6081   // The floats are not in flow
6082   rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
6083   NS_ENSURE_SUCCESS(rv, rv);
6084   // Our child content is in flow
6085   rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
6086   NS_ENSURE_SUCCESS(rv, rv);
6087   // The positioned descendants may not be in-flow
6088   rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
6089   NS_ENSURE_SUCCESS(rv, rv);
6090   // The outlines may not be in-flow
6091   return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
6092 }
6093 
nsDisplayOpacity(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,bool aForEventsAndPluginsOnly)6094 nsDisplayOpacity::nsDisplayOpacity(
6095     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
6096     const ActiveScrolledRoot* aActiveScrolledRoot,
6097     bool aForEventsAndPluginsOnly)
6098     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
6099       mOpacity(aFrame->StyleEffects()->mOpacity),
6100       mForEventsAndPluginsOnly(aForEventsAndPluginsOnly) {
6101   MOZ_COUNT_CTOR(nsDisplayOpacity);
6102   mState.mOpacity = mOpacity;
6103 }
6104 
6105 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayOpacity()6106 nsDisplayOpacity::~nsDisplayOpacity() { MOZ_COUNT_DTOR(nsDisplayOpacity); }
6107 #endif
6108 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const6109 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
6110                                            bool* aSnap) const {
6111   *aSnap = false;
6112   // The only time where mOpacity == 1.0 should be when we have will-change.
6113   // We could report this as opaque then but when the will-change value starts
6114   // animating the element would become non opaque and could cause repaints.
6115   return nsRegion();
6116 }
6117 
6118 // nsDisplayOpacity uses layers for rendering
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)6119 already_AddRefed<Layer> nsDisplayOpacity::BuildLayer(
6120     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
6121     const ContainerLayerParameters& aContainerParameters) {
6122   ContainerLayerParameters params = aContainerParameters;
6123   params.mForEventsAndPluginsOnly = mForEventsAndPluginsOnly;
6124   RefPtr<Layer> container = aManager->GetLayerBuilder()->BuildContainerLayerFor(
6125       aBuilder, aManager, mFrame, this, &mList, params, nullptr,
6126       FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR);
6127   if (!container) return nullptr;
6128 
6129   container->SetOpacity(mOpacity);
6130   nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
6131       container, aBuilder, this, mFrame, eCSSProperty_opacity);
6132   return container.forget();
6133 }
6134 
6135 /**
6136  * This doesn't take into account layer scaling --- the layer may be
6137  * rendered at a higher (or lower) resolution, affecting the retained layer
6138  * size --- but this should be good enough.
6139  */
IsItemTooSmallForActiveLayer(nsIFrame * aFrame)6140 static bool IsItemTooSmallForActiveLayer(nsIFrame* aFrame) {
6141   nsIntRect visibleDevPixels =
6142       aFrame->GetVisualOverflowRectRelativeToSelf().ToOutsidePixels(
6143           aFrame->PresContext()->AppUnitsPerDevPixel());
6144   return visibleDevPixels.Size() <
6145          nsIntSize(gfxPrefs::LayoutMinActiveLayerSize(),
6146                    gfxPrefs::LayoutMinActiveLayerSize());
6147 }
6148 
NeedsActiveLayer(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)6149 /* static */ bool nsDisplayOpacity::NeedsActiveLayer(
6150     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
6151   if (EffectCompositor::HasAnimationsForCompositor(aFrame,
6152                                                    eCSSProperty_opacity) ||
6153       (ActiveLayerTracker::IsStyleAnimated(aBuilder, aFrame,
6154                                            eCSSProperty_opacity) &&
6155        !IsItemTooSmallForActiveLayer(aFrame))) {
6156     return true;
6157   }
6158   return false;
6159 }
6160 
ApplyOpacity(nsDisplayListBuilder * aBuilder,float aOpacity,const DisplayItemClipChain * aClip)6161 void nsDisplayOpacity::ApplyOpacity(nsDisplayListBuilder* aBuilder,
6162                                     float aOpacity,
6163                                     const DisplayItemClipChain* aClip) {
6164   NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
6165   mOpacity = mOpacity * aOpacity;
6166   IntersectClip(aBuilder, aClip, false);
6167 }
6168 
CanApplyOpacity() const6169 bool nsDisplayOpacity::CanApplyOpacity() const {
6170   return !EffectCompositor::HasAnimationsForCompositor(mFrame,
6171                                                        eCSSProperty_opacity);
6172 }
6173 
6174 /**
6175  * Recursively iterates through |aList| and collects at most |aMaxChildCount|
6176  * display item pointers to items that return true for CanApplyOpacity().
6177  * The item pointers are added to |aArray|.
6178  *
6179  * LayerEventRegions and WrapList items are ignored.
6180  *
6181  * We need to do this recursively, because the child display items might contain
6182  * nested nsDisplayWrapLists.
6183  *
6184  * Returns false if there are more than |aMaxChildCount| items, or if an item
6185  * that returns false for CanApplyOpacity() is encountered.
6186  * Otherwise returns true.
6187  */
CollectItemsWithOpacity(nsDisplayList * aList,nsTArray<nsDisplayItem * > & aArray,const size_t aMaxChildCount)6188 static bool CollectItemsWithOpacity(nsDisplayList* aList,
6189                                     nsTArray<nsDisplayItem*>& aArray,
6190                                     const size_t aMaxChildCount) {
6191   for (nsDisplayItem* i = aList->GetBottom(); i; i = i->GetAbove()) {
6192     DisplayItemType type = i->GetType();
6193     nsDisplayList* children = i->GetChildren();
6194 
6195     // Descend only into wraplists.
6196     if (type == DisplayItemType::TYPE_WRAP_LIST && children) {
6197       // The current display item has children, process them first.
6198       if (!CollectItemsWithOpacity(children, aArray, aMaxChildCount)) {
6199         return false;
6200       }
6201     }
6202 
6203     if (type == DisplayItemType::TYPE_LAYER_EVENT_REGIONS ||
6204         type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO ||
6205         type == DisplayItemType::TYPE_WRAP_LIST) {
6206       continue;
6207     }
6208 
6209     if (!i->CanApplyOpacity() || aArray.Length() == aMaxChildCount) {
6210       return false;
6211     }
6212 
6213     aArray.AppendElement(i);
6214   }
6215 
6216   return true;
6217 }
6218 
ShouldFlattenAway(nsDisplayListBuilder * aBuilder)6219 bool nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
6220   if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation()) {
6221     // If we've been split, then we might need to merge, so
6222     // don't flatten us away.
6223     return false;
6224   }
6225 
6226   if (NeedsActiveLayer(aBuilder, mFrame) || mOpacity == 0.0) {
6227     // If our opacity is zero then we'll discard all descendant display items
6228     // except for layer event regions, so there's no point in doing this
6229     // optimization (and if we do do it, then invalidations of those descendants
6230     // might trigger repainting).
6231     return false;
6232   }
6233 
6234   if (mList.IsEmpty()) {
6235     return false;
6236   }
6237 
6238   // Only try folding our opacity down if we have at most kMaxChildCount
6239   // children that don't overlap and can all apply the opacity to themselves.
6240   static const size_t kMaxChildCount = 3;
6241 
6242   // Iterate through the child display list and copy at most kMaxChildCount
6243   // child display item pointers to a temporary list.
6244   AutoTArray<nsDisplayItem*, kMaxChildCount> items;
6245   if (!CollectItemsWithOpacity(&mList, items, kMaxChildCount)) {
6246     return false;
6247   }
6248 
6249   struct {
6250     nsDisplayItem* item;
6251     nsRect bounds;
6252   } children[kMaxChildCount];
6253 
6254   bool snap;
6255   size_t childCount = 0;
6256   for (nsDisplayItem* item : items) {
6257     children[childCount].item = item;
6258     children[childCount].bounds = item->GetBounds(aBuilder, &snap);
6259     childCount++;
6260   }
6261 
6262   for (size_t i = 0; i < childCount; i++) {
6263     for (size_t j = i + 1; j < childCount; j++) {
6264       if (children[i].bounds.Intersects(children[j].bounds)) {
6265         return false;
6266       }
6267     }
6268   }
6269 
6270   for (uint32_t i = 0; i < childCount; i++) {
6271     children[i].item->ApplyOpacity(aBuilder, mOpacity, mClipChain);
6272   }
6273 
6274   return true;
6275 }
6276 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)6277 nsDisplayItem::LayerState nsDisplayOpacity::GetLayerState(
6278     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
6279     const ContainerLayerParameters& aParameters) {
6280   // If we only created this item so that we'd get correct nsDisplayEventRegions
6281   // for child frames, then force us to inactive to avoid unnecessary
6282   // layerization changes for content that won't ever be painted.
6283   if (mForEventsAndPluginsOnly) {
6284     MOZ_ASSERT(mOpacity == 0);
6285     return LAYER_INACTIVE;
6286   }
6287 
6288   if (NeedsActiveLayer(aBuilder, mFrame)) {
6289     // Returns LAYER_ACTIVE_FORCE to avoid flatterning the layer for async
6290     // animations.
6291     return LAYER_ACTIVE_FORCE;
6292   }
6293 
6294   return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList,
6295                                        GetAnimatedGeometryRoot());
6296 }
6297 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)6298 bool nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder,
6299                                          nsRegion* aVisibleRegion) {
6300   // Our children are translucent so we should not allow them to subtract
6301   // area from aVisibleRegion. We do need to find out what is visible under
6302   // our children in the temporary compositing buffer, because if our children
6303   // paint our entire bounds opaquely then we don't need an alpha channel in
6304   // the temporary compositing buffer.
6305   nsRect bounds = GetClippedBounds(aBuilder);
6306   nsRegion visibleUnderChildren;
6307   visibleUnderChildren.And(*aVisibleRegion, bounds);
6308   return nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren);
6309 }
6310 
WriteDebugInfo(std::stringstream & aStream)6311 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) {
6312   aStream << " (opacity " << mOpacity << ")";
6313 }
6314 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)6315 bool nsDisplayOpacity::CreateWebRenderCommands(
6316     mozilla::wr::DisplayListBuilder& aBuilder,
6317     mozilla::wr::IpcResourceUpdateQueue& aResources,
6318     const StackingContextHelper& aSc,
6319     mozilla::layers::WebRenderLayerManager* aManager,
6320     nsDisplayListBuilder* aDisplayListBuilder) {
6321   float* opacityForSC = &mOpacity;
6322 
6323   RefPtr<WebRenderAnimationData> animationData =
6324       aManager->CommandBuilder()
6325           .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this);
6326   AnimationInfo& animationInfo = animationData->GetAnimationInfo();
6327   AddAnimationsForProperty(Frame(), aDisplayListBuilder, this,
6328                            eCSSProperty_opacity, animationInfo, false, true);
6329   animationInfo.StartPendingAnimations(aManager->GetAnimationReadyTime());
6330 
6331   // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
6332   // are no active animations.
6333   uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
6334   wr::WrAnimationProperty prop;
6335 
6336   if (!animationInfo.GetAnimations().IsEmpty()) {
6337     opacityForSC = nullptr;
6338     OptionalOpacity opacityForCompositor = mOpacity;
6339     prop.id = animationsId;
6340     prop.effect_type = wr::WrAnimationType::Opacity;
6341 
6342     OpAddCompositorAnimations anim(
6343         CompositorAnimations(animationInfo.GetAnimations(), animationsId),
6344         void_t(), opacityForCompositor);
6345     aManager->WrBridge()->AddWebRenderParentCommand(anim);
6346     aManager->AddActiveCompositorAnimationId(animationsId);
6347   } else if (animationsId) {
6348     aManager->AddCompositorAnimationsIdForDiscard(animationsId);
6349     animationsId = 0;
6350   }
6351 
6352   nsTArray<mozilla::wr::WrFilterOp> filters;
6353   StackingContextHelper sc(aSc, aBuilder, filters, LayoutDeviceRect(), nullptr,
6354                            animationsId ? &prop : nullptr, opacityForSC);
6355 
6356   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
6357       &mList, aDisplayListBuilder, sc, aBuilder, aResources);
6358   return true;
6359 }
6360 
nsDisplayBlendMode(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,uint8_t aBlendMode,const ActiveScrolledRoot * aActiveScrolledRoot,uint32_t aIndex)6361 nsDisplayBlendMode::nsDisplayBlendMode(
6362     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
6363     uint8_t aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot,
6364     uint32_t aIndex)
6365     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
6366       mBlendMode(aBlendMode),
6367       mIndex(aIndex) {
6368   MOZ_COUNT_CTOR(nsDisplayBlendMode);
6369 }
6370 
6371 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayBlendMode()6372 nsDisplayBlendMode::~nsDisplayBlendMode() {
6373   MOZ_COUNT_DTOR(nsDisplayBlendMode);
6374 }
6375 #endif
6376 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const6377 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
6378                                              bool* aSnap) const {
6379   *aSnap = false;
6380   // We are never considered opaque
6381   return nsRegion();
6382 }
6383 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)6384 LayerState nsDisplayBlendMode::GetLayerState(
6385     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
6386     const ContainerLayerParameters& aParameters) {
6387   return LAYER_ACTIVE;
6388 }
6389 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)6390 bool nsDisplayBlendMode::CreateWebRenderCommands(
6391     mozilla::wr::DisplayListBuilder& aBuilder,
6392     mozilla::wr::IpcResourceUpdateQueue& aResources,
6393     const StackingContextHelper& aSc,
6394     mozilla::layers::WebRenderLayerManager* aManager,
6395     nsDisplayListBuilder* aDisplayListBuilder) {
6396   nsTArray<mozilla::wr::WrFilterOp> filters;
6397   StackingContextHelper sc(aSc, aBuilder, filters, LayoutDeviceRect(), nullptr,
6398                            nullptr, nullptr, nullptr, nullptr,
6399                            nsCSSRendering::GetGFXBlendMode(mBlendMode));
6400 
6401   return nsDisplayWrapList::CreateWebRenderCommands(
6402       aBuilder, aResources, sc, aManager, aDisplayListBuilder);
6403 }
6404 
6405 // nsDisplayBlendMode uses layers for rendering
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)6406 already_AddRefed<Layer> nsDisplayBlendMode::BuildLayer(
6407     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
6408     const ContainerLayerParameters& aContainerParameters) {
6409   ContainerLayerParameters newContainerParameters = aContainerParameters;
6410   newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
6411 
6412   RefPtr<Layer> container = aManager->GetLayerBuilder()->BuildContainerLayerFor(
6413       aBuilder, aManager, mFrame, this, &mList, newContainerParameters,
6414       nullptr);
6415   if (!container) {
6416     return nullptr;
6417   }
6418 
6419   container->SetMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
6420 
6421   return container.forget();
6422 }
6423 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)6424 bool nsDisplayBlendMode::ComputeVisibility(nsDisplayListBuilder* aBuilder,
6425                                            nsRegion* aVisibleRegion) {
6426   // Our children are need their backdrop so we should not allow them to
6427   // subtract area from aVisibleRegion. We do need to find out what is visible
6428   // under our children in the temporary compositing buffer, because if our
6429   // children paint our entire bounds opaquely then we don't need an alpha
6430   // channel in the temporary compositing buffer.
6431   nsRect bounds = GetClippedBounds(aBuilder);
6432   nsRegion visibleUnderChildren;
6433   visibleUnderChildren.And(*aVisibleRegion, bounds);
6434   return nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren);
6435 }
6436 
CanMerge(const nsDisplayItem * aItem) const6437 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const {
6438   // Items for the same content element should be merged into a single
6439   // compositing group.
6440   if (!HasSameTypeAndClip(aItem) || !HasSameContent(aItem)) {
6441     return false;
6442   }
6443 
6444   const nsDisplayBlendMode* item =
6445       static_cast<const nsDisplayBlendMode*>(aItem);
6446 
6447   if (item->mIndex != 0 || mIndex != 0) {
6448     // Don't merge background-blend-mode items
6449     return false;
6450   }
6451 
6452   return true;
6453 }
6454 
6455 /* static */ nsDisplayBlendContainer*
CreateForMixBlendMode(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot)6456 nsDisplayBlendContainer::CreateForMixBlendMode(
6457     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
6458     const ActiveScrolledRoot* aActiveScrolledRoot) {
6459   return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
6460                                                   aActiveScrolledRoot, false);
6461 }
6462 
6463 /* static */ nsDisplayBlendContainer*
CreateForBackgroundBlendMode(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot)6464 nsDisplayBlendContainer::CreateForBackgroundBlendMode(
6465     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
6466     const ActiveScrolledRoot* aActiveScrolledRoot) {
6467   return MakeDisplayItem<nsDisplayBlendContainer>(aBuilder, aFrame, aList,
6468                                                   aActiveScrolledRoot, true);
6469 }
6470 
nsDisplayBlendContainer(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,bool aIsForBackground)6471 nsDisplayBlendContainer::nsDisplayBlendContainer(
6472     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
6473     const ActiveScrolledRoot* aActiveScrolledRoot, bool aIsForBackground)
6474     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true),
6475       mIsForBackground(aIsForBackground) {
6476   MOZ_COUNT_CTOR(nsDisplayBlendContainer);
6477 }
6478 
6479 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayBlendContainer()6480 nsDisplayBlendContainer::~nsDisplayBlendContainer() {
6481   MOZ_COUNT_DTOR(nsDisplayBlendContainer);
6482 }
6483 #endif
6484 
6485 // nsDisplayBlendContainer uses layers for rendering
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)6486 already_AddRefed<Layer> nsDisplayBlendContainer::BuildLayer(
6487     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
6488     const ContainerLayerParameters& aContainerParameters) {
6489   // turn off anti-aliasing in the parent stacking context because it changes
6490   // how the group is initialized.
6491   ContainerLayerParameters newContainerParameters = aContainerParameters;
6492   newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
6493 
6494   RefPtr<Layer> container = aManager->GetLayerBuilder()->BuildContainerLayerFor(
6495       aBuilder, aManager, mFrame, this, &mList, newContainerParameters,
6496       nullptr);
6497   if (!container) {
6498     return nullptr;
6499   }
6500 
6501   container->SetForceIsolatedGroup(true);
6502   return container.forget();
6503 }
6504 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)6505 LayerState nsDisplayBlendContainer::GetLayerState(
6506     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
6507     const ContainerLayerParameters& aParameters) {
6508   return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList,
6509                                        GetAnimatedGeometryRoot());
6510 }
6511 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)6512 bool nsDisplayBlendContainer::CreateWebRenderCommands(
6513     mozilla::wr::DisplayListBuilder& aBuilder,
6514     mozilla::wr::IpcResourceUpdateQueue& aResources,
6515     const StackingContextHelper& aSc,
6516     mozilla::layers::WebRenderLayerManager* aManager,
6517     nsDisplayListBuilder* aDisplayListBuilder) {
6518   StackingContextHelper sc(aSc, aBuilder);
6519 
6520   return nsDisplayWrapList::CreateWebRenderCommands(
6521       aBuilder, aResources, sc, aManager, aDisplayListBuilder);
6522 }
6523 
6524 /* static */ nsDisplayTableBlendContainer*
CreateForBackgroundBlendMode(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,nsIFrame * aAncestorFrame)6525 nsDisplayTableBlendContainer::CreateForBackgroundBlendMode(
6526     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
6527     const ActiveScrolledRoot* aActiveScrolledRoot, nsIFrame* aAncestorFrame) {
6528   return MakeDisplayItem<nsDisplayTableBlendContainer>(
6529       aBuilder, aFrame, aList, aActiveScrolledRoot, true, aAncestorFrame);
6530 }
6531 
nsDisplayOwnLayer(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot,nsDisplayOwnLayerFlags aFlags,ViewID aScrollTarget,const ScrollThumbData & aThumbData,bool aForceActive,bool aClearClipChain)6532 nsDisplayOwnLayer::nsDisplayOwnLayer(
6533     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
6534     const ActiveScrolledRoot* aActiveScrolledRoot,
6535     nsDisplayOwnLayerFlags aFlags, ViewID aScrollTarget,
6536     const ScrollThumbData& aThumbData, bool aForceActive, bool aClearClipChain)
6537     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
6538                         aClearClipChain),
6539       mFlags(aFlags),
6540       mScrollTarget(aScrollTarget),
6541       mThumbData(aThumbData),
6542       mForceActive(aForceActive),
6543       mWrAnimationId(0) {
6544   MOZ_COUNT_CTOR(nsDisplayOwnLayer);
6545 
6546   // For scroll thumb layers, override the AGR to be the thumb's AGR rather
6547   // than the AGR for mFrame (which is the slider frame).
6548   if (IsScrollThumbLayer()) {
6549     if (nsIFrame* thumbFrame = nsBox::GetChildXULBox(mFrame)) {
6550       mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(thumbFrame);
6551     }
6552   }
6553 }
6554 
6555 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayOwnLayer()6556 nsDisplayOwnLayer::~nsDisplayOwnLayer() { MOZ_COUNT_DTOR(nsDisplayOwnLayer); }
6557 #endif
6558 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)6559 LayerState nsDisplayOwnLayer::GetLayerState(
6560     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
6561     const ContainerLayerParameters& aParameters) {
6562   if (mForceActive) {
6563     return mozilla::LAYER_ACTIVE_FORCE;
6564   }
6565 
6566   return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList,
6567                                        mAnimatedGeometryRoot);
6568 }
6569 
IsScrollThumbLayer() const6570 bool nsDisplayOwnLayer::IsScrollThumbLayer() const {
6571   return mThumbData.mDirection.isSome();
6572 }
6573 
ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder * aBuilder) const6574 bool nsDisplayOwnLayer::ShouldBuildLayerEvenIfInvisible(
6575     nsDisplayListBuilder* aBuilder) const {
6576   // Render scroll thumb layers even if they are invisible, because async
6577   // scrolling might bring them into view.
6578   return IsScrollThumbLayer();
6579 }
6580 
6581 // nsDisplayOpacity uses layers for rendering
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)6582 already_AddRefed<Layer> nsDisplayOwnLayer::BuildLayer(
6583     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
6584     const ContainerLayerParameters& aContainerParameters) {
6585   RefPtr<ContainerLayer> layer =
6586       aManager->GetLayerBuilder()->BuildContainerLayerFor(
6587           aBuilder, aManager, mFrame, this, &mList, aContainerParameters,
6588           nullptr, FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR);
6589   if (IsScrollThumbLayer()) {
6590     layer->SetScrollThumbData(mScrollTarget, mThumbData);
6591   }
6592   if (mFlags & nsDisplayOwnLayerFlags::eScrollbarContainer) {
6593     ScrollDirection dir = (mFlags & nsDisplayOwnLayerFlags::eVerticalScrollbar)
6594                               ? ScrollDirection::eVertical
6595                               : ScrollDirection::eHorizontal;
6596     layer->SetScrollbarContainer(mScrollTarget, dir);
6597   }
6598 
6599   if (mFlags & nsDisplayOwnLayerFlags::eGenerateSubdocInvalidations) {
6600     mFrame->PresContext()->SetNotifySubDocInvalidationData(layer);
6601   }
6602   return layer.forget();
6603 }
6604 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)6605 bool nsDisplayOwnLayer::CreateWebRenderCommands(
6606     mozilla::wr::DisplayListBuilder& aBuilder,
6607     mozilla::wr::IpcResourceUpdateQueue& aResources,
6608     const StackingContextHelper& aSc, WebRenderLayerManager* aManager,
6609     nsDisplayListBuilder* aDisplayListBuilder) {
6610   if (!aManager->AsyncPanZoomEnabled() || !IsScrollThumbLayer()) {
6611     return nsDisplayWrapList::CreateWebRenderCommands(
6612         aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
6613   }
6614 
6615   // APZ is enabled and this is a scroll thumb, so we need to create and
6616   // set an animation id. That way APZ can move this scrollthumb around as
6617   // needed.
6618   RefPtr<WebRenderAnimationData> animationData =
6619       aManager->CommandBuilder()
6620           .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this);
6621   AnimationInfo& animationInfo = animationData->GetAnimationInfo();
6622   animationInfo.EnsureAnimationsId();
6623   mWrAnimationId = animationInfo.GetCompositorAnimationsId();
6624 
6625   wr::WrAnimationProperty prop;
6626   prop.id = mWrAnimationId;
6627   prop.effect_type = wr::WrAnimationType::Transform;
6628 
6629   StackingContextHelper sc(aSc, aBuilder, nsTArray<wr::WrFilterOp>(),
6630                            LayoutDeviceRect(), nullptr, &prop);
6631 
6632   nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
6633                                              aDisplayListBuilder);
6634   return true;
6635 }
6636 
UpdateScrollData(mozilla::layers::WebRenderScrollData * aData,mozilla::layers::WebRenderLayerScrollData * aLayerData)6637 bool nsDisplayOwnLayer::UpdateScrollData(
6638     mozilla::layers::WebRenderScrollData* aData,
6639     mozilla::layers::WebRenderLayerScrollData* aLayerData) {
6640   bool ret = false;
6641   if (IsScrollThumbLayer()) {
6642     ret = true;
6643     if (aLayerData) {
6644       aLayerData->SetScrollThumbData(mThumbData);
6645       aLayerData->SetScrollbarAnimationId(mWrAnimationId);
6646       aLayerData->SetScrollbarTargetContainerId(mScrollTarget);
6647     }
6648   }
6649   if (mFlags & nsDisplayOwnLayerFlags::eScrollbarContainer) {
6650     ret = true;
6651     if (aLayerData) {
6652       ScrollDirection dir =
6653           (mFlags & nsDisplayOwnLayerFlags::eVerticalScrollbar)
6654               ? ScrollDirection::eVertical
6655               : ScrollDirection::eHorizontal;
6656       aLayerData->SetScrollbarContainerDirection(dir);
6657       aLayerData->SetScrollbarTargetContainerId(mScrollTarget);
6658     }
6659   }
6660   return ret;
6661 }
6662 
WriteDebugInfo(std::stringstream & aStream)6663 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) {
6664   aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
6665                              (int)mFlags, mScrollTarget)
6666                  .get();
6667 }
6668 
nsDisplaySubDocument(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsSubDocumentFrame * aSubDocFrame,nsDisplayList * aList,nsDisplayOwnLayerFlags aFlags)6669 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
6670                                            nsIFrame* aFrame,
6671                                            nsSubDocumentFrame* aSubDocFrame,
6672                                            nsDisplayList* aList,
6673                                            nsDisplayOwnLayerFlags aFlags)
6674     : nsDisplayOwnLayer(aBuilder, aFrame, aList,
6675                         aBuilder->CurrentActiveScrolledRoot(), aFlags),
6676       mScrollParentId(aBuilder->GetCurrentScrollParentId()),
6677       mShouldFlatten(false),
6678       mSubDocFrame(aSubDocFrame) {
6679   MOZ_COUNT_CTOR(nsDisplaySubDocument);
6680 
6681   // The SubDocument display item is conceptually outside the viewport frame,
6682   // so in cases where the viewport frame is an AGR, the SubDocument's AGR
6683   // should be not the viewport frame itself, but its parent AGR.
6684   if (*mAnimatedGeometryRoot == mFrame && mAnimatedGeometryRoot->mParentAGR) {
6685     mAnimatedGeometryRoot = mAnimatedGeometryRoot->mParentAGR;
6686   }
6687 }
6688 
6689 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplaySubDocument()6690 nsDisplaySubDocument::~nsDisplaySubDocument() {
6691   MOZ_COUNT_DTOR(nsDisplaySubDocument);
6692 }
6693 #endif
6694 
ComputeScrollMetadata(LayerManager * aLayerManager,const ContainerLayerParameters & aContainerParameters)6695 UniquePtr<ScrollMetadata> nsDisplaySubDocument::ComputeScrollMetadata(
6696     LayerManager* aLayerManager,
6697     const ContainerLayerParameters& aContainerParameters) {
6698   if (!(mFlags & nsDisplayOwnLayerFlags::eGenerateScrollableLayer)) {
6699     return UniquePtr<ScrollMetadata>(nullptr);
6700   }
6701 
6702   nsPresContext* presContext = mFrame->PresContext();
6703   nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame();
6704   bool isRootContentDocument = presContext->IsRootContentDocument();
6705   nsIPresShell* presShell = presContext->PresShell();
6706   ContainerLayerParameters params(
6707       aContainerParameters.mXScale * presShell->GetResolution(),
6708       aContainerParameters.mYScale * presShell->GetResolution(), nsIntPoint(),
6709       aContainerParameters);
6710 
6711   nsRect viewport = mFrame->GetRect() - mFrame->GetPosition() +
6712                     mFrame->GetOffsetToCrossDoc(ReferenceFrame());
6713 
6714   return MakeUnique<ScrollMetadata>(nsLayoutUtils::ComputeScrollMetadata(
6715       mFrame, rootScrollFrame, rootScrollFrame->GetContent(), ReferenceFrame(),
6716       aLayerManager, mScrollParentId, viewport, Nothing(),
6717       isRootContentDocument, params));
6718 }
6719 
UseDisplayPortForViewport(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)6720 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder,
6721                                       nsIFrame* aFrame) {
6722   return aBuilder->IsPaintingToWindow() &&
6723          nsLayoutUtils::ViewportHasDisplayPort(aFrame->PresContext());
6724 }
6725 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const6726 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder,
6727                                        bool* aSnap) const {
6728   bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
6729 
6730   if ((mFlags & nsDisplayOwnLayerFlags::eGenerateScrollableLayer) &&
6731       usingDisplayPort) {
6732     *aSnap = false;
6733     return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
6734   }
6735 
6736   return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
6737 }
6738 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)6739 bool nsDisplaySubDocument::ComputeVisibility(nsDisplayListBuilder* aBuilder,
6740                                              nsRegion* aVisibleRegion) {
6741   bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
6742 
6743   if (!(mFlags & nsDisplayOwnLayerFlags::eGenerateScrollableLayer) ||
6744       !usingDisplayPort) {
6745     return nsDisplayWrapList::ComputeVisibility(aBuilder, aVisibleRegion);
6746   }
6747 
6748   nsRect displayport;
6749   nsIFrame* rootScrollFrame = mFrame->PresShell()->GetRootScrollFrame();
6750   MOZ_ASSERT(rootScrollFrame);
6751   Unused << nsLayoutUtils::GetDisplayPort(
6752       rootScrollFrame->GetContent(), &displayport, RelativeTo::ScrollFrame);
6753 
6754   nsRegion childVisibleRegion;
6755   // The visible region for the children may be much bigger than the hole we
6756   // are viewing the children from, so that the compositor process has enough
6757   // content to asynchronously pan while content is being refreshed.
6758   childVisibleRegion =
6759       displayport + mFrame->GetOffsetToCrossDoc(ReferenceFrame());
6760 
6761   nsRect boundedRect = childVisibleRegion.GetBounds().Intersect(
6762       mList.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot));
6763   bool visible = mList.ComputeVisibilityForSublist(
6764       aBuilder, &childVisibleRegion, boundedRect);
6765 
6766   // If APZ is enabled then don't allow this computation to influence
6767   // aVisibleRegion, on the assumption that the layer can be asynchronously
6768   // scrolled so we'll definitely need all the content under it.
6769   if (!nsLayoutUtils::UsesAsyncScrolling(mFrame)) {
6770     bool snap;
6771     nsRect bounds = GetBounds(aBuilder, &snap);
6772     nsRegion removed;
6773     removed.Sub(bounds, childVisibleRegion);
6774 
6775     aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed);
6776   }
6777 
6778   return visible;
6779 }
6780 
ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder * aBuilder) const6781 bool nsDisplaySubDocument::ShouldBuildLayerEvenIfInvisible(
6782     nsDisplayListBuilder* aBuilder) const {
6783   bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
6784 
6785   if ((mFlags & nsDisplayOwnLayerFlags::eGenerateScrollableLayer) &&
6786       usingDisplayPort) {
6787     return true;
6788   }
6789 
6790   return nsDisplayOwnLayer::ShouldBuildLayerEvenIfInvisible(aBuilder);
6791 }
6792 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const6793 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
6794                                                bool* aSnap) const {
6795   bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
6796 
6797   if ((mFlags & nsDisplayOwnLayerFlags::eGenerateScrollableLayer) &&
6798       usingDisplayPort) {
6799     *aSnap = false;
6800     return nsRegion();
6801   }
6802 
6803   return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
6804 }
6805 
nsDisplayResolution(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,nsDisplayOwnLayerFlags aFlags)6806 nsDisplayResolution::nsDisplayResolution(nsDisplayListBuilder* aBuilder,
6807                                          nsIFrame* aFrame, nsDisplayList* aList,
6808                                          nsDisplayOwnLayerFlags aFlags)
6809     : nsDisplaySubDocument(aBuilder, aFrame, nullptr, aList, aFlags) {
6810   MOZ_COUNT_CTOR(nsDisplayResolution);
6811 }
6812 
6813 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayResolution()6814 nsDisplayResolution::~nsDisplayResolution() {
6815   MOZ_COUNT_DTOR(nsDisplayResolution);
6816 }
6817 #endif
6818 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)6819 void nsDisplayResolution::HitTest(nsDisplayListBuilder* aBuilder,
6820                                   const nsRect& aRect, HitTestState* aState,
6821                                   nsTArray<nsIFrame*>* aOutFrames) {
6822   nsIPresShell* presShell = mFrame->PresShell();
6823   nsRect rect = aRect.RemoveResolution(
6824       presShell->ScaleToResolution() ? presShell->GetResolution() : 1.0f);
6825   mList.HitTest(aBuilder, rect, aState, aOutFrames);
6826 }
6827 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)6828 already_AddRefed<Layer> nsDisplayResolution::BuildLayer(
6829     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
6830     const ContainerLayerParameters& aContainerParameters) {
6831   nsIPresShell* presShell = mFrame->PresShell();
6832   ContainerLayerParameters containerParameters(
6833       presShell->GetResolution(), presShell->GetResolution(), nsIntPoint(),
6834       aContainerParameters);
6835 
6836   RefPtr<Layer> layer =
6837       nsDisplaySubDocument::BuildLayer(aBuilder, aManager, containerParameters);
6838   layer->SetPostScale(1.0f / presShell->GetResolution(),
6839                       1.0f / presShell->GetResolution());
6840   layer->AsContainerLayer()->SetScaleToResolution(
6841       presShell->ScaleToResolution(), presShell->GetResolution());
6842   return layer.forget();
6843 }
6844 
nsDisplayFixedPosition(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot)6845 nsDisplayFixedPosition::nsDisplayFixedPosition(
6846     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
6847     const ActiveScrolledRoot* aActiveScrolledRoot)
6848     : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
6849       mIndex(0),
6850       mIsFixedBackground(false) {
6851   MOZ_COUNT_CTOR(nsDisplayFixedPosition);
6852   Init(aBuilder);
6853 }
6854 
nsDisplayFixedPosition(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,uint32_t aIndex)6855 nsDisplayFixedPosition::nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder,
6856                                                nsIFrame* aFrame,
6857                                                nsDisplayList* aList,
6858                                                uint32_t aIndex)
6859     : nsDisplayOwnLayer(aBuilder, aFrame, aList,
6860                         aBuilder->CurrentActiveScrolledRoot()),
6861       mIndex(aIndex),
6862       mIsFixedBackground(true) {
6863   MOZ_COUNT_CTOR(nsDisplayFixedPosition);
6864   Init(aBuilder);
6865 }
6866 
Init(nsDisplayListBuilder * aBuilder)6867 void nsDisplayFixedPosition::Init(nsDisplayListBuilder* aBuilder) {
6868   mAnimatedGeometryRootForScrollMetadata = mAnimatedGeometryRoot;
6869   if (ShouldFixToViewport(aBuilder)) {
6870     mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(this);
6871   }
6872 }
6873 
6874 /* static */ nsDisplayFixedPosition*
CreateForFixedBackground(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayBackgroundImage * aImage,uint32_t aIndex)6875 nsDisplayFixedPosition::CreateForFixedBackground(
6876     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
6877     nsDisplayBackgroundImage* aImage, uint32_t aIndex) {
6878   nsDisplayList temp;
6879   temp.AppendToTop(aImage);
6880 
6881   return MakeDisplayItem<nsDisplayFixedPosition>(aBuilder, aFrame, &temp,
6882                                                  aIndex + 1);
6883 }
6884 
6885 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayFixedPosition()6886 nsDisplayFixedPosition::~nsDisplayFixedPosition() {
6887   MOZ_COUNT_DTOR(nsDisplayFixedPosition);
6888 }
6889 #endif
6890 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)6891 already_AddRefed<Layer> nsDisplayFixedPosition::BuildLayer(
6892     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
6893     const ContainerLayerParameters& aContainerParameters) {
6894   RefPtr<Layer> layer =
6895       nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
6896 
6897   layer->SetIsFixedPosition(true);
6898 
6899   nsPresContext* presContext = mFrame->PresContext();
6900   nsIFrame* fixedFrame =
6901       mIsFixedBackground ? presContext->PresShell()->GetRootFrame() : mFrame;
6902 
6903   const nsIFrame* viewportFrame = fixedFrame->GetParent();
6904   // anchorRect will be in the container's coordinate system (aLayer's parent
6905   // layer). This is the same as the display items' reference frame.
6906   nsRect anchorRect;
6907   if (viewportFrame) {
6908     // Fixed position frames are reflowed into the scroll-port size if one has
6909     // been set.
6910     if (presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
6911       anchorRect.SizeTo(
6912           presContext->PresShell()->GetScrollPositionClampingScrollPortSize());
6913     } else {
6914       anchorRect.SizeTo(viewportFrame->GetSize());
6915     }
6916   } else {
6917     // A display item directly attached to the viewport.
6918     // For background-attachment:fixed items, the anchor point is always the
6919     // top-left of the viewport currently.
6920     viewportFrame = fixedFrame;
6921   }
6922   // The anchorRect top-left is always the viewport top-left.
6923   anchorRect.MoveTo(viewportFrame->GetOffsetToCrossDoc(ReferenceFrame()));
6924 
6925   nsLayoutUtils::SetFixedPositionLayerData(layer, viewportFrame, anchorRect,
6926                                            fixedFrame, presContext,
6927                                            aContainerParameters);
6928 
6929   return layer.forget();
6930 }
6931 
UpdateScrollData(mozilla::layers::WebRenderScrollData * aData,mozilla::layers::WebRenderLayerScrollData * aLayerData)6932 bool nsDisplayFixedPosition::UpdateScrollData(
6933     mozilla::layers::WebRenderScrollData* aData,
6934     mozilla::layers::WebRenderLayerScrollData* aLayerData) {
6935   if (aLayerData) {
6936     FrameMetrics::ViewID id =
6937         nsLayoutUtils::ScrollIdForRootScrollFrame(Frame()->PresContext());
6938     aLayerData->SetFixedPositionScrollContainerId(id);
6939   }
6940   return nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData) | true;
6941 }
6942 
GetTableTypeFromFrame(nsIFrame * aFrame)6943 TableType GetTableTypeFromFrame(nsIFrame* aFrame) {
6944   if (aFrame->IsTableFrame()) {
6945     return TableType::TABLE;
6946   }
6947 
6948   if (aFrame->IsTableColFrame()) {
6949     return TableType::TABLE_COL;
6950   }
6951 
6952   if (aFrame->IsTableColGroupFrame()) {
6953     return TableType::TABLE_COL_GROUP;
6954   }
6955 
6956   if (aFrame->IsTableRowFrame()) {
6957     return TableType::TABLE_ROW;
6958   }
6959 
6960   if (aFrame->IsTableRowGroupFrame()) {
6961     return TableType::TABLE_ROW_GROUP;
6962   }
6963 
6964   if (aFrame->IsTableCellFrame()) {
6965     return TableType::TABLE_CELL;
6966   }
6967 
6968   MOZ_ASSERT_UNREACHABLE("Invalid frame.");
6969   return TableType::TABLE;
6970 }
6971 
nsDisplayTableFixedPosition(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,uint32_t aIndex,nsIFrame * aAncestorFrame)6972 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(
6973     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
6974     uint32_t aIndex, nsIFrame* aAncestorFrame)
6975     : nsDisplayFixedPosition(aBuilder, aFrame, aList, aIndex),
6976       mAncestorFrame(aAncestorFrame),
6977       mTableType(GetTableTypeFromFrame(aAncestorFrame)) {}
6978 
6979 /* static */ nsDisplayTableFixedPosition*
CreateForFixedBackground(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayBackgroundImage * aImage,uint32_t aIndex,nsIFrame * aAncestorFrame)6980 nsDisplayTableFixedPosition::CreateForFixedBackground(
6981     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
6982     nsDisplayBackgroundImage* aImage, uint32_t aIndex,
6983     nsIFrame* aAncestorFrame) {
6984   nsDisplayList temp;
6985   temp.AppendToTop(aImage);
6986 
6987   return MakeDisplayItem<nsDisplayTableFixedPosition>(
6988       aBuilder, aFrame, &temp, aIndex + 1, aAncestorFrame);
6989 }
6990 
nsDisplayStickyPosition(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aActiveScrolledRoot)6991 nsDisplayStickyPosition::nsDisplayStickyPosition(
6992     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
6993     const ActiveScrolledRoot* aActiveScrolledRoot)
6994     : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot) {
6995   MOZ_COUNT_CTOR(nsDisplayStickyPosition);
6996 }
6997 
SetClipChain(const DisplayItemClipChain * aClipChain,bool aStore)6998 void nsDisplayStickyPosition::SetClipChain(
6999     const DisplayItemClipChain* aClipChain, bool aStore) {
7000   mClipChain = aClipChain;
7001   mClip = nullptr;
7002 
7003   MOZ_ASSERT(!mClip,
7004              "There should never be a clip on this item because no clip moves "
7005              "with it.");
7006 
7007   if (aStore) {
7008     mState.mClipChain = aClipChain;
7009     mState.mClip = mClip;
7010   }
7011 }
7012 
7013 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayStickyPosition()7014 nsDisplayStickyPosition::~nsDisplayStickyPosition() {
7015   MOZ_COUNT_DTOR(nsDisplayStickyPosition);
7016 }
7017 #endif
7018 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)7019 already_AddRefed<Layer> nsDisplayStickyPosition::BuildLayer(
7020     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
7021     const ContainerLayerParameters& aContainerParameters) {
7022   RefPtr<Layer> layer =
7023       nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
7024 
7025   StickyScrollContainer* stickyScrollContainer =
7026       StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
7027   if (!stickyScrollContainer) {
7028     return layer.forget();
7029   }
7030 
7031   nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
7032   nsPresContext* presContext = scrollFrame->PresContext();
7033 
7034   // Sticky position frames whose scroll frame is the root scroll frame are
7035   // reflowed into the scroll-port size if one has been set.
7036   nsSize scrollFrameSize = scrollFrame->GetSize();
7037   if (scrollFrame == presContext->PresShell()->GetRootScrollFrame() &&
7038       presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
7039     scrollFrameSize =
7040         presContext->PresShell()->GetScrollPositionClampingScrollPortSize();
7041   }
7042 
7043   nsLayoutUtils::SetFixedPositionLayerData(
7044       layer, scrollFrame,
7045       nsRect(scrollFrame->GetOffsetToCrossDoc(ReferenceFrame()),
7046              scrollFrameSize),
7047       mFrame, presContext, aContainerParameters);
7048 
7049   ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(
7050       stickyScrollContainer->ScrollFrame()->GetScrolledFrame()->GetContent());
7051 
7052   float factor = presContext->AppUnitsPerDevPixel();
7053   nsRectAbsolute outer;
7054   nsRectAbsolute inner;
7055   stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
7056   LayerRectAbsolute stickyOuter(
7057       NSAppUnitsToFloatPixels(outer.X(), factor) * aContainerParameters.mXScale,
7058       NSAppUnitsToFloatPixels(outer.Y(), factor) * aContainerParameters.mYScale,
7059       NSAppUnitsToFloatPixels(outer.XMost(), factor) *
7060           aContainerParameters.mXScale,
7061       NSAppUnitsToFloatPixels(outer.YMost(), factor) *
7062           aContainerParameters.mYScale);
7063   LayerRectAbsolute stickyInner(
7064       NSAppUnitsToFloatPixels(inner.X(), factor) * aContainerParameters.mXScale,
7065       NSAppUnitsToFloatPixels(inner.Y(), factor) * aContainerParameters.mYScale,
7066       NSAppUnitsToFloatPixels(inner.XMost(), factor) *
7067           aContainerParameters.mXScale,
7068       NSAppUnitsToFloatPixels(inner.YMost(), factor) *
7069           aContainerParameters.mYScale);
7070   layer->SetStickyPositionData(scrollId, stickyOuter, stickyInner);
7071 
7072   return layer.forget();
7073 }
7074 
7075 // Returns the smallest distance from "0" to the range [min, max] where
7076 // min <= max.
DistanceToRange(nscoord min,nscoord max)7077 static nscoord DistanceToRange(nscoord min, nscoord max) {
7078   MOZ_ASSERT(min <= max);
7079   if (max < 0) {
7080     return max;
7081   }
7082   if (min > 0) {
7083     return min;
7084   }
7085   MOZ_ASSERT(min <= 0 && max >= 0);
7086   return 0;
7087 }
7088 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)7089 bool nsDisplayStickyPosition::CreateWebRenderCommands(
7090     mozilla::wr::DisplayListBuilder& aBuilder,
7091     mozilla::wr::IpcResourceUpdateQueue& aResources,
7092     const StackingContextHelper& aSc, WebRenderLayerManager* aManager,
7093     nsDisplayListBuilder* aDisplayListBuilder) {
7094   StickyScrollContainer* stickyScrollContainer =
7095       StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
7096   if (stickyScrollContainer) {
7097     // If there's no ASR for the scrollframe that this sticky item is attached
7098     // to, then don't create a WR sticky item for it either. Trying to do so
7099     // will end in sadness because WR will interpret some coordinates as
7100     // relative to the nearest enclosing scrollframe, which will correspond
7101     // to the nearest ancestor ASR on the gecko side. That ASR will not be the
7102     // same as the scrollframe this sticky item is actually supposed to be
7103     // attached to, thus the sadness.
7104     // Not sending WR the sticky item is ok, because the enclosing scrollframe
7105     // will never be asynchronously scrolled. Instead we will always position
7106     // the sticky items correctly on the gecko side and WR will never need to
7107     // adjust their position itself.
7108     if (!stickyScrollContainer->ScrollFrame()
7109              ->IsMaybeAsynchronouslyScrolled()) {
7110       stickyScrollContainer = nullptr;
7111     }
7112   }
7113 
7114   if (stickyScrollContainer) {
7115     float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
7116 
7117     bool snap;
7118     nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
7119 
7120     Maybe<float> topMargin;
7121     Maybe<float> rightMargin;
7122     Maybe<float> bottomMargin;
7123     Maybe<float> leftMargin;
7124     wr::StickyOffsetBounds vBounds = {0.0, 0.0};
7125     wr::StickyOffsetBounds hBounds = {0.0, 0.0};
7126     nsPoint appliedOffset;
7127 
7128     nsRectAbsolute outer;
7129     nsRectAbsolute inner;
7130     stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
7131 
7132     nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
7133     nsPoint offset = scrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
7134 
7135     // Adjust the scrollPort coordinates to be relative to the reference frame,
7136     // so that it is in the same space as everything else.
7137     nsRect scrollPort =
7138         stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
7139     scrollPort += offset;
7140 
7141     // The following computations make more sense upon understanding the
7142     // semantics of "inner" and "outer", which is explained in the comment on
7143     // SetStickyPositionData in Layers.h.
7144 
7145     if (outer.YMost() != inner.YMost()) {
7146       // Question: How far will itemBounds.y be from the top of the scrollport
7147       // when we have scrolled from the current scroll position of "0" to
7148       // reach the range [inner.YMost(), outer.YMost()] where the item gets
7149       // stuck?
7150       // Answer: the current distance is "itemBounds.y - scrollPort.y". That
7151       // needs to be adjusted by the distance to the range. If the distance is
7152       // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
7153       // scrolling upwards (decreasing scroll offset) to reach that range,
7154       // which would increase itemBounds.y and make it farther away from the
7155       // top of the scrollport. So in that case the adjustment is -distance.
7156       // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
7157       // we would be scrolling downwards, itemBounds.y would decrease, and we
7158       // again need to adjust by -distance. If we are already in the range
7159       // then no adjustment is needed and distance is 0 so again using
7160       // -distance works.
7161       nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
7162       topMargin = Some(NSAppUnitsToFloatPixels(
7163           itemBounds.y - scrollPort.y - distance, auPerDevPixel));
7164       // Question: What is the maximum positive ("downward") offset that WR
7165       // will have to apply to this item in order to prevent the item from
7166       // visually moving?
7167       // Answer: Since the item is "sticky" in the range [inner.YMost(),
7168       // outer.YMost()], the maximum offset will be the size of the range, which
7169       // is outer.YMost() - inner.YMost().
7170       vBounds.max =
7171           NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
7172       // Question: how much of an offset has layout already applied to the item?
7173       // Answer: if we are
7174       // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
7175       // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
7176       // then layout has already applied some offset to the position of the
7177       // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
7178       // and |outer.YMost() - inner.YMost()| in case (b).
7179       if (inner.YMost() < 0) {
7180         appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
7181         MOZ_ASSERT(appliedOffset.y > 0);
7182       }
7183     }
7184     if (outer.Y() != inner.Y()) {
7185       // Similar logic as in the previous section, but this time we care about
7186       // the distance from itemBounds.YMost() to scrollPort.YMost().
7187       nscoord distance = DistanceToRange(outer.Y(), inner.Y());
7188       bottomMargin = Some(NSAppUnitsToFloatPixels(
7189           scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
7190       // And here WR will be moving the item upwards rather than downwards so
7191       // again things are inverted from the previous block.
7192       vBounds.min =
7193           NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
7194       // We can't have appliedOffset be both positive and negative, and the top
7195       // adjustment takes priority. So here we only update appliedOffset.y if
7196       // it wasn't set by the top-sticky case above.
7197       if (appliedOffset.y == 0 && inner.Y() > 0) {
7198         appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
7199         MOZ_ASSERT(appliedOffset.y < 0);
7200       }
7201     }
7202     // Same as above, but for the x-axis
7203     if (outer.XMost() != inner.XMost()) {
7204       nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
7205       leftMargin = Some(NSAppUnitsToFloatPixels(
7206           itemBounds.x - scrollPort.x - distance, auPerDevPixel));
7207       hBounds.max =
7208           NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
7209       if (inner.XMost() < 0) {
7210         appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
7211         MOZ_ASSERT(appliedOffset.x > 0);
7212       }
7213     }
7214     if (outer.X() != inner.X()) {
7215       nscoord distance = DistanceToRange(outer.X(), inner.X());
7216       rightMargin = Some(NSAppUnitsToFloatPixels(
7217           scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
7218       hBounds.min =
7219           NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
7220       if (appliedOffset.x == 0 && inner.X() > 0) {
7221         appliedOffset.x = std::max(0, outer.X()) - inner.X();
7222         MOZ_ASSERT(appliedOffset.x < 0);
7223       }
7224     }
7225 
7226     LayoutDeviceRect bounds =
7227         LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
7228     wr::LayoutVector2D applied = {
7229         NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
7230         NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
7231     wr::WrStickyId id = aBuilder.DefineStickyFrame(
7232         aSc.ToRelativeLayoutRect(bounds), topMargin.ptrOr(nullptr),
7233         rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
7234         leftMargin.ptrOr(nullptr), vBounds, hBounds, applied);
7235 
7236     aBuilder.PushStickyFrame(id, GetClipChain());
7237   }
7238 
7239   nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, aSc,
7240                                              aManager, aDisplayListBuilder);
7241 
7242   if (stickyScrollContainer) {
7243     aBuilder.PopStickyFrame(GetClipChain());
7244   }
7245 
7246   return true;
7247 }
7248 
nsDisplayScrollInfoLayer(nsDisplayListBuilder * aBuilder,nsIFrame * aScrolledFrame,nsIFrame * aScrollFrame)7249 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
7250     nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
7251     nsIFrame* aScrollFrame)
7252     : nsDisplayWrapList(aBuilder, aScrollFrame),
7253       mScrollFrame(aScrollFrame),
7254       mScrolledFrame(aScrolledFrame),
7255       mScrollParentId(aBuilder->GetCurrentScrollParentId()) {
7256 #ifdef NS_BUILD_REFCNT_LOGGING
7257   MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
7258 #endif
7259 }
7260 
7261 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayScrollInfoLayer()7262 nsDisplayScrollInfoLayer::~nsDisplayScrollInfoLayer() {
7263   MOZ_COUNT_DTOR(nsDisplayScrollInfoLayer);
7264 }
7265 #endif
7266 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)7267 already_AddRefed<Layer> nsDisplayScrollInfoLayer::BuildLayer(
7268     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
7269     const ContainerLayerParameters& aContainerParameters) {
7270   // In general for APZ with event-regions we no longer have a need for
7271   // scrollinfo layers. However, in some cases, there might be content that
7272   // cannot be layerized, and so needs to scroll synchronously. To handle those
7273   // cases, we still want to generate scrollinfo layers.
7274 
7275   return aManager->GetLayerBuilder()->BuildContainerLayerFor(
7276       aBuilder, aManager, mFrame, this, &mList, aContainerParameters, nullptr,
7277       FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR);
7278 }
7279 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)7280 LayerState nsDisplayScrollInfoLayer::GetLayerState(
7281     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
7282     const ContainerLayerParameters& aParameters) {
7283   return LAYER_ACTIVE_EMPTY;
7284 }
7285 
ComputeScrollMetadata(LayerManager * aLayerManager,const ContainerLayerParameters & aContainerParameters)7286 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata(
7287     LayerManager* aLayerManager,
7288     const ContainerLayerParameters& aContainerParameters) {
7289   nsRect viewport = mScrollFrame->GetRect() - mScrollFrame->GetPosition() +
7290                     mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
7291 
7292   ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata(
7293       mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(),
7294       ReferenceFrame(), aLayerManager, mScrollParentId, viewport, Nothing(),
7295       false, aContainerParameters);
7296   metadata.GetMetrics().SetIsScrollInfoLayer(true);
7297 
7298   return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata));
7299 }
7300 
UpdateScrollData(mozilla::layers::WebRenderScrollData * aData,mozilla::layers::WebRenderLayerScrollData * aLayerData)7301 bool nsDisplayScrollInfoLayer::UpdateScrollData(
7302     mozilla::layers::WebRenderScrollData* aData,
7303     mozilla::layers::WebRenderLayerScrollData* aLayerData) {
7304   if (aLayerData) {
7305     UniquePtr<ScrollMetadata> metadata =
7306         ComputeScrollMetadata(aData->GetManager(), ContainerLayerParameters());
7307     MOZ_ASSERT(aData);
7308     MOZ_ASSERT(metadata);
7309     aLayerData->AppendScrollMetadata(*aData, *metadata);
7310   }
7311   return true;
7312 }
7313 
WriteDebugInfo(std::stringstream & aStream)7314 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) {
7315   aStream << " (scrollframe " << mScrollFrame << " scrolledFrame "
7316           << mScrolledFrame << ")";
7317 }
7318 
nsDisplayZoom(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,int32_t aAPD,int32_t aParentAPD,nsDisplayOwnLayerFlags aFlags)7319 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
7320                              nsDisplayList* aList, int32_t aAPD,
7321                              int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags)
7322     : nsDisplaySubDocument(aBuilder, aFrame, nullptr, aList, aFlags),
7323       mAPD(aAPD),
7324       mParentAPD(aParentAPD) {
7325   MOZ_COUNT_CTOR(nsDisplayZoom);
7326 }
7327 
7328 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayZoom()7329 nsDisplayZoom::~nsDisplayZoom() { MOZ_COUNT_DTOR(nsDisplayZoom); }
7330 #endif
7331 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const7332 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder,
7333                                 bool* aSnap) const {
7334   nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
7335   *aSnap = false;
7336   return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD);
7337 }
7338 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)7339 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
7340                             HitTestState* aState,
7341                             nsTArray<nsIFrame*>* aOutFrames) {
7342   nsRect rect;
7343   // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
7344   // rect as well instead of possibly rounding the width or height to zero.
7345   if (aRect.width == 1 && aRect.height == 1) {
7346     rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD));
7347     rect.width = rect.height = 1;
7348   } else {
7349     rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
7350   }
7351   mList.HitTest(aBuilder, rect, aState, aOutFrames);
7352 }
7353 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)7354 bool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder* aBuilder,
7355                                       nsRegion* aVisibleRegion) {
7356   // Convert the passed in visible region to our appunits.
7357   nsRegion visibleRegion;
7358   // mVisibleRect has been clipped to GetClippedBounds
7359   visibleRegion.And(*aVisibleRegion, mVisibleRect);
7360   visibleRegion = visibleRegion.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
7361   nsRegion originalVisibleRegion = visibleRegion;
7362 
7363   nsRect transformedVisibleRect =
7364       mVisibleRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD);
7365   bool retval;
7366   // If we are to generate a scrollable layer we call
7367   // nsDisplaySubDocument::ComputeVisibility to make the necessary adjustments
7368   // for ComputeVisibility, it does all it's calculations in the child APD.
7369   bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
7370   if (!(mFlags & nsDisplayOwnLayerFlags::eGenerateScrollableLayer) ||
7371       !usingDisplayPort) {
7372     retval = mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion,
7373                                                transformedVisibleRect);
7374   } else {
7375     retval = nsDisplaySubDocument::ComputeVisibility(aBuilder, &visibleRegion);
7376   }
7377 
7378   nsRegion removed;
7379   // removed = originalVisibleRegion - visibleRegion
7380   removed.Sub(originalVisibleRegion, visibleRegion);
7381   // Convert removed region to parent appunits.
7382   removed = removed.ScaleToOtherAppUnitsRoundIn(mAPD, mParentAPD);
7383   // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications
7384   // SubtractFromVisibleRegion does)
7385   aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed);
7386 
7387   return retval;
7388 }
7389 
7390 ///////////////////////////////////////////////////
7391 // nsDisplayTransform Implementation
7392 //
7393 
7394 // Write #define UNIFIED_CONTINUATIONS here and in
7395 // TransformReferenceBox::Initialize to have the transform property try
7396 // to transform content with continuations as one unified block instead of
7397 // several smaller ones.  This is currently disabled because it doesn't work
7398 // correctly, since when the frames are initially being reflowed, their
7399 // continuations all compute their bounding rects independently of each other
7400 // and consequently get the wrong value.  Write #define DEBUG_HIT here to have
7401 // the nsDisplayTransform class dump out a bunch of information about hit
7402 // detection.
7403 #undef UNIFIED_CONTINUATIONS
7404 #undef DEBUG_HIT
7405 
nsDisplayTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const nsRect & aChildrenVisibleRect,ComputeTransformFunction aTransformGetter,uint32_t aIndex)7406 nsDisplayTransform::nsDisplayTransform(
7407     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
7408     const nsRect& aChildrenVisibleRect,
7409     ComputeTransformFunction aTransformGetter, uint32_t aIndex)
7410     : nsDisplayItem(aBuilder, aFrame),
7411       mStoredList(aBuilder, aFrame, aList),
7412       mTransformGetter(aTransformGetter),
7413       mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
7414       mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
7415       mChildrenVisibleRect(aChildrenVisibleRect),
7416       mIndex(aIndex),
7417       mNoExtendContext(false),
7418       mIsTransformSeparator(false),
7419       mTransformPreserves3DInited(false),
7420       mAllowAsyncAnimation(false) {
7421   MOZ_COUNT_CTOR(nsDisplayTransform);
7422   MOZ_ASSERT(aFrame, "Must have a frame!");
7423   Init(aBuilder);
7424 }
7425 
SetReferenceFrameToAncestor(nsDisplayListBuilder * aBuilder)7426 void nsDisplayTransform::SetReferenceFrameToAncestor(
7427     nsDisplayListBuilder* aBuilder) {
7428   if (mFrame == aBuilder->RootReferenceFrame()) {
7429     return;
7430   }
7431   nsIFrame* outerFrame = nsLayoutUtils::GetCrossDocParentFrame(mFrame);
7432   mReferenceFrame = aBuilder->FindReferenceFrameFor(outerFrame);
7433   mToReferenceFrame = mFrame->GetOffsetToCrossDoc(mReferenceFrame);
7434   if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(mFrame)) {
7435     // This is an odd special case. If we are both IsFixedPosFrameInDisplayPort
7436     // and transformed that we are our own AGR parent.
7437     // We want our frame to be our AGR because FrameLayerBuilder uses our AGR to
7438     // determine if we are inside a fixed pos subtree. If we use the outer AGR
7439     // from outside the fixed pos subtree FLB can't tell that we are fixed pos.
7440     mAnimatedGeometryRoot = mAnimatedGeometryRootForChildren;
7441   } else if (mFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY &&
7442              IsStickyFrameActive(aBuilder, mFrame, nullptr)) {
7443     // Similar to the IsFixedPosFrameInDisplayPort case we are our own AGR.
7444     // We are inside the sticky position, so our AGR is the sticky positioned
7445     // frame, which is our AGR, not the parent AGR.
7446     mAnimatedGeometryRoot = mAnimatedGeometryRootForChildren;
7447   } else if (mAnimatedGeometryRoot->mParentAGR) {
7448     mAnimatedGeometryRootForScrollMetadata = mAnimatedGeometryRoot->mParentAGR;
7449     if (!MayBeAnimated(aBuilder)) {
7450       // If we're an animated transform then we want the same AGR as our
7451       // children so that FrameLayerBuilder knows that this layer moves with the
7452       // transform and won't compute occlusions. If we're not animated then use
7453       // our parent AGR so that inactive transform layers can go in the same
7454       // PaintedLayer as surrounding content.
7455       mAnimatedGeometryRoot = mAnimatedGeometryRoot->mParentAGR;
7456     }
7457   }
7458 
7459   SetVisibleRect(aBuilder->GetVisibleRect() + mToReferenceFrame, true);
7460 }
7461 
Init(nsDisplayListBuilder * aBuilder)7462 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder) {
7463   mHasBounds = false;
7464   mStoredList.SetClipChain(nullptr, true);
7465   mStoredList.SetVisibleRect(mChildrenVisibleRect, true);
7466 }
7467 
nsDisplayTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const nsRect & aChildrenVisibleRect,uint32_t aIndex,bool aAllowAsyncAnimation)7468 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
7469                                        nsIFrame* aFrame, nsDisplayList* aList,
7470                                        const nsRect& aChildrenVisibleRect,
7471                                        uint32_t aIndex,
7472                                        bool aAllowAsyncAnimation)
7473     : nsDisplayItem(aBuilder, aFrame),
7474       mStoredList(aBuilder, aFrame, aList),
7475       mTransformGetter(nullptr),
7476       mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
7477       mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
7478       mChildrenVisibleRect(aChildrenVisibleRect),
7479       mIndex(aIndex),
7480       mNoExtendContext(false),
7481       mIsTransformSeparator(false),
7482       mTransformPreserves3DInited(false),
7483       mAllowAsyncAnimation(aAllowAsyncAnimation) {
7484   MOZ_COUNT_CTOR(nsDisplayTransform);
7485   MOZ_ASSERT(aFrame, "Must have a frame!");
7486   SetReferenceFrameToAncestor(aBuilder);
7487   Init(aBuilder);
7488   UpdateBoundsFor3D(aBuilder);
7489 }
7490 
nsDisplayTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayItem * aItem,const nsRect & aChildrenVisibleRect,uint32_t aIndex)7491 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
7492                                        nsIFrame* aFrame, nsDisplayItem* aItem,
7493                                        const nsRect& aChildrenVisibleRect,
7494                                        uint32_t aIndex)
7495     : nsDisplayItem(aBuilder, aFrame),
7496       mStoredList(aBuilder, aFrame, aItem),
7497       mTransformGetter(nullptr),
7498       mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
7499       mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
7500       mChildrenVisibleRect(aChildrenVisibleRect),
7501       mIndex(aIndex),
7502       mNoExtendContext(false),
7503       mIsTransformSeparator(false),
7504       mTransformPreserves3DInited(false),
7505       mAllowAsyncAnimation(false) {
7506   MOZ_COUNT_CTOR(nsDisplayTransform);
7507   MOZ_ASSERT(aFrame, "Must have a frame!");
7508   SetReferenceFrameToAncestor(aBuilder);
7509   Init(aBuilder);
7510 }
7511 
nsDisplayTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const nsRect & aChildrenVisibleRect,const Matrix4x4 & aTransform,uint32_t aIndex)7512 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
7513                                        nsIFrame* aFrame, nsDisplayList* aList,
7514                                        const nsRect& aChildrenVisibleRect,
7515                                        const Matrix4x4& aTransform,
7516                                        uint32_t aIndex)
7517     : nsDisplayItem(aBuilder, aFrame),
7518       mStoredList(aBuilder, aFrame, aList),
7519       mTransform(aTransform),
7520       mTransformGetter(nullptr),
7521       mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot),
7522       mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
7523       mChildrenVisibleRect(aChildrenVisibleRect),
7524       mIndex(aIndex),
7525       mNoExtendContext(false),
7526       mIsTransformSeparator(true),
7527       mTransformPreserves3DInited(false),
7528       mAllowAsyncAnimation(false) {
7529   MOZ_COUNT_CTOR(nsDisplayTransform);
7530   MOZ_ASSERT(aFrame, "Must have a frame!");
7531   Init(aBuilder);
7532   UpdateBoundsFor3D(aBuilder);
7533 }
7534 
7535 /* Returns the delta specified by the transform-origin property.
7536  * This is a positive delta, meaning that it indicates the direction to move
7537  * to get from (0, 0) of the frame to the transform origin.  This function is
7538  * called off the main thread.
7539  */
GetDeltaToTransformOrigin(const nsIFrame * aFrame,float aAppUnitsPerPixel,const nsRect * aBoundsOverride)7540 /* static */ Point3D nsDisplayTransform::GetDeltaToTransformOrigin(
7541     const nsIFrame* aFrame, float aAppUnitsPerPixel,
7542     const nsRect* aBoundsOverride) {
7543   NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
7544   NS_PRECONDITION(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
7545                       aFrame->Combines3DTransformWithAncestors(),
7546                   "Shouldn't get a delta for an untransformed frame!");
7547 
7548   if (!aFrame->IsTransformed()) {
7549     return Point3D();
7550   }
7551 
7552   /* For both of the coordinates, if the value of transform is a
7553    * percentage, it's relative to the size of the frame.  Otherwise, if it's
7554    * a distance, it's already computed for us!
7555    */
7556   const nsStyleDisplay* display = aFrame->StyleDisplay();
7557   // We don't use aBoundsOverride for SVG since we need to account for
7558   // refBox.X/Y(). This happens to work because ReflowSVG sets the frame's
7559   // mRect before calling FinishAndStoreOverflow so we don't need the override.
7560   TransformReferenceBox refBox;
7561   if (aBoundsOverride && !(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
7562     refBox.Init(aBoundsOverride->Size());
7563   } else {
7564     refBox.Init(aFrame);
7565   }
7566 
7567   /* Allows us to access dimension getters by index. */
7568   float transformOrigin[2];
7569   TransformReferenceBox::DimensionGetter dimensionGetter[] = {
7570       &TransformReferenceBox::Width, &TransformReferenceBox::Height};
7571   TransformReferenceBox::DimensionGetter offsetGetter[] = {
7572       &TransformReferenceBox::X, &TransformReferenceBox::Y};
7573 
7574   for (uint8_t index = 0; index < 2; ++index) {
7575     /* If the transform-origin specifies a percentage, take the percentage
7576      * of the size of the box.
7577      */
7578     const nsStyleCoord& originValue = display->mTransformOrigin[index];
7579     if (originValue.GetUnit() == eStyleUnit_Calc) {
7580       const nsStyleCoord::Calc* calc = originValue.GetCalcValue();
7581       transformOrigin[index] =
7582           NSAppUnitsToFloatPixels((refBox.*dimensionGetter[index])(),
7583                                   aAppUnitsPerPixel) *
7584               calc->mPercent +
7585           NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
7586     } else if (originValue.GetUnit() == eStyleUnit_Percent) {
7587       transformOrigin[index] =
7588           NSAppUnitsToFloatPixels((refBox.*dimensionGetter[index])(),
7589                                   aAppUnitsPerPixel) *
7590           originValue.GetPercentValue();
7591     } else {
7592       MOZ_ASSERT(originValue.GetUnit() == eStyleUnit_Coord, "unexpected unit");
7593       transformOrigin[index] = NSAppUnitsToFloatPixels(
7594           originValue.GetCoordValue(), aAppUnitsPerPixel);
7595     }
7596 
7597     if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
7598       // SVG frames (unlike other frames) have a reference box that can be (and
7599       // typically is) offset from the TopLeft() of the frame. We need to
7600       // account for that here.
7601       transformOrigin[index] += NSAppUnitsToFloatPixels(
7602           (refBox.*offsetGetter[index])(), aAppUnitsPerPixel);
7603     }
7604   }
7605 
7606   return Point3D(
7607       transformOrigin[0], transformOrigin[1],
7608       NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
7609                               aAppUnitsPerPixel));
7610 }
7611 
ComputePerspectiveMatrix(const nsIFrame * aFrame,float aAppUnitsPerPixel,Matrix4x4 & aOutMatrix)7612 /* static */ bool nsDisplayTransform::ComputePerspectiveMatrix(
7613     const nsIFrame* aFrame, float aAppUnitsPerPixel, Matrix4x4& aOutMatrix) {
7614   NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
7615   NS_PRECONDITION(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() ||
7616                       aFrame->Combines3DTransformWithAncestors(),
7617                   "Shouldn't get a delta for an untransformed frame!");
7618   NS_PRECONDITION(aOutMatrix.IsIdentity(), "Must have a blank output matrix");
7619 
7620   if (!aFrame->IsTransformed()) {
7621     return false;
7622   }
7623 
7624   /* Find our containing block, which is the element that provides the
7625    * value for perspective we need to use
7626    */
7627 
7628   // TODO: Is it possible that the cbFrame's bounds haven't been set correctly
7629   // yet
7630   // (similar to the aBoundsOverride case for GetResultingTransformMatrix)?
7631   nsIFrame* cbFrame = aFrame->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME);
7632   if (!cbFrame) {
7633     return false;
7634   }
7635 
7636   /* Grab the values for perspective and perspective-origin (if present) */
7637 
7638   const nsStyleDisplay* cbDisplay = cbFrame->StyleDisplay();
7639   if (cbDisplay->mChildPerspective.GetUnit() != eStyleUnit_Coord) {
7640     return false;
7641   }
7642   nscoord perspective = cbDisplay->mChildPerspective.GetCoordValue();
7643   if (perspective < std::numeric_limits<Float>::epsilon()) {
7644     return true;
7645   }
7646 
7647   TransformReferenceBox refBox(cbFrame);
7648 
7649   Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition(
7650       cbDisplay->mPerspectiveOrigin, refBox, aAppUnitsPerPixel);
7651 
7652   /* GetOffsetTo computes the offset required to move from 0,0 in cbFrame to 0,0
7653    * in aFrame. Although we actually want the inverse of this, it's faster to
7654    * compute this way.
7655    */
7656   nsPoint frameToCbOffset = -aFrame->GetOffsetTo(cbFrame);
7657   Point frameToCbGfxOffset(
7658       NSAppUnitsToFloatPixels(frameToCbOffset.x, aAppUnitsPerPixel),
7659       NSAppUnitsToFloatPixels(frameToCbOffset.y, aAppUnitsPerPixel));
7660 
7661   /* Move the perspective origin to be relative to aFrame, instead of relative
7662    * to the containing block which is how it was specified in the style system.
7663    */
7664   perspectiveOrigin += frameToCbGfxOffset;
7665 
7666   aOutMatrix._34 =
7667       -1.0 / NSAppUnitsToFloatPixels(perspective, aAppUnitsPerPixel);
7668 
7669   aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
7670   return true;
7671 }
7672 
FrameTransformProperties(const nsIFrame * aFrame,float aAppUnitsPerPixel,const nsRect * aBoundsOverride)7673 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(
7674     const nsIFrame* aFrame, float aAppUnitsPerPixel,
7675     const nsRect* aBoundsOverride)
7676     : mFrame(aFrame),
7677       mTransformList(aFrame->StyleDisplay()->GetCombinedTransform()),
7678       mToTransformOrigin(GetDeltaToTransformOrigin(aFrame, aAppUnitsPerPixel,
7679                                                    aBoundsOverride)) {}
7680 
7681 /* Wraps up the transform matrix in a change-of-basis matrix pair that
7682  * translates from local coordinate space to transform coordinate space, then
7683  * hands it back.
7684  */
GetResultingTransformMatrix(const FrameTransformProperties & aProperties,const nsPoint & aOrigin,float aAppUnitsPerPixel,uint32_t aFlags,const nsRect * aBoundsOverride)7685 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
7686     const FrameTransformProperties& aProperties, const nsPoint& aOrigin,
7687     float aAppUnitsPerPixel, uint32_t aFlags, const nsRect* aBoundsOverride) {
7688   return GetResultingTransformMatrixInternal(
7689       aProperties, aOrigin, aAppUnitsPerPixel, aFlags, aBoundsOverride);
7690 }
7691 
GetResultingTransformMatrix(const nsIFrame * aFrame,const nsPoint & aOrigin,float aAppUnitsPerPixel,uint32_t aFlags,const nsRect * aBoundsOverride)7692 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix(
7693     const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel,
7694     uint32_t aFlags, const nsRect* aBoundsOverride) {
7695   FrameTransformProperties props(aFrame, aAppUnitsPerPixel, aBoundsOverride);
7696 
7697   return GetResultingTransformMatrixInternal(props, aOrigin, aAppUnitsPerPixel,
7698                                              aFlags, aBoundsOverride);
7699 }
7700 
GetResultingTransformMatrixInternal(const FrameTransformProperties & aProperties,const nsPoint & aOrigin,float aAppUnitsPerPixel,uint32_t aFlags,const nsRect * aBoundsOverride)7701 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal(
7702     const FrameTransformProperties& aProperties, const nsPoint& aOrigin,
7703     float aAppUnitsPerPixel, uint32_t aFlags, const nsRect* aBoundsOverride) {
7704   const nsIFrame* frame = aProperties.mFrame;
7705   NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE),
7706                "Must have a frame to compute perspective!");
7707 
7708   // Get the underlying transform matrix:
7709 
7710   // We don't use aBoundsOverride for SVG since we need to account for
7711   // refBox.X/Y(). This happens to work because ReflowSVG sets the frame's
7712   // mRect before calling FinishAndStoreOverflow so we don't need the override.
7713   TransformReferenceBox refBox;
7714   if (aBoundsOverride &&
7715       (!frame || !(frame->GetStateBits() & NS_FRAME_SVG_LAYOUT))) {
7716     refBox.Init(aBoundsOverride->Size());
7717   } else {
7718     refBox.Init(frame);
7719   }
7720 
7721   /* Get the matrix, then change its basis to factor in the origin. */
7722   RuleNodeCacheConditions dummy;
7723   bool dummyBool;
7724   Matrix4x4 result;
7725   // Call IsSVGTransformed() regardless of the value of
7726   // disp->mSpecifiedTransform, since we still need any
7727   // parentsChildrenOnlyTransform.
7728   Matrix svgTransform, parentsChildrenOnlyTransform;
7729   bool hasSVGTransforms =
7730       frame &&
7731       frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform);
7732   /* Transformed frames always have a transform, or are preserving 3d (and might
7733    * still have perspective!) */
7734   if (aProperties.mTransformList) {
7735     result = nsStyleTransformMatrix::ReadTransforms(
7736         aProperties.mTransformList->mHead,
7737         frame ? frame->StyleContext() : nullptr,
7738         frame ? frame->PresContext() : nullptr, dummy, refBox,
7739         aAppUnitsPerPixel, &dummyBool);
7740   } else if (hasSVGTransforms) {
7741     // Correct the translation components for zoom:
7742     float pixelsPerCSSPx =
7743         frame->PresContext()->AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
7744     svgTransform._31 *= pixelsPerCSSPx;
7745     svgTransform._32 *= pixelsPerCSSPx;
7746     result = Matrix4x4::From2D(svgTransform);
7747   }
7748 
7749   // Apply any translation due to 'transform-origin' and/or 'transform-box':
7750   result.ChangeBasis(aProperties.mToTransformOrigin);
7751 
7752   // See the comment for nsSVGContainerFrame::HasChildrenOnlyTransform for
7753   // an explanation of what children-only transforms are.
7754   bool parentHasChildrenOnlyTransform =
7755       hasSVGTransforms && !parentsChildrenOnlyTransform.IsIdentity();
7756 
7757   if (parentHasChildrenOnlyTransform) {
7758     float pixelsPerCSSPx =
7759         frame->PresContext()->AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
7760     parentsChildrenOnlyTransform._31 *= pixelsPerCSSPx;
7761     parentsChildrenOnlyTransform._32 *= pixelsPerCSSPx;
7762 
7763     Point3D frameOffset(
7764         NSAppUnitsToFloatPixels(-frame->GetPosition().x, aAppUnitsPerPixel),
7765         NSAppUnitsToFloatPixels(-frame->GetPosition().y, aAppUnitsPerPixel), 0);
7766     Matrix4x4 parentsChildrenOnlyTransform3D =
7767         Matrix4x4::From2D(parentsChildrenOnlyTransform)
7768             .ChangeBasis(frameOffset);
7769 
7770     result *= parentsChildrenOnlyTransform3D;
7771   }
7772 
7773   Matrix4x4 perspectiveMatrix;
7774   bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE;
7775   if (hasPerspective) {
7776     if (ComputePerspectiveMatrix(frame, aAppUnitsPerPixel, perspectiveMatrix)) {
7777       result *= perspectiveMatrix;
7778     }
7779   }
7780 
7781   if ((aFlags & INCLUDE_PRESERVE3D_ANCESTORS) && frame &&
7782       frame->Combines3DTransformWithAncestors()) {
7783     // Include the transform set on our parent
7784     nsIFrame* parentFrame = frame->GetInFlowParent();
7785     NS_ASSERTION(parentFrame && parentFrame->IsTransformed() &&
7786                      parentFrame->Extend3DContext(),
7787                  "Preserve3D mismatch!");
7788     FrameTransformProperties props(parentFrame, aAppUnitsPerPixel, nullptr);
7789 
7790     uint32_t flags =
7791         aFlags & (INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE);
7792 
7793     // If this frame isn't transformed (but we exist for backface-visibility),
7794     // then we're not a reference frame so no offset to origin will be added.
7795     // Otherwise we need to manually translate into our parent's coordinate
7796     // space.
7797     if (frame->IsTransformed()) {
7798       nsLayoutUtils::PostTranslate(result, frame->GetPosition(),
7799                                    aAppUnitsPerPixel, !hasSVGTransforms);
7800     }
7801     Matrix4x4 parent = GetResultingTransformMatrixInternal(
7802         props, nsPoint(0, 0), aAppUnitsPerPixel, flags, nullptr);
7803     result = result * parent;
7804   }
7805 
7806   if (aFlags & OFFSET_BY_ORIGIN) {
7807     nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel,
7808                                  !hasSVGTransforms);
7809   }
7810 
7811   return result;
7812 }
7813 
CanUseAsyncAnimations(nsDisplayListBuilder * aBuilder)7814 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
7815   if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame,
7816                                           eCSSProperty_opacity)) {
7817     return true;
7818   }
7819 
7820   EffectCompositor::SetPerformanceWarning(
7821       mFrame, eCSSProperty_opacity,
7822       AnimationPerformanceWarning(
7823           AnimationPerformanceWarning::Type::OpacityFrameInactive));
7824 
7825   return false;
7826 }
7827 
CanUseAsyncAnimations(nsDisplayListBuilder * aBuilder)7828 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
7829   return mAllowAsyncAnimation;
7830 }
7831 
ShouldPrerenderTransformedContent(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsRect * aDirtyRect)7832 /* static */ auto nsDisplayTransform::ShouldPrerenderTransformedContent(
7833     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect)
7834     -> PrerenderDecision {
7835   // Elements whose transform has been modified recently, or which
7836   // have a compositor-animated transform, can be prerendered. An element
7837   // might have only just had its transform animated in which case
7838   // the ActiveLayerManager may not have been notified yet.
7839   if (!ActiveLayerTracker::IsStyleMaybeAnimated(aFrame,
7840                                                 eCSSProperty_transform) &&
7841       !EffectCompositor::HasAnimationsForCompositor(aFrame,
7842                                                     eCSSProperty_transform)) {
7843     EffectCompositor::SetPerformanceWarning(
7844         aFrame, eCSSProperty_transform,
7845         AnimationPerformanceWarning(
7846             AnimationPerformanceWarning::Type::TransformFrameInactive));
7847 
7848     return NoPrerender;
7849   }
7850 
7851   // We should not allow prerender if any ancestor container element has
7852   // mask/clip-path effects.
7853   //
7854   // With prerender and async transform animation, we do not need to restyle an
7855   // animated element to respect position changes, since that transform is done
7856   // by layer animation. As a result, the container element is not aware of
7857   // position change of that containing element and loses the chance to update
7858   // the content of mask/clip-path.
7859   //
7860   // Why do we need to update a mask? This is relative to how we generate a
7861   // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a
7862   // mask layer, to reduce memory usage, we did not choose the size of the
7863   // masked element as mask size. Instead, we read the union of bounds of all
7864   // children display items by nsDisplayWrapList::GetBounds, which is smaller
7865   // than or equal to the masked element's boundary, and use it as the position
7866   // size of the mask layer. That union bounds is actually affected by the
7867   // geometry of the animated element. To keep the content of mask up to date,
7868   // forbidding of prerender is required.
7869   for (nsIFrame* container = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
7870        container;
7871        container = nsLayoutUtils::GetCrossDocParentFrame(container)) {
7872     const nsStyleSVGReset* svgReset = container->StyleSVGReset();
7873     if (svgReset->HasMask() || svgReset->HasClipPath()) {
7874       return NoPrerender;
7875     }
7876   }
7877 
7878   // If the incoming dirty rect already contains the entire overflow area,
7879   // we are already rendering the entire content.
7880   nsRect overflow = aFrame->GetVisualOverflowRectRelativeToSelf();
7881   if (aDirtyRect->Contains(overflow)) {
7882     return FullPrerender;
7883   }
7884 
7885   float viewportRatioX = gfxPrefs::AnimationPrerenderViewportRatioLimitX();
7886   float viewportRatioY = gfxPrefs::AnimationPrerenderViewportRatioLimitY();
7887   uint32_t absoluteLimitX = gfxPrefs::AnimationPrerenderAbsoluteLimitX();
7888   uint32_t absoluteLimitY = gfxPrefs::AnimationPrerenderAbsoluteLimitY();
7889   nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
7890   // Only prerender if the transformed frame's size is <= a multiple of the
7891   // reference frame size (~viewport), and less than an absolute limit.
7892   // Both the ratio and the absolute limit are configurable.
7893   nsSize relativeLimit(nscoord(refSize.width * viewportRatioX),
7894                        nscoord(refSize.height * viewportRatioY));
7895   nsSize absoluteLimit(
7896       aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
7897       aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
7898   nsSize maxSize = Min(relativeLimit, absoluteLimit);
7899   gfxSize scale = nsLayoutUtils::GetTransformToAncestorScale(aFrame);
7900   nsSize frameSize(overflow.Size().width * scale.width,
7901                    overflow.Size().height * scale.height);
7902   uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
7903   uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
7904   if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
7905     *aDirtyRect = overflow;
7906     return FullPrerender;
7907   } else if (gfxPrefs::PartiallyPrerenderAnimatedContent()) {
7908     *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(*aDirtyRect,
7909                                                              overflow, maxSize);
7910     return PartialPrerender;
7911   }
7912 
7913   if (frameArea > maxLimitArea) {
7914     uint64_t appUnitsPerPixel = nsPresContext::AppUnitsPerCSSPixel();
7915     EffectCompositor::SetPerformanceWarning(
7916         aFrame, eCSSProperty_transform,
7917         AnimationPerformanceWarning(
7918             AnimationPerformanceWarning::Type::ContentTooLargeArea,
7919             {
7920                 int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
7921                 int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
7922             }));
7923   } else {
7924     EffectCompositor::SetPerformanceWarning(
7925         aFrame, eCSSProperty_transform,
7926         AnimationPerformanceWarning(
7927             AnimationPerformanceWarning::Type::ContentTooLarge,
7928             {
7929                 nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
7930                 nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
7931                 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
7932                 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
7933                 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
7934                 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
7935             }));
7936   }
7937 
7938   return NoPrerender;
7939 }
7940 
7941 /* If the matrix is singular, or a hidden backface is shown, the frame won't be
7942  * visible or hit. */
IsFrameVisible(nsIFrame * aFrame,const Matrix4x4 & aMatrix)7943 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix) {
7944   if (aMatrix.IsSingular()) {
7945     return false;
7946   }
7947   if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) {
7948     return false;
7949   }
7950   return true;
7951 }
7952 
GetTransform() const7953 const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const {
7954   if (mTransform.IsIdentity()) {
7955     float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
7956     Point3D newOrigin =
7957         Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
7958                 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f);
7959     if (mTransformGetter) {
7960       mTransform = mTransformGetter(mFrame, scale);
7961       mTransform.ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
7962     } else if (!mIsTransformSeparator) {
7963       DebugOnly<bool> isReference =
7964           mFrame->IsTransformed() ||
7965           mFrame->Combines3DTransformWithAncestors() ||
7966           mFrame->Extend3DContext();
7967       MOZ_ASSERT(isReference);
7968       mTransform =
7969           GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale,
7970                                       INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN);
7971     }
7972   }
7973   return mTransform;
7974 }
7975 
GetTransformForRendering(LayoutDevicePoint * aOutOrigin)7976 Matrix4x4 nsDisplayTransform::GetTransformForRendering(
7977     LayoutDevicePoint* aOutOrigin) {
7978   if (!mFrame->HasPerspective() || mTransformGetter || mIsTransformSeparator) {
7979     if (!mTransformGetter && !mIsTransformSeparator && aOutOrigin) {
7980       // If aOutOrigin is provided, put the offset to origin into it, because
7981       // we need to keep it separate for webrender. The combination of
7982       // *aOutOrigin and the returned matrix here should always be equivalent
7983       // to what GetTransform() would have returned.
7984       float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
7985       *aOutOrigin = LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale);
7986       return GetResultingTransformMatrix(mFrame, nsPoint(0, 0), scale,
7987                                          INCLUDE_PERSPECTIVE);
7988     }
7989     return GetTransform().GetMatrix();
7990   }
7991   MOZ_ASSERT(!mTransformGetter);
7992 
7993   float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
7994   // Don't include perspective transform, or the offset to origin, since
7995   // nsDisplayPerspective will handle both of those.
7996   return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
7997 }
7998 
GetAccumulatedPreserved3DTransform(nsDisplayListBuilder * aBuilder)7999 const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform(
8000     nsDisplayListBuilder* aBuilder) {
8001   MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
8002   // XXX: should go back to fix mTransformGetter.
8003   if (!mTransformPreserves3DInited) {
8004     mTransformPreserves3DInited = true;
8005     if (!IsLeafOf3DContext()) {
8006       mTransformPreserves3D = GetTransform().GetMatrix();
8007       return mTransformPreserves3D;
8008     }
8009 
8010     const nsIFrame* establisher;  // Establisher of the 3D rendering context.
8011     for (establisher = mFrame;
8012          establisher && establisher->Combines3DTransformWithAncestors();
8013          establisher = establisher->GetInFlowParent()) {
8014     }
8015     const nsIFrame* establisherReference = aBuilder->FindReferenceFrameFor(
8016         nsLayoutUtils::GetCrossDocParentFrame(establisher));
8017 
8018     nsPoint offset = establisher->GetOffsetToCrossDoc(establisherReference);
8019     float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
8020     uint32_t flags =
8021         INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN;
8022     mTransformPreserves3D =
8023         GetResultingTransformMatrix(mFrame, offset, scale, flags);
8024   }
8025   return mTransformPreserves3D;
8026 }
8027 
ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder * aBuilder) const8028 bool nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(
8029     nsDisplayListBuilder* aBuilder) const {
8030   // The visible rect of a Preserves-3D frame is just an intermediate
8031   // result.  It should always build a layer to make sure it is
8032   // rendering correctly.
8033   return MayBeAnimated(aBuilder) || mFrame->Combines3DTransformWithAncestors();
8034 }
8035 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)8036 bool nsDisplayTransform::CreateWebRenderCommands(
8037     mozilla::wr::DisplayListBuilder& aBuilder,
8038     mozilla::wr::IpcResourceUpdateQueue& aResources,
8039     const StackingContextHelper& aSc, WebRenderLayerManager* aManager,
8040     nsDisplayListBuilder* aDisplayListBuilder) {
8041   // We want to make sure we don't pollute the transform property in the WR
8042   // stacking context by including the position of this frame (relative to the
8043   // parent reference frame). We need to keep those separate; the position of
8044   // this frame goes into the stacking context bounds while the transform goes
8045   // into the transform.
8046   LayoutDevicePoint position;
8047   Matrix4x4 newTransformMatrix = GetTransformForRendering(&position);
8048 
8049   gfx::Matrix4x4* transformForSC = &newTransformMatrix;
8050   if (newTransformMatrix.IsIdentity()) {
8051     // If the transform is an identity transform, strip it out so that WR
8052     // doesn't turn this stacking context into a reference frame, as it
8053     // affects positioning. Bug 1345577 tracks a better fix.
8054     transformForSC = nullptr;
8055   }
8056 
8057   RefPtr<WebRenderAnimationData> animationData =
8058       aManager->CommandBuilder()
8059           .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this);
8060 
8061   AnimationInfo& animationInfo = animationData->GetAnimationInfo();
8062   AddAnimationsForProperty(Frame(), aDisplayListBuilder, this,
8063                            eCSSProperty_transform, animationInfo, false, true);
8064   animationInfo.StartPendingAnimations(aManager->GetAnimationReadyTime());
8065 
8066   // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
8067   // are no active animations.
8068   uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
8069   wr::WrAnimationProperty prop;
8070   if (!animationInfo.GetAnimations().IsEmpty()) {
8071     // Update transfrom as nullptr in stacking context if there exists
8072     // transform animation, the transform value will be resolved
8073     // after animation sampling on the compositor
8074     transformForSC = nullptr;
8075 
8076     prop.id = animationsId;
8077     prop.effect_type = wr::WrAnimationType::Transform;
8078 
8079     // Pass default transform to compositor in case gecko fails to
8080     // get animated value after animation sampling.
8081     OptionalTransform transformForCompositor = newTransformMatrix;
8082 
8083     OpAddCompositorAnimations anim(
8084         CompositorAnimations(animationInfo.GetAnimations(), animationsId),
8085         transformForCompositor, void_t());
8086     aManager->WrBridge()->AddWebRenderParentCommand(anim);
8087     aManager->AddActiveCompositorAnimationId(animationsId);
8088   } else if (animationsId) {
8089     aManager->AddCompositorAnimationsIdForDiscard(animationsId);
8090     animationsId = 0;
8091   }
8092 
8093   nsTArray<mozilla::wr::WrFilterOp> filters;
8094   StackingContextHelper sc(
8095       aSc, aBuilder, filters, LayoutDeviceRect(position, LayoutDeviceSize()),
8096       &newTransformMatrix, animationsId ? &prop : nullptr, nullptr,
8097       transformForSC, nullptr, gfx::CompositionOp::OP_OVER, !BackfaceIsHidden(),
8098       mFrame->Extend3DContext() && !mNoExtendContext);
8099 
8100   return mStoredList.CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8101                                              aDisplayListBuilder);
8102 }
8103 
UpdateScrollData(mozilla::layers::WebRenderScrollData * aData,mozilla::layers::WebRenderLayerScrollData * aLayerData)8104 bool nsDisplayTransform::UpdateScrollData(
8105     mozilla::layers::WebRenderScrollData* aData,
8106     mozilla::layers::WebRenderLayerScrollData* aLayerData) {
8107   if (aLayerData) {
8108     aLayerData->SetTransform(GetTransform().GetMatrix());
8109     aLayerData->SetTransformIsPerspective(mFrame->HasPerspective());
8110   }
8111   return true;
8112 }
8113 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)8114 already_AddRefed<Layer> nsDisplayTransform::BuildLayer(
8115     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
8116     const ContainerLayerParameters& aContainerParameters) {
8117   // While generating a glyph mask, the transform vector of the root frame had
8118   // been applied into the target context, so stop applying it again here.
8119   const bool shouldSkipTransform = (aBuilder->RootReferenceFrame() == mFrame) &&
8120                                    (aBuilder->IsForGenerateGlyphMask() ||
8121                                     aBuilder->IsForPaintingSelectionBG());
8122 
8123   /* For frames without transform, it would not be removed for
8124    * backface hidden here.  But, it would be removed by the init
8125    * function of nsDisplayTransform.
8126    */
8127   const Matrix4x4 newTransformMatrix =
8128       shouldSkipTransform ? Matrix4x4() : GetTransformForRendering();
8129 
8130   uint32_t flags = FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR;
8131   RefPtr<ContainerLayer> container =
8132       aManager->GetLayerBuilder()->BuildContainerLayerFor(
8133           aBuilder, aManager, mFrame, this, mStoredList.GetChildren(),
8134           aContainerParameters, &newTransformMatrix, flags);
8135 
8136   if (!container) {
8137     return nullptr;
8138   }
8139 
8140   // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all
8141   // flags, so we never need to explicitely unset this flag.
8142   if (mFrame->Extend3DContext() && !mNoExtendContext) {
8143     container->SetContentFlags(container->GetContentFlags() |
8144                                Layer::CONTENT_EXTEND_3D_CONTEXT);
8145   } else {
8146     container->SetContentFlags(container->GetContentFlags() &
8147                                ~Layer::CONTENT_EXTEND_3D_CONTEXT);
8148   }
8149 
8150   nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
8151       container, aBuilder, this, mFrame, eCSSProperty_transform);
8152   if (mAllowAsyncAnimation && MayBeAnimated(aBuilder)) {
8153     // Only allow async updates to the transform if we're an animated layer,
8154     // since that's what triggers us to set the correct AGR in the constructor
8155     // and makes sure FrameLayerBuilder won't compute occlusions for this layer.
8156     container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
8157                            /*the value is irrelevant*/ nullptr);
8158     container->SetContentFlags(container->GetContentFlags() |
8159                                Layer::CONTENT_MAY_CHANGE_TRANSFORM);
8160   } else {
8161     container->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
8162     container->SetContentFlags(container->GetContentFlags() &
8163                                ~Layer::CONTENT_MAY_CHANGE_TRANSFORM);
8164   }
8165   return container.forget();
8166 }
8167 
MayBeAnimated(nsDisplayListBuilder * aBuilder) const8168 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
8169   // If EffectCompositor::HasAnimationsForCompositor() is true then we can
8170   // completely bypass the main thread for this animation, so it is always
8171   // worthwhile.
8172   // For ActiveLayerTracker::IsStyleAnimated() cases the main thread is
8173   // already involved so there is less to be gained.
8174   // Therefore we check that the *post-transform* bounds of this item are
8175   // big enough to justify an active layer.
8176   if (EffectCompositor::HasAnimationsForCompositor(mFrame,
8177                                                    eCSSProperty_transform) ||
8178       (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame,
8179                                            eCSSProperty_transform) &&
8180        !IsItemTooSmallForActiveLayer(mFrame))) {
8181     return true;
8182   }
8183   return false;
8184 }
8185 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)8186 nsDisplayItem::LayerState nsDisplayTransform::GetLayerState(
8187     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
8188     const ContainerLayerParameters& aParameters) {
8189   // If the transform is 3d, the layer takes part in preserve-3d
8190   // sorting, or the layer is a separator then we *always* want this
8191   // to be an active layer.
8192   // Checking HasPerspective() is needed to handle perspective value 0 when
8193   // the transform is 2D.
8194   if (!GetTransform().Is2D() || mFrame->Combines3DTransformWithAncestors() ||
8195       mIsTransformSeparator || mFrame->HasPerspective()) {
8196     return LAYER_ACTIVE_FORCE;
8197   }
8198 
8199   if (MayBeAnimated(aBuilder)) {
8200     // Returns LAYER_ACTIVE_FORCE to avoid flatterning the layer for async
8201     // animations.
8202     return LAYER_ACTIVE_FORCE;
8203   }
8204 
8205   // Expect the child display items to have this frame as their animated
8206   // geometry root (since it will be their reference frame). If they have a
8207   // different animated geometry root, we'll make this an active layer so the
8208   // animation can be accelerated.
8209   return RequiredLayerStateForChildren(aBuilder, aManager, aParameters,
8210                                        *mStoredList.GetChildren(),
8211                                        mAnimatedGeometryRootForChildren);
8212 }
8213 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)8214 bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder* aBuilder,
8215                                            nsRegion* aVisibleRegion) {
8216   // nsDisplayTransform::GetBounds() returns an empty rect in nested 3d context.
8217   // Calling mStoredList.RecomputeVisibility below for such transform causes the
8218   // child display items to end up with empty visible rect.
8219   // We avoid this by bailing out always if we are dealing with a 3d context.
8220   if (mFrame->Extend3DContext() || mFrame->Combines3DTransformWithAncestors()) {
8221     return true;
8222   }
8223 
8224   /* As we do this, we need to be sure to
8225    * untransform the visible rect, since we want everything that's painting to
8226    * think that it's painting in its original rectangular coordinate space.
8227    * If we can't untransform, take the entire overflow rect */
8228   nsRect untransformedVisibleRect;
8229   if (!UntransformVisibleRect(aBuilder, &untransformedVisibleRect)) {
8230     untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf();
8231   }
8232   nsRegion untransformedVisible = untransformedVisibleRect;
8233   // Call RecomputeVisiblity instead of ComputeVisibility since
8234   // nsDisplayItem::ComputeVisibility should only be called from
8235   // nsDisplayList::ComputeVisibility (which sets mVisibleRect on the item)
8236   mStoredList.RecomputeVisibility(aBuilder, &untransformedVisible);
8237   return true;
8238 }
8239 
8240 #ifdef DEBUG_HIT
8241 #include <time.h>
8242 #endif
8243 
8244 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)8245 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
8246                                  const nsRect& aRect, HitTestState* aState,
8247                                  nsTArray<nsIFrame*>* aOutFrames) {
8248   if (aState->mInPreserves3D) {
8249     mStoredList.HitTest(aBuilder, aRect, aState, aOutFrames);
8250     return;
8251   }
8252 
8253   /* Here's how this works:
8254    * 1. Get the matrix.  If it's singular, abort (clearly we didn't hit
8255    *    anything).
8256    * 2. Invert the matrix.
8257    * 3. Use it to transform the rect into the correct space.
8258    * 4. Pass that rect down through to the list's version of HitTest.
8259    */
8260   // GetTransform always operates in dev pixels.
8261   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
8262   Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
8263 
8264   if (!IsFrameVisible(mFrame, matrix)) {
8265     return;
8266   }
8267 
8268   /* We want to go from transformed-space to regular space.
8269    * Thus we have to invert the matrix, which normally does
8270    * the reverse operation (e.g. regular->transformed)
8271    */
8272 
8273   /* Now, apply the transform and pass it down the channel. */
8274   matrix.Invert();
8275   nsRect resultingRect;
8276   if (aRect.width == 1 && aRect.height == 1) {
8277     // Magic width/height indicating we're hit testing a point, not a rect
8278     Point4D point =
8279         matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
8280                                   NSAppUnitsToFloatPixels(aRect.y, factor)));
8281     if (!point.HasPositiveWCoord()) {
8282       return;
8283     }
8284 
8285     Point point2d = point.As2DPoint();
8286 
8287     resultingRect =
8288         nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
8289                NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1);
8290 
8291   } else {
8292     Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
8293                       NSAppUnitsToFloatPixels(aRect.y, factor),
8294                       NSAppUnitsToFloatPixels(aRect.width, factor),
8295                       NSAppUnitsToFloatPixels(aRect.height, factor));
8296 
8297     bool snap;
8298     nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
8299     Rect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
8300                         NSAppUnitsToFloatPixels(childBounds.y, factor),
8301                         NSAppUnitsToFloatPixels(childBounds.width, factor),
8302                         NSAppUnitsToFloatPixels(childBounds.height, factor));
8303 
8304     Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds);
8305 
8306     resultingRect =
8307         nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
8308                NSFloatPixelsToAppUnits(float(rect.Y()), factor),
8309                NSFloatPixelsToAppUnits(float(rect.Width()), factor),
8310                NSFloatPixelsToAppUnits(float(rect.Height()), factor));
8311   }
8312 
8313   if (resultingRect.IsEmpty()) {
8314     return;
8315   }
8316 
8317 #ifdef DEBUG_HIT
8318   printf("Frame: %p\n", dynamic_cast<void*>(mFrame));
8319   printf("  Untransformed point: (%f, %f)\n", resultingRect.X(),
8320          resultingRect.Y());
8321   uint32_t originalFrameCount = aOutFrames.Length();
8322 #endif
8323 
8324   mStoredList.HitTest(aBuilder, resultingRect, aState, aOutFrames);
8325 
8326 #ifdef DEBUG_HIT
8327   if (originalFrameCount != aOutFrames.Length())
8328     printf("  Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
8329            dynamic_cast<void*>(aOutFrames.ElementAt(0)));
8330   printf("=== end of hit test ===\n");
8331 #endif
8332 }
8333 
GetHitDepthAtPoint(nsDisplayListBuilder * aBuilder,const nsPoint & aPoint)8334 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder,
8335                                              const nsPoint& aPoint) {
8336   // GetTransform always operates in dev pixels.
8337   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
8338   Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder);
8339 
8340   NS_ASSERTION(IsFrameVisible(mFrame, matrix),
8341                "We can't have hit a frame that isn't visible!");
8342 
8343   Matrix4x4 inverse = matrix;
8344   inverse.Invert();
8345   Point4D point =
8346       inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
8347                                  NSAppUnitsToFloatPixels(aPoint.y, factor)));
8348 
8349   Point point2d = point.As2DPoint();
8350 
8351   Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
8352   return transformed.z;
8353 }
8354 
8355 /* The bounding rectangle for the object is the overflow rectangle translated
8356  * by the reference point.
8357  */
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const8358 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
8359                                      bool* aSnap) const {
8360   *aSnap = false;
8361 
8362   if (mHasBounds) {
8363     return mBounds;
8364   }
8365 
8366   if (mFrame->Extend3DContext() && !mIsTransformSeparator) {
8367     return nsRect();
8368   }
8369 
8370   nsRect untransformedBounds = mStoredList.GetBounds(aBuilder, aSnap);
8371   // GetTransform always operates in dev pixels.
8372   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
8373   mBounds = nsLayoutUtils::MatrixTransformRect(untransformedBounds,
8374                                                GetTransform(), factor);
8375   mHasBounds = true;
8376   return mBounds;
8377 }
8378 
ComputeBounds(nsDisplayListBuilder * aBuilder)8379 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
8380   MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
8381 
8382   /* For some cases, the transform would make an empty bounds, but it
8383    * may be turned back again to get a non-empty bounds.  We should
8384    * not depend on transforming bounds level by level.
8385    *
8386    * Here, it applies accumulated transforms on the leaf frames of the
8387    * 3d rendering context, and track and accmulate bounds at
8388    * nsDisplayListBuilder.
8389    */
8390   nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
8391 
8392   accTransform.Accumulate(GetTransform().GetMatrix());
8393 
8394   if (!IsLeafOf3DContext()) {
8395     // Do not dive into another 3D context.
8396     mStoredList.DoUpdateBoundsPreserves3D(aBuilder);
8397   }
8398 
8399   /* For Preserves3D, it is bounds of only children as leaf frames.
8400    * For non-leaf frames, their bounds are accumulated and kept at
8401    * nsDisplayListBuilder.
8402    */
8403   bool snap;
8404   nsRect untransformedBounds = mStoredList.GetBounds(aBuilder, &snap);
8405   // GetTransform always operates in dev pixels.
8406   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
8407   nsRect rect = nsLayoutUtils::MatrixTransformRect(
8408       untransformedBounds, accTransform.GetCurrentTransform(), factor);
8409 
8410   aBuilder->AccumulateRect(rect);
8411 }
8412 
8413 /* The transform is opaque iff the transform consists solely of scales and
8414  * translations and if the underlying content is opaque.  Thus if the transform
8415  * is of the form
8416  *
8417  * |a c e|
8418  * |b d f|
8419  * |0 0 1|
8420  *
8421  * We need b and c to be zero.
8422  *
8423  * We also need to check whether the underlying opaque content completely fills
8424  * our visible rect. We use UntransformRect which expands to the axis-aligned
8425  * bounding rect, but that's OK since if
8426  * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
8427  * certainly contains the actual (non-axis-aligned) untransformed rect.
8428  */
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const8429 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
8430                                              bool* aSnap) const {
8431   *aSnap = false;
8432   nsRect untransformedVisible;
8433   if (!UntransformVisibleRect(aBuilder, &untransformedVisible)) {
8434     return nsRegion();
8435   }
8436 
8437   const Matrix4x4Flagged& matrix = GetTransform();
8438 
8439   nsRegion result;
8440   Matrix matrix2d;
8441   bool tmpSnap;
8442   if (matrix.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles() &&
8443       mStoredList.GetOpaqueRegion(aBuilder, &tmpSnap)
8444           .Contains(untransformedVisible)) {
8445     result = mVisibleRect.Intersect(GetBounds(aBuilder, &tmpSnap));
8446   }
8447   return result;
8448 }
8449 
8450 /* The transform is uniform if it fills the entire bounding rect and the
8451  * wrapped list is uniform.  See GetOpaqueRegion for discussion of why this
8452  * works.
8453  */
IsUniform(nsDisplayListBuilder * aBuilder) const8454 Maybe<nscolor> nsDisplayTransform::IsUniform(
8455     nsDisplayListBuilder* aBuilder) const {
8456   nsRect untransformedVisible;
8457   if (!UntransformVisibleRect(aBuilder, &untransformedVisible)) {
8458     return Nothing();
8459   }
8460   const Matrix4x4Flagged& matrix = GetTransform();
8461 
8462   Matrix matrix2d;
8463   if (matrix.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles() &&
8464       mStoredList.GetVisibleRect().Contains(untransformedVisible)) {
8465     return mStoredList.IsUniform(aBuilder);
8466   }
8467 
8468   return Nothing();
8469 }
8470 
8471 /* TransformRect takes in as parameters a rectangle (in app space) and returns
8472  * the smallest rectangle (in app space) containing the transformed image of
8473  * that rectangle.  That is, it takes the four corners of the rectangle,
8474  * transforms them according to the matrix associated with the specified frame,
8475  * then returns the smallest rectangle containing the four transformed points.
8476  *
8477  * @param aUntransformedBounds The rectangle (in app units) to transform.
8478  * @param aFrame The frame whose transformation should be applied.
8479  * @param aOrigin The delta from the frame origin to the coordinate space origin
8480  * @param aBoundsOverride (optional) Force the frame bounds to be the
8481  *        specified bounds.
8482  * @return The smallest rectangle containing the image of the transformed
8483  *         rectangle.
8484  */
TransformRect(const nsRect & aUntransformedBounds,const nsIFrame * aFrame,const nsRect * aBoundsOverride)8485 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds,
8486                                          const nsIFrame* aFrame,
8487                                          const nsRect* aBoundsOverride) {
8488   NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
8489 
8490   float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
8491 
8492   uint32_t flags =
8493       INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN | INCLUDE_PRESERVE3D_ANCESTORS;
8494   return nsLayoutUtils::MatrixTransformRect(
8495       aUntransformedBounds,
8496       GetResultingTransformMatrix(aFrame, nsPoint(0, 0), factor, flags,
8497                                   aBoundsOverride),
8498       factor);
8499 }
8500 
UntransformRect(const nsRect & aTransformedBounds,const nsRect & aChildBounds,const nsIFrame * aFrame,nsRect * aOutRect)8501 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds,
8502                                          const nsRect& aChildBounds,
8503                                          const nsIFrame* aFrame,
8504                                          nsRect* aOutRect) {
8505   NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
8506 
8507   float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
8508 
8509   uint32_t flags =
8510       INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN | INCLUDE_PRESERVE3D_ANCESTORS;
8511 
8512   Matrix4x4 transform =
8513       GetResultingTransformMatrix(aFrame, nsPoint(0, 0), factor, flags);
8514   if (transform.IsSingular()) {
8515     return false;
8516   }
8517 
8518   RectDouble result(NSAppUnitsToFloatPixels(aTransformedBounds.x, factor),
8519                     NSAppUnitsToFloatPixels(aTransformedBounds.y, factor),
8520                     NSAppUnitsToFloatPixels(aTransformedBounds.width, factor),
8521                     NSAppUnitsToFloatPixels(aTransformedBounds.height, factor));
8522 
8523   RectDouble childGfxBounds(
8524       NSAppUnitsToFloatPixels(aChildBounds.x, factor),
8525       NSAppUnitsToFloatPixels(aChildBounds.y, factor),
8526       NSAppUnitsToFloatPixels(aChildBounds.width, factor),
8527       NSAppUnitsToFloatPixels(aChildBounds.height, factor));
8528 
8529   result = transform.Inverse().ProjectRectBounds(result, childGfxBounds);
8530   *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
8531   return true;
8532 }
8533 
UntransformVisibleRect(nsDisplayListBuilder * aBuilder,nsRect * aOutRect) const8534 bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
8535                                                 nsRect* aOutRect) const {
8536   const Matrix4x4Flagged& matrix = GetTransform();
8537   if (matrix.IsSingular()) return false;
8538 
8539   // GetTransform always operates in dev pixels.
8540   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
8541   RectDouble result(NSAppUnitsToFloatPixels(mVisibleRect.x, factor),
8542                     NSAppUnitsToFloatPixels(mVisibleRect.y, factor),
8543                     NSAppUnitsToFloatPixels(mVisibleRect.width, factor),
8544                     NSAppUnitsToFloatPixels(mVisibleRect.height, factor));
8545 
8546   bool snap;
8547   nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
8548   RectDouble childGfxBounds(
8549       NSAppUnitsToFloatPixels(childBounds.x, factor),
8550       NSAppUnitsToFloatPixels(childBounds.y, factor),
8551       NSAppUnitsToFloatPixels(childBounds.width, factor),
8552       NSAppUnitsToFloatPixels(childBounds.height, factor));
8553 
8554   /* We want to untransform the matrix, so invert the transformation first! */
8555   result = matrix.Inverse().ProjectRectBounds(result, childGfxBounds);
8556 
8557   *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
8558 
8559   return true;
8560 }
8561 
WriteDebugInfo(std::stringstream & aStream)8562 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) {
8563   AppendToString(aStream, GetTransform().GetMatrix());
8564   if (IsTransformSeparator()) {
8565     aStream << " transform-separator";
8566   }
8567   if (IsLeafOf3DContext()) {
8568     aStream << " 3d-context-leaf";
8569   }
8570   if (mFrame->Extend3DContext()) {
8571     aStream << " extends-3d-context";
8572   }
8573   if (mFrame->Combines3DTransformWithAncestors()) {
8574     aStream << " combines-3d-with-ancestors";
8575   }
8576 }
8577 
nsDisplayPerspective(nsDisplayListBuilder * aBuilder,nsIFrame * aTransformFrame,nsIFrame * aPerspectiveFrame,nsDisplayList * aList)8578 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
8579                                            nsIFrame* aTransformFrame,
8580                                            nsIFrame* aPerspectiveFrame,
8581                                            nsDisplayList* aList)
8582     : nsDisplayItem(aBuilder, aPerspectiveFrame),
8583       mList(aBuilder, aPerspectiveFrame, aList),
8584       mTransformFrame(aTransformFrame),
8585       mIndex(aBuilder->PerspectiveItemIndex()) {
8586   MOZ_ASSERT(mList.GetChildren()->Count() == 1);
8587   MOZ_ASSERT(mList.GetChildren()->GetTop()->GetType() ==
8588              DisplayItemType::TYPE_TRANSFORM);
8589 
8590   if (aBuilder->IsRetainingDisplayList()) {
8591     mTransformFrame->AddDisplayItem(this);
8592   }
8593 }
8594 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)8595 already_AddRefed<Layer> nsDisplayPerspective::BuildLayer(
8596     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
8597     const ContainerLayerParameters& aContainerParameters) {
8598   float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8599 
8600   Matrix4x4 perspectiveMatrix;
8601   DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
8602       mTransformFrame, appUnitsPerPixel, perspectiveMatrix);
8603   MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
8604 
8605   /*
8606    * ClipListToRange can remove our child after we were created.
8607    */
8608   if (!mList.GetChildren()->GetTop()) {
8609     return nullptr;
8610   }
8611 
8612   /*
8613    * The resulting matrix is still in the coordinate space of the transformed
8614    * frame. Append a translation to the reference frame coordinates.
8615    */
8616   nsDisplayTransform* transform =
8617       static_cast<nsDisplayTransform*>(mList.GetChildren()->GetTop());
8618 
8619   Point3D newOrigin =
8620       Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
8621                                       appUnitsPerPixel),
8622               NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
8623                                       appUnitsPerPixel),
8624               0.0f);
8625   Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
8626 
8627   perspectiveMatrix.PostTranslate(roundedOrigin);
8628 
8629   RefPtr<ContainerLayer> container =
8630       aManager->GetLayerBuilder()->BuildContainerLayerFor(
8631           aBuilder, aManager, mFrame, this, mList.GetChildren(),
8632           aContainerParameters, &perspectiveMatrix, 0);
8633 
8634   if (!container) {
8635     return nullptr;
8636   }
8637 
8638   // Sort of a lie, but we want to pretend that the perspective layer extends a
8639   // 3d context so that it gets its transform combined with children. Might need
8640   // a better name that reflects this use case and isn't specific to
8641   // preserve-3d.
8642   container->SetContentFlags(container->GetContentFlags() |
8643                              Layer::CONTENT_EXTEND_3D_CONTEXT);
8644   container->SetTransformIsPerspective(true);
8645 
8646   return container.forget();
8647 }
8648 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)8649 LayerState nsDisplayPerspective::GetLayerState(
8650     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
8651     const ContainerLayerParameters& aParameters) {
8652   return LAYER_ACTIVE_FORCE;
8653 }
8654 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)8655 bool nsDisplayPerspective::CreateWebRenderCommands(
8656     mozilla::wr::DisplayListBuilder& aBuilder,
8657     mozilla::wr::IpcResourceUpdateQueue& aResources,
8658     const StackingContextHelper& aSc, WebRenderLayerManager* aManager,
8659     nsDisplayListBuilder* aDisplayListBuilder) {
8660   float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
8661   Matrix4x4 perspectiveMatrix;
8662   DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix(
8663       mTransformFrame, appUnitsPerPixel, perspectiveMatrix);
8664   MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
8665 
8666   /*
8667    * ClipListToRange can remove our child after we were created.
8668    */
8669   if (!mList.GetChildren()->GetTop()) {
8670     return false;
8671   }
8672 
8673   /*
8674    * The resulting matrix is still in the coordinate space of the transformed
8675    * frame. Append a translation to the reference frame coordinates.
8676    */
8677   nsDisplayTransform* transform =
8678       static_cast<nsDisplayTransform*>(mList.GetChildren()->GetTop());
8679 
8680   Point3D newOrigin =
8681       Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x,
8682                                       appUnitsPerPixel),
8683               NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y,
8684                                       appUnitsPerPixel),
8685               0.0f);
8686   Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
8687 
8688   gfx::Matrix4x4 transformForSC = gfx::Matrix4x4::Translation(roundedOrigin);
8689 
8690   nsTArray<mozilla::wr::WrFilterOp> filters;
8691   StackingContextHelper sc(aSc, aBuilder, filters, LayoutDeviceRect(), nullptr,
8692                            nullptr, nullptr, &transformForSC,
8693                            &perspectiveMatrix, gfx::CompositionOp::OP_OVER,
8694                            !BackfaceIsHidden(), true);
8695 
8696   return mList.CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
8697                                        aDisplayListBuilder);
8698 }
8699 
ZIndex() const8700 int32_t nsDisplayPerspective::ZIndex() const {
8701   return ZIndexForFrame(mTransformFrame);
8702 }
8703 
AllocateGeometry(nsDisplayListBuilder * aBuilder)8704 nsDisplayItemGeometry* nsCharClipDisplayItem::AllocateGeometry(
8705     nsDisplayListBuilder* aBuilder) {
8706   return new nsCharClipGeometry(this, aBuilder);
8707 }
8708 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const8709 void nsCharClipDisplayItem::ComputeInvalidationRegion(
8710     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
8711     nsRegion* aInvalidRegion) const {
8712   const nsCharClipGeometry* geometry =
8713       static_cast<const nsCharClipGeometry*>(aGeometry);
8714 
8715   bool snap;
8716   nsRect newRect = geometry->mBounds;
8717   nsRect oldRect = GetBounds(aBuilder, &snap);
8718   if (mVisIStartEdge != geometry->mVisIStartEdge ||
8719       mVisIEndEdge != geometry->mVisIEndEdge ||
8720       !oldRect.IsEqualInterior(newRect) ||
8721       !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
8722     aInvalidRegion->Or(oldRect, newRect);
8723   }
8724 }
8725 
nsDisplaySVGEffects(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,bool aHandleOpacity,const ActiveScrolledRoot * aActiveScrolledRoot,bool aClearClipChain)8726 nsDisplaySVGEffects::nsDisplaySVGEffects(
8727     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
8728     bool aHandleOpacity, const ActiveScrolledRoot* aActiveScrolledRoot,
8729     bool aClearClipChain)
8730     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot,
8731                         aClearClipChain),
8732       mHandleOpacity(aHandleOpacity) {
8733   MOZ_COUNT_CTOR(nsDisplaySVGEffects);
8734 }
8735 
nsDisplaySVGEffects(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,bool aHandleOpacity)8736 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
8737                                          nsIFrame* aFrame, nsDisplayList* aList,
8738                                          bool aHandleOpacity)
8739     : nsDisplayWrapList(aBuilder, aFrame, aList),
8740       mHandleOpacity(aHandleOpacity) {
8741   MOZ_COUNT_CTOR(nsDisplaySVGEffects);
8742 }
8743 
8744 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplaySVGEffects()8745 nsDisplaySVGEffects::~nsDisplaySVGEffects() {
8746   MOZ_COUNT_DTOR(nsDisplaySVGEffects);
8747 }
8748 #endif
8749 
GetOpaqueRegion(nsDisplayListBuilder * aBuilder,bool * aSnap) const8750 nsRegion nsDisplaySVGEffects::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
8751                                               bool* aSnap) const {
8752   *aSnap = false;
8753   return nsRegion();
8754 }
8755 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)8756 void nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder,
8757                                   const nsRect& aRect, HitTestState* aState,
8758                                   nsTArray<nsIFrame*>* aOutFrames) {
8759   nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
8760   if (nsSVGIntegrationUtils::HitTestFrameForEffects(
8761           mFrame, rectCenter - ToReferenceFrame())) {
8762     mList.HitTest(aBuilder, aRect, aState, aOutFrames);
8763   }
8764 }
8765 
BBoxInUserSpace() const8766 gfxRect nsDisplaySVGEffects::BBoxInUserSpace() const {
8767   return nsSVGUtils::GetBBox(mFrame);
8768 }
8769 
UserSpaceOffset() const8770 gfxPoint nsDisplaySVGEffects::UserSpaceOffset() const {
8771   return nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
8772 }
8773 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const8774 void nsDisplaySVGEffects::ComputeInvalidationRegion(
8775     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
8776     nsRegion* aInvalidRegion) const {
8777   const nsDisplaySVGEffectGeometry* geometry =
8778       static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
8779   bool snap;
8780   nsRect bounds = GetBounds(aBuilder, &snap);
8781   if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
8782       geometry->mUserSpaceOffset != UserSpaceOffset() ||
8783       !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
8784     // Filter and mask output can depend on the location of the frame's user
8785     // space and on the frame's BBox. We need to invalidate if either of these
8786     // change relative to the reference frame.
8787     // Invalidations from our inactive layer manager are not enough to catch
8788     // some of these cases because filters can produce output even if there's
8789     // nothing in the filter input.
8790     aInvalidRegion->Or(bounds, geometry->mBounds);
8791   }
8792 }
8793 
ValidateSVGFrame()8794 bool nsDisplaySVGEffects::ValidateSVGFrame() {
8795   const nsIContent* content = mFrame->GetContent();
8796   bool hasSVGLayout = (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
8797   if (hasSVGLayout) {
8798     nsSVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame);
8799     if (!svgFrame || !mFrame->GetContent()->IsSVGElement()) {
8800       NS_ASSERTION(false, "why?");
8801       return false;
8802     }
8803     if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
8804       return false;  // The SVG spec says not to draw filters for this
8805     }
8806   }
8807 
8808   return true;
8809 }
8810 
ComputeClipExtsInDeviceSpace(gfxContext & aCtx)8811 static IntRect ComputeClipExtsInDeviceSpace(gfxContext& aCtx) {
8812   // Get the clip extents in device space.
8813   gfxRect clippedFrameSurfaceRect =
8814       aCtx.GetClipExtents(gfxContext::eDeviceSpace);
8815   clippedFrameSurfaceRect.RoundOut();
8816 
8817   IntRect result;
8818   ToRect(clippedFrameSurfaceRect).ToIntRect(&result);
8819   return mozilla::gfx::Factory::CheckSurfaceSize(result.Size()) ? result
8820                                                                 : IntRect();
8821 }
8822 
8823 typedef nsSVGIntegrationUtils::PaintFramesParams PaintFramesParams;
8824 
ComputeMaskGeometry(PaintFramesParams & aParams)8825 static void ComputeMaskGeometry(PaintFramesParams& aParams) {
8826   // Properties are added lazily and may have been removed by a restyle, so
8827   // make sure all applicable ones are set again.
8828   nsIFrame* firstFrame =
8829       nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
8830 
8831   const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
8832 
8833   SVGObserverUtils::EffectProperties effectProperties =
8834       SVGObserverUtils::GetEffectProperties(firstFrame);
8835   nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
8836 
8837   if (maskFrames.Length() == 0) {
8838     return;
8839   }
8840 
8841   gfxContext& ctx = aParams.ctx;
8842   nsIFrame* frame = aParams.frame;
8843 
8844   nsPoint offsetToUserSpace =
8845       nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame);
8846 
8847   gfxPoint devPixelOffsetToUserSpace = nsLayoutUtils::PointToGfxPoint(
8848       offsetToUserSpace, frame->PresContext()->AppUnitsPerDevPixel());
8849 
8850   gfxContextMatrixAutoSaveRestore matSR(&ctx);
8851   ctx.SetMatrixDouble(
8852       ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace));
8853 
8854   // Convert boaderArea and dirtyRect to user space.
8855   int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
8856   nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
8857   nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
8858 
8859   // Union all mask layer rectangles in user space.
8860   gfxRect maskInUserSpace;
8861   for (size_t i = 0; i < maskFrames.Length(); i++) {
8862     nsSVGMaskFrame* maskFrame = maskFrames[i];
8863     gfxRect currentMaskSurfaceRect;
8864 
8865     if (maskFrame) {
8866       currentMaskSurfaceRect = maskFrame->GetMaskArea(aParams.frame);
8867     } else {
8868       nsCSSRendering::ImageLayerClipState clipState;
8869       nsCSSRendering::GetImageLayerClip(
8870           svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(),
8871           userSpaceBorderArea, userSpaceDirtyRect, false, /* aWillPaintBorder */
8872           appUnitsPerDevPixel, &clipState);
8873       currentMaskSurfaceRect = clipState.mDirtyRectInDevPx;
8874     }
8875 
8876     maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
8877   }
8878 
8879   gfxContextAutoSaveRestore autoSR;
8880 
8881   if (!maskInUserSpace.IsEmpty()) {
8882     autoSR.SetContext(&ctx);
8883     ctx.Clip(maskInUserSpace);
8884   }
8885 
8886   IntRect result = ComputeClipExtsInDeviceSpace(ctx);
8887   aParams.maskRect = result;
8888 }
8889 
nsDisplayMask(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,bool aHandleOpacity,const ActiveScrolledRoot * aActiveScrolledRoot)8890 nsDisplayMask::nsDisplayMask(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
8891                              nsDisplayList* aList, bool aHandleOpacity,
8892                              const ActiveScrolledRoot* aActiveScrolledRoot)
8893     : nsDisplaySVGEffects(aBuilder, aFrame, aList, aHandleOpacity,
8894                           aActiveScrolledRoot, true) {
8895   MOZ_COUNT_CTOR(nsDisplayMask);
8896 
8897   nsPresContext* presContext = mFrame->PresContext();
8898   uint32_t flags =
8899       aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE;
8900   const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
8901   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
8902     if (!svgReset->mMask.mLayers[i].mImage.IsResolved()) {
8903       continue;
8904     }
8905     bool isTransformedFixed;
8906     nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
8907         presContext, aFrame, flags, mFrame->GetRectRelativeToSelf(),
8908         mFrame->GetRectRelativeToSelf(), svgReset->mMask.mLayers[i],
8909         &isTransformedFixed);
8910     mDestRects.AppendElement(state.mDestArea);
8911   }
8912 }
8913 
8914 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayMask()8915 nsDisplayMask::~nsDisplayMask() { MOZ_COUNT_DTOR(nsDisplayMask); }
8916 #endif
8917 
CanMergeDisplayMaskFrame(nsIFrame * aFrame)8918 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) {
8919   // Do not merge items for box-decoration-break:clone elements,
8920   // since each box should have its own mask in that case.
8921   if (aFrame->StyleBorder()->mBoxDecorationBreak ==
8922       mozilla::StyleBoxDecorationBreak::Clone) {
8923     return false;
8924   }
8925 
8926   // Do not merge if either frame has a mask. Continuation frames should apply
8927   // the mask independently (just like nsDisplayBackgroundImage).
8928   if (aFrame->StyleSVGReset()->HasMask()) {
8929     return false;
8930   }
8931 
8932   return true;
8933 }
8934 
CanMerge(const nsDisplayItem * aItem) const8935 bool nsDisplayMask::CanMerge(const nsDisplayItem* aItem) const {
8936   // Items for the same content element should be merged into a single
8937   // compositing group.
8938   if (!HasSameTypeAndClip(aItem) || !HasSameContent(aItem)) {
8939     return false;
8940   }
8941 
8942   return CanMergeDisplayMaskFrame(mFrame) &&
8943          CanMergeDisplayMaskFrame(aItem->Frame());
8944 }
8945 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)8946 already_AddRefed<Layer> nsDisplayMask::BuildLayer(
8947     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
8948     const ContainerLayerParameters& aContainerParameters) {
8949   if (!ValidateSVGFrame()) {
8950     return nullptr;
8951   }
8952 
8953   if (mFrame->StyleEffects()->mOpacity == 0.0f && mHandleOpacity) {
8954     return nullptr;
8955   }
8956 
8957   nsIFrame* firstFrame =
8958       nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
8959   SVGObserverUtils::EffectProperties effectProperties =
8960       SVGObserverUtils::GetEffectProperties(firstFrame);
8961 
8962   if (effectProperties.HasInvalidClipPath() ||
8963       effectProperties.HasInvalidMask()) {
8964     return nullptr;
8965   }
8966 
8967   RefPtr<ContainerLayer> container =
8968       aManager->GetLayerBuilder()->BuildContainerLayerFor(
8969           aBuilder, aManager, mFrame, this, &mList, aContainerParameters,
8970           nullptr);
8971 
8972   return container.forget();
8973 }
8974 
PaintMask(nsDisplayListBuilder * aBuilder,gfxContext * aMaskContext)8975 bool nsDisplayMask::PaintMask(nsDisplayListBuilder* aBuilder,
8976                               gfxContext* aMaskContext) {
8977   MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
8978 
8979   imgDrawingParams imgParmas(aBuilder->ShouldSyncDecodeImages()
8980                                  ? imgIContainer::FLAG_SYNC_DECODE
8981                                  : imgIContainer::FLAG_SYNC_DECODE_IF_FAST);
8982   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
8983   nsSVGIntegrationUtils::PaintFramesParams params(
8984       *aMaskContext, mFrame, mVisibleRect, borderArea, aBuilder, nullptr,
8985       mHandleOpacity, imgParmas);
8986   ComputeMaskGeometry(params);
8987   nsSVGIntegrationUtils::PaintMask(params);
8988 
8989   nsDisplayMaskGeometry::UpdateDrawResult(this, imgParmas.result);
8990 
8991   return imgParmas.result == mozilla::image::ImgDrawResult::SUCCESS;
8992 }
8993 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)8994 LayerState nsDisplayMask::GetLayerState(
8995     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
8996     const ContainerLayerParameters& aParameters) {
8997   if (CanPaintOnMaskLayer(aManager)) {
8998     LayerState result = RequiredLayerStateForChildren(
8999         aBuilder, aManager, aParameters, mList, GetAnimatedGeometryRoot());
9000     // When we're not active, FrameLayerBuilder will call PaintAsLayer()
9001     // on us during painting. In that case we don't want a mask layer to
9002     // be created, because PaintAsLayer() takes care of applying the mask.
9003     // So we return LAYER_SVG_EFFECTS instead of LAYER_INACTIVE so that
9004     // FrameLayerBuilder doesn't set a mask layer on our layer.
9005     return result == LAYER_INACTIVE ? LAYER_SVG_EFFECTS : result;
9006   }
9007 
9008   return LAYER_SVG_EFFECTS;
9009 }
9010 
CanPaintOnMaskLayer(LayerManager * aManager)9011 bool nsDisplayMask::CanPaintOnMaskLayer(LayerManager* aManager) {
9012   if (!nsSVGIntegrationUtils::IsMaskResourceReady(mFrame)) {
9013     return false;
9014   }
9015 
9016   if (gfxPrefs::DrawMaskLayer()) {
9017     return false;
9018   }
9019 
9020   return true;
9021 }
9022 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)9023 bool nsDisplayMask::ComputeVisibility(nsDisplayListBuilder* aBuilder,
9024                                       nsRegion* aVisibleRegion) {
9025   // Our children may be made translucent or arbitrarily deformed so we should
9026   // not allow them to subtract area from aVisibleRegion.
9027   nsRegion childrenVisible(mVisibleRect);
9028   nsRect r = mVisibleRect.Intersect(mList.GetBounds(aBuilder));
9029   mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r);
9030   return true;
9031 }
9032 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const9033 void nsDisplayMask::ComputeInvalidationRegion(
9034     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
9035     nsRegion* aInvalidRegion) const {
9036   nsDisplaySVGEffects::ComputeInvalidationRegion(aBuilder, aGeometry,
9037                                                  aInvalidRegion);
9038 
9039   const nsDisplayMaskGeometry* geometry =
9040       static_cast<const nsDisplayMaskGeometry*>(aGeometry);
9041   bool snap;
9042   nsRect bounds = GetBounds(aBuilder, &snap);
9043 
9044   if (mFrame->StyleEffects()->mOpacity != geometry->mOpacity ||
9045       mHandleOpacity != geometry->mHandleOpacity) {
9046     aInvalidRegion->Or(*aInvalidRegion, bounds);
9047   }
9048 
9049   if (mDestRects.Length() != geometry->mDestRects.Length()) {
9050     aInvalidRegion->Or(bounds, geometry->mBounds);
9051   } else {
9052     for (size_t i = 0; i < mDestRects.Length(); i++) {
9053       if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) {
9054         aInvalidRegion->Or(bounds, geometry->mBounds);
9055         break;
9056       }
9057     }
9058   }
9059 
9060   if (aBuilder->ShouldSyncDecodeImages() &&
9061       geometry->ShouldInvalidateToSyncDecodeImages()) {
9062     const nsStyleSVGReset* svgReset = mFrame->StyleSVGReset();
9063     NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) {
9064       const nsStyleImage& image = svgReset->mMask.mLayers[i].mImage;
9065       if (image.GetType() == eStyleImageType_Image) {
9066         aInvalidRegion->Or(*aInvalidRegion, bounds);
9067         break;
9068       }
9069     }
9070   }
9071 }
9072 
PaintAsLayer(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,LayerManager * aManager)9073 void nsDisplayMask::PaintAsLayer(nsDisplayListBuilder* aBuilder,
9074                                  gfxContext* aCtx, LayerManager* aManager) {
9075   // Clip the drawing target by mVisibleRect, which contains the visible
9076   // region of the target frame and its out-of-flow and inflow descendants.
9077   gfxContext* context = aCtx;
9078 
9079   Rect bounds =
9080       NSRectToRect(mVisibleRect, mFrame->PresContext()->AppUnitsPerDevPixel());
9081   bounds.RoundOut();
9082   context->Clip(bounds);
9083 
9084   imgDrawingParams imgParams(aBuilder->ShouldSyncDecodeImages()
9085                                  ? imgIContainer::FLAG_SYNC_DECODE
9086                                  : imgIContainer::FLAG_SYNC_DECODE_IF_FAST);
9087   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
9088   nsSVGIntegrationUtils::PaintFramesParams params(
9089       *aCtx, mFrame, mVisibleRect, borderArea, aBuilder, aManager,
9090       mHandleOpacity, imgParams);
9091 
9092   ComputeMaskGeometry(params);
9093 
9094   nsSVGIntegrationUtils::PaintMaskAndClipPath(params);
9095 
9096   context->PopClip();
9097 
9098   nsDisplayMaskGeometry::UpdateDrawResult(this, imgParams.result);
9099 }
9100 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)9101 bool nsDisplayMask::CreateWebRenderCommands(
9102     mozilla::wr::DisplayListBuilder& aBuilder,
9103     mozilla::wr::IpcResourceUpdateQueue& aResources,
9104     const StackingContextHelper& aSc,
9105     mozilla::layers::WebRenderLayerManager* aManager,
9106     nsDisplayListBuilder* aDisplayListBuilder) {
9107   bool snap;
9108   float appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
9109   nsRect displayBound = GetBounds(aDisplayListBuilder, &snap);
9110   LayoutDeviceRect bounds =
9111       LayoutDeviceRect::FromAppUnits(displayBound, appUnitsPerDevPixel);
9112 
9113   Maybe<wr::WrImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(
9114       this, aBuilder, aResources, aSc, aDisplayListBuilder, bounds);
9115   if (mask) {
9116     wr::WrClipId clipId = aBuilder.DefineClip(Nothing(), Nothing(),
9117                                               aSc.ToRelativeLayoutRect(bounds),
9118                                               nullptr, mask.ptr());
9119     // Don't record this clip push in aBuilder's internal clip stack, because
9120     // otherwise any nested ScrollingLayersHelper instances that are created
9121     // will get confused about which clips are pushed.
9122     aBuilder.PushClip(clipId, GetClipChain());
9123   }
9124 
9125   nsDisplaySVGEffects::CreateWebRenderCommands(aBuilder, aResources, aSc,
9126                                                aManager, aDisplayListBuilder);
9127 
9128   if (mask) {
9129     aBuilder.PopClip(GetClipChain());
9130   }
9131 
9132   return true;
9133 }
9134 
GetClipWithRespectToASR(nsDisplayListBuilder * aBuilder,const ActiveScrolledRoot * aASR) const9135 Maybe<nsRect> nsDisplayMask::GetClipWithRespectToASR(
9136     nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const {
9137   if (const DisplayItemClip* clip =
9138           DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
9139     return Some(clip->GetClipRect());
9140   }
9141   // This item does not have a clip with respect to |aASR|. However, we
9142   // might still have finite bounds with respect to |aASR|. Check our
9143   // children.
9144   nsDisplayList* childList = GetSameCoordinateSystemChildren();
9145   if (childList) {
9146     return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR));
9147   }
9148 #ifdef DEBUG
9149   if (!gfxPrefs::LayoutUseContainersForRootFrames()) {
9150     MOZ_ASSERT(false, "item should have finite clip with respect to aASR");
9151   }
9152 #endif
9153   return Nothing();
9154 }
9155 
9156 #ifdef MOZ_DUMP_PAINTING
PrintEffects(nsACString & aTo)9157 void nsDisplayMask::PrintEffects(nsACString& aTo) {
9158   nsIFrame* firstFrame =
9159       nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
9160   SVGObserverUtils::EffectProperties effectProperties =
9161       SVGObserverUtils::GetEffectProperties(firstFrame);
9162   nsSVGClipPathFrame* clipPathFrame = effectProperties.GetClipPathFrame();
9163   bool first = true;
9164   aTo += " effects=(";
9165   if (mFrame->StyleEffects()->mOpacity != 1.0f && mHandleOpacity) {
9166     first = false;
9167     aTo += nsPrintfCString("opacity(%f)", mFrame->StyleEffects()->mOpacity);
9168   }
9169   if (clipPathFrame) {
9170     if (!first) {
9171       aTo += ", ";
9172     }
9173     aTo += nsPrintfCString(
9174         "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
9175     first = false;
9176   }
9177   const nsStyleSVGReset* style = mFrame->StyleSVGReset();
9178   if (style->HasClipPath() && !clipPathFrame) {
9179     if (!first) {
9180       aTo += ", ";
9181     }
9182     aTo += "clip(basic-shape)";
9183     first = false;
9184   }
9185 
9186   nsTArray<nsSVGMaskFrame*> masks = effectProperties.GetMaskFrames();
9187   if (!masks.IsEmpty() && masks[0]) {
9188     if (!first) {
9189       aTo += ", ";
9190     }
9191     aTo += "mask";
9192   }
9193   aTo += ")";
9194 }
9195 #endif
9196 
nsDisplayFilter(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,bool aHandleOpacity)9197 nsDisplayFilter::nsDisplayFilter(nsDisplayListBuilder* aBuilder,
9198                                  nsIFrame* aFrame, nsDisplayList* aList,
9199                                  bool aHandleOpacity)
9200     : nsDisplaySVGEffects(aBuilder, aFrame, aList, aHandleOpacity),
9201       mEffectsBounds(aFrame->GetVisualOverflowRectRelativeToSelf()) {
9202   MOZ_COUNT_CTOR(nsDisplayFilter);
9203 }
9204 
9205 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayFilter()9206 nsDisplayFilter::~nsDisplayFilter() { MOZ_COUNT_DTOR(nsDisplayFilter); }
9207 #endif
9208 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)9209 already_AddRefed<Layer> nsDisplayFilter::BuildLayer(
9210     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
9211     const ContainerLayerParameters& aContainerParameters) {
9212   if (!ValidateSVGFrame()) {
9213     return nullptr;
9214   }
9215 
9216   if (mFrame->StyleEffects()->mOpacity == 0.0f && mHandleOpacity) {
9217     return nullptr;
9218   }
9219 
9220   nsIFrame* firstFrame =
9221       nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
9222   SVGObserverUtils::EffectProperties effectProperties =
9223       SVGObserverUtils::GetEffectProperties(firstFrame);
9224 
9225   if (effectProperties.HasInvalidFilter()) {
9226     return nullptr;
9227   }
9228 
9229   MOZ_ASSERT(effectProperties.mFilter && mFrame->StyleEffects()->HasFilters(),
9230              "By getting here, we must have valid CSS filters.");
9231 
9232   ContainerLayerParameters newContainerParameters = aContainerParameters;
9233   newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
9234 
9235   RefPtr<ContainerLayer> container =
9236       aManager->GetLayerBuilder()->BuildContainerLayerFor(
9237           aBuilder, aManager, mFrame, this, &mList, newContainerParameters,
9238           nullptr);
9239   return container.forget();
9240 }
9241 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)9242 LayerState nsDisplayFilter::GetLayerState(
9243     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
9244     const ContainerLayerParameters& aParameters) {
9245   return LAYER_SVG_EFFECTS;
9246 }
9247 
ComputeVisibility(nsDisplayListBuilder * aBuilder,nsRegion * aVisibleRegion)9248 bool nsDisplayFilter::ComputeVisibility(nsDisplayListBuilder* aBuilder,
9249                                         nsRegion* aVisibleRegion) {
9250   nsPoint offset = ToReferenceFrame();
9251   nsRect dirtyRect = nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(
9252                          mFrame, mVisibleRect - offset) +
9253                      offset;
9254 
9255   // Our children may be made translucent or arbitrarily deformed so we should
9256   // not allow them to subtract area from aVisibleRegion.
9257   nsRegion childrenVisible(dirtyRect);
9258   nsRect r = dirtyRect.Intersect(
9259       mList.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot));
9260   mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r);
9261   return true;
9262 }
9263 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const9264 void nsDisplayFilter::ComputeInvalidationRegion(
9265     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
9266     nsRegion* aInvalidRegion) const {
9267   nsDisplaySVGEffects::ComputeInvalidationRegion(aBuilder, aGeometry,
9268                                                  aInvalidRegion);
9269 
9270   const nsDisplayFilterGeometry* geometry =
9271       static_cast<const nsDisplayFilterGeometry*>(aGeometry);
9272 
9273   if (aBuilder->ShouldSyncDecodeImages() &&
9274       geometry->ShouldInvalidateToSyncDecodeImages()) {
9275     bool snap;
9276     nsRect bounds = GetBounds(aBuilder, &snap);
9277     aInvalidRegion->Or(*aInvalidRegion, bounds);
9278   }
9279 }
9280 
PaintAsLayer(nsDisplayListBuilder * aBuilder,gfxContext * aCtx,LayerManager * aManager)9281 void nsDisplayFilter::PaintAsLayer(nsDisplayListBuilder* aBuilder,
9282                                    gfxContext* aCtx, LayerManager* aManager) {
9283   imgDrawingParams imgParams(aBuilder->ShouldSyncDecodeImages()
9284                                  ? imgIContainer::FLAG_SYNC_DECODE
9285                                  : imgIContainer::FLAG_SYNC_DECODE_IF_FAST);
9286   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
9287   nsSVGIntegrationUtils::PaintFramesParams params(
9288       *aCtx, mFrame, mVisibleRect, borderArea, aBuilder, aManager,
9289       mHandleOpacity, imgParams);
9290   nsSVGIntegrationUtils::PaintFilter(params);
9291   nsDisplayFilterGeometry::UpdateDrawResult(this, imgParams.result);
9292 }
9293 
ClampStdDeviation(float aStdDeviation)9294 static float ClampStdDeviation(float aStdDeviation) {
9295   // Cap software blur radius for performance reasons.
9296   return std::min(std::max(0.0f, aStdDeviation), 100.0f);
9297 }
9298 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)9299 bool nsDisplayFilter::CreateWebRenderCommands(
9300     mozilla::wr::DisplayListBuilder& aBuilder,
9301     mozilla::wr::IpcResourceUpdateQueue& aResources,
9302     const StackingContextHelper& aSc,
9303     mozilla::layers::WebRenderLayerManager* aManager,
9304     nsDisplayListBuilder* aDisplayListBuilder) {
9305   if (mFrame->IsFrameOfType(nsIFrame::eSVG)) {
9306     return false;
9307   }
9308 
9309   // Due to differences in the way that WebRender filters operate
9310   // only the brightness and contrast filters use that path. We
9311   // can gradually enable more filters as WebRender bugs are fixed.
9312   nsTArray<mozilla::wr::WrFilterOp> wrFilters;
9313   const nsTArray<nsStyleFilter>& filters = mFrame->StyleEffects()->mFilters;
9314   for (const nsStyleFilter& filter : filters) {
9315     switch (filter.GetType()) {
9316       case NS_STYLE_FILTER_BRIGHTNESS:
9317       case NS_STYLE_FILTER_CONTRAST:
9318       case NS_STYLE_FILTER_GRAYSCALE:
9319       case NS_STYLE_FILTER_INVERT:
9320       case NS_STYLE_FILTER_OPACITY:
9321       case NS_STYLE_FILTER_SATURATE:
9322       case NS_STYLE_FILTER_SEPIA: {
9323         mozilla::wr::WrFilterOp filterOp = {
9324             wr::ToWrFilterOpType(filter.GetType()),
9325             filter.GetFilterParameter().GetFactorOrPercentValue(),
9326         };
9327         wrFilters.AppendElement(filterOp);
9328         break;
9329       }
9330       case NS_STYLE_FILTER_HUE_ROTATE: {
9331         mozilla::wr::WrFilterOp filterOp = {
9332             wr::ToWrFilterOpType(filter.GetType()),
9333             (float)filter.GetFilterParameter().GetAngleValueInDegrees(),
9334         };
9335         wrFilters.AppendElement(filterOp);
9336         break;
9337       }
9338       case NS_STYLE_FILTER_BLUR: {
9339         float appUnitsPerDevPixel =
9340             mFrame->PresContext()->AppUnitsPerDevPixel();
9341         mozilla::wr::WrFilterOp filterOp = {
9342             wr::ToWrFilterOpType(filter.GetType()),
9343             ClampStdDeviation(NSAppUnitsToFloatPixels(
9344                 filter.GetFilterParameter().GetCoordValue(),
9345                 appUnitsPerDevPixel)),
9346         };
9347         wrFilters.AppendElement(filterOp);
9348         break;
9349       }
9350       case NS_STYLE_FILTER_DROP_SHADOW: {
9351         float appUnitsPerDevPixel =
9352             mFrame->PresContext()->AppUnitsPerDevPixel();
9353         nsCSSShadowArray* shadows = filter.GetDropShadow();
9354         if (!shadows || shadows->Length() != 1) {
9355           NS_NOTREACHED("Exactly one drop shadow should have been parsed.");
9356           return false;
9357         }
9358 
9359         nsCSSShadowItem* shadow = shadows->ShadowAt(0);
9360         nscolor color = shadow->mColor;
9361         if (!shadow->mHasColor) {
9362           color = mFrame->StyleColor()->mColor;
9363         }
9364 
9365         mozilla::wr::WrFilterOp filterOp = {
9366             wr::ToWrFilterOpType(filter.GetType()),
9367             NSAppUnitsToFloatPixels(shadow->mRadius, appUnitsPerDevPixel),
9368             {
9369                 NSAppUnitsToFloatPixels(shadow->mXOffset, appUnitsPerDevPixel),
9370                 NSAppUnitsToFloatPixels(shadow->mYOffset, appUnitsPerDevPixel),
9371             },
9372             {
9373                 NS_GET_R(color) / 255.0f,
9374                 NS_GET_G(color) / 255.0f,
9375                 NS_GET_B(color) / 255.0f,
9376                 NS_GET_A(color) / 255.0f,
9377             }};
9378 
9379         wrFilters.AppendElement(filterOp);
9380         break;
9381       }
9382       default:
9383         return false;
9384     }
9385   }
9386 
9387   float opacity = mFrame->StyleEffects()->mOpacity;
9388   StackingContextHelper sc(
9389       aSc, aBuilder, wrFilters, LayoutDeviceRect(), nullptr, nullptr,
9390       opacity != 1.0f && mHandleOpacity ? &opacity : nullptr);
9391 
9392   nsDisplaySVGEffects::CreateWebRenderCommands(aBuilder, aResources, sc,
9393                                                aManager, aDisplayListBuilder);
9394   return true;
9395 }
9396 
9397 #ifdef MOZ_DUMP_PAINTING
PrintEffects(nsACString & aTo)9398 void nsDisplayFilter::PrintEffects(nsACString& aTo) {
9399   nsIFrame* firstFrame =
9400       nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
9401   SVGObserverUtils::EffectProperties effectProperties =
9402       SVGObserverUtils::GetEffectProperties(firstFrame);
9403   bool first = true;
9404   aTo += " effects=(";
9405   if (mFrame->StyleEffects()->mOpacity != 1.0f && mHandleOpacity) {
9406     first = false;
9407     aTo += nsPrintfCString("opacity(%f)", mFrame->StyleEffects()->mOpacity);
9408   }
9409   if (effectProperties.HasValidFilter()) {
9410     if (!first) {
9411       aTo += ", ";
9412     }
9413     aTo += "filter";
9414   }
9415   aTo += ")";
9416 }
9417 #endif
9418 
nsDisplaySVGWrapper(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList)9419 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder,
9420                                          nsIFrame* aFrame, nsDisplayList* aList)
9421     : nsDisplayWrapList(aBuilder, aFrame, aList) {
9422   MOZ_COUNT_CTOR(nsDisplaySVGWrapper);
9423 }
9424 
9425 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplaySVGWrapper()9426 nsDisplaySVGWrapper::~nsDisplaySVGWrapper() {
9427   MOZ_COUNT_DTOR(nsDisplaySVGWrapper);
9428 }
9429 #endif
9430 
GetLayerState(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aParameters)9431 LayerState nsDisplaySVGWrapper::GetLayerState(
9432     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
9433     const ContainerLayerParameters& aParameters) {
9434   RefPtr<LayerManager> layerManager = aBuilder->GetWidgetLayerManager();
9435   if (layerManager &&
9436       layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
9437     return LAYER_ACTIVE_FORCE;
9438   }
9439   return LAYER_NONE;
9440 }
9441 
ShouldFlattenAway(nsDisplayListBuilder * aBuilder)9442 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
9443   RefPtr<LayerManager> layerManager = aBuilder->GetWidgetLayerManager();
9444   if (layerManager &&
9445       layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
9446     return false;
9447   }
9448   return true;
9449 }
9450 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)9451 already_AddRefed<Layer> nsDisplaySVGWrapper::BuildLayer(
9452     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
9453     const ContainerLayerParameters& aContainerParameters) {
9454   ContainerLayerParameters newContainerParameters = aContainerParameters;
9455   newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
9456 
9457   RefPtr<ContainerLayer> container =
9458       aManager->GetLayerBuilder()->BuildContainerLayerFor(
9459           aBuilder, aManager, mFrame, this, &mList, newContainerParameters,
9460           nullptr);
9461 
9462   return container.forget();
9463 }
9464 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)9465 bool nsDisplaySVGWrapper::CreateWebRenderCommands(
9466     mozilla::wr::DisplayListBuilder& aBuilder,
9467     mozilla::wr::IpcResourceUpdateQueue& aResources,
9468     const StackingContextHelper& aSc,
9469     mozilla::layers::WebRenderLayerManager* aManager,
9470     nsDisplayListBuilder* aDisplayListBuilder) {
9471   return false;
9472 }
9473 
9474 namespace mozilla {
9475 
9476 uint32_t PaintTelemetry::sPaintLevel = 0;
9477 uint32_t PaintTelemetry::sMetricLevel = 0;
9478 EnumeratedArray<PaintTelemetry::Metric, PaintTelemetry::Metric::COUNT, double>
9479     PaintTelemetry::sMetrics;
9480 
AutoRecordPaint()9481 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() {
9482   // Don't record nested paints.
9483   if (sPaintLevel++ > 0) {
9484     return;
9485   }
9486 
9487   // Reset metrics for a new paint.
9488   for (auto& metric : sMetrics) {
9489     metric = 0.0;
9490   }
9491   mStart = TimeStamp::Now();
9492 }
9493 
~AutoRecordPaint()9494 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() {
9495   MOZ_ASSERT(sPaintLevel != 0);
9496   if (--sPaintLevel > 0) {
9497     return;
9498   }
9499 
9500   // If we're in multi-process mode, don't include paint times for the parent
9501   // process.
9502   if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) {
9503     return;
9504   }
9505 
9506   double totalMs = (TimeStamp::Now() - mStart).ToMilliseconds();
9507 
9508   // Record the total time.
9509   Telemetry::Accumulate(Telemetry::CONTENT_PAINT_TIME,
9510                         static_cast<uint32_t>(totalMs));
9511 
9512   // Helpers for recording large/small paints.
9513   auto recordLarge = [=](const nsCString& aKey, double aDurationMs) -> void {
9514     MOZ_ASSERT(aDurationMs <= totalMs);
9515     uint32_t amount = static_cast<int32_t>((aDurationMs / totalMs) * 100.0);
9516     Telemetry::Accumulate(Telemetry::CONTENT_LARGE_PAINT_PHASE_WEIGHT, aKey,
9517                           amount);
9518   };
9519   auto recordSmall = [=](const nsString& aKey, double aDurationMs) -> void {
9520     MOZ_ASSERT(aDurationMs <= totalMs);
9521     uint32_t amount = static_cast<int32_t>((aDurationMs / totalMs) * 100.0);
9522     Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_SMALL_PAINT_PHASE_WEIGHT,
9523                          aKey, amount);
9524   };
9525 
9526   double dlMs = sMetrics[Metric::DisplayList];
9527   double flbMs = sMetrics[Metric::Layerization];
9528   double rMs = sMetrics[Metric::Rasterization];
9529 
9530   // If the total time was >= 16ms, then it's likely we missed a frame due to
9531   // painting. We bucket these metrics separately.
9532   if (totalMs >= 16.0) {
9533     recordLarge(NS_LITERAL_CSTRING("dl"), dlMs);
9534     recordLarge(NS_LITERAL_CSTRING("flb"), flbMs);
9535     recordLarge(NS_LITERAL_CSTRING("r"), rMs);
9536   } else {
9537     recordSmall(NS_LITERAL_STRING("dl"), dlMs);
9538     recordSmall(NS_LITERAL_STRING("flb"), flbMs);
9539     recordSmall(NS_LITERAL_STRING("r"), rMs);
9540   }
9541 }
9542 
AutoRecord(Metric aMetric)9543 PaintTelemetry::AutoRecord::AutoRecord(Metric aMetric) : mMetric(aMetric) {
9544   // Don't double-record anything nested.
9545   if (sMetricLevel++ > 0) {
9546     return;
9547   }
9548 
9549   // Don't record inside nested paints, or outside of paints.
9550   if (sPaintLevel != 1) {
9551     return;
9552   }
9553 
9554   mStart = TimeStamp::Now();
9555 }
9556 
~AutoRecord()9557 PaintTelemetry::AutoRecord::~AutoRecord() {
9558   MOZ_ASSERT(sMetricLevel != 0);
9559 
9560   sMetricLevel--;
9561   if (mStart.IsNull()) {
9562     return;
9563   }
9564 
9565   sMetrics[mMetric] += (TimeStamp::Now() - mStart).ToMilliseconds();
9566 }
9567 
9568 }  // namespace mozilla
9569