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