Bumping manifests a=b2g-bump
[gecko.git] / layout / base / nsDisplayList.cpp
blob1b38c719094fec6e7bd65f34b80bfa013e28e8cd
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=2 sw=2 et tw=78:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 */
8 /*
9 * structures that represent things to be painted (ordered in z-order),
10 * used during painting and hit testing
13 #include "nsDisplayList.h"
15 #include <stdint.h>
16 #include <algorithm>
18 #include "gfxUtils.h"
19 #include "mozilla/dom/TabChild.h"
20 #include "mozilla/gfx/2D.h"
21 #include "mozilla/layers/PLayerTransaction.h"
22 #include "nsCSSRendering.h"
23 #include "nsRenderingContext.h"
24 #include "nsISelectionController.h"
25 #include "nsIPresShell.h"
26 #include "nsRegion.h"
27 #include "nsStyleStructInlines.h"
28 #include "nsStyleTransformMatrix.h"
29 #include "gfxMatrix.h"
30 #include "gfxPrefs.h"
31 #include "nsSVGIntegrationUtils.h"
32 #include "nsSVGUtils.h"
33 #include "nsLayoutUtils.h"
34 #include "nsIScrollableFrame.h"
35 #include "nsIFrameInlines.h"
36 #include "nsThemeConstants.h"
37 #include "LayerTreeInvalidation.h"
39 #include "imgIContainer.h"
40 #include "BasicLayers.h"
41 #include "nsBoxFrame.h"
42 #include "nsViewportFrame.h"
43 #include "nsSubDocumentFrame.h"
44 #include "nsSVGEffects.h"
45 #include "nsSVGElement.h"
46 #include "nsSVGClipPathFrame.h"
47 #include "GeckoProfiler.h"
48 #include "nsAnimationManager.h"
49 #include "nsTransitionManager.h"
50 #include "nsViewManager.h"
51 #include "ImageLayers.h"
52 #include "ImageContainer.h"
53 #include "nsCanvasFrame.h"
54 #include "StickyScrollContainer.h"
55 #include "mozilla/EventStates.h"
56 #include "mozilla/LookAndFeel.h"
57 #include "mozilla/PendingPlayerTracker.h"
58 #include "mozilla/Preferences.h"
59 #include "mozilla/UniquePtr.h"
60 #include "ActiveLayerTracker.h"
61 #include "nsContentUtils.h"
62 #include "nsPrintfCString.h"
63 #include "UnitTransforms.h"
64 #include "LayersLogging.h"
65 #include "FrameLayerBuilder.h"
66 #include "RestyleManager.h"
67 #include "nsCaret.h"
68 #include "nsISelection.h"
70 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
71 // GetTickCount().
72 #ifdef GetCurrentTime
73 #undef GetCurrentTime
74 #endif
76 using namespace mozilla;
77 using namespace mozilla::layers;
78 using namespace mozilla::dom;
79 using namespace mozilla::image;
80 using namespace mozilla::layout;
81 using namespace mozilla::gfx;
83 typedef FrameMetrics::ViewID ViewID;
85 #ifdef DEBUG
86 static bool
87 SpammyLayoutWarningsEnabled()
89 static bool sValue = false;
90 static bool sValueInitialized = false;
92 if (!sValueInitialized) {
93 Preferences::GetBool("layout.spammy_warnings.enabled", &sValue);
94 sValueInitialized = true;
97 return sValue;
99 #endif
101 static inline nsIFrame*
102 GetTransformRootFrame(nsIFrame* aFrame)
104 return nsLayoutUtils::GetTransformRootFrame(aFrame);
107 static void AddTransformFunctions(nsCSSValueList* aList,
108 nsStyleContext* aContext,
109 nsPresContext* aPresContext,
110 nsRect& aBounds,
111 InfallibleTArray<TransformFunction>& aFunctions)
113 if (aList->mValue.GetUnit() == eCSSUnit_None) {
114 return;
117 for (const nsCSSValueList* curr = aList; curr; curr = curr->mNext) {
118 const nsCSSValue& currElem = curr->mValue;
119 NS_ASSERTION(currElem.GetUnit() == eCSSUnit_Function,
120 "Stream should consist solely of functions!");
121 nsCSSValue::Array* array = currElem.GetArrayValue();
122 bool canStoreInRuleTree = true;
123 switch (nsStyleTransformMatrix::TransformFunctionOf(array)) {
124 case eCSSKeyword_rotatex:
126 double theta = array->Item(1).GetAngleValueInRadians();
127 aFunctions.AppendElement(RotationX(theta));
128 break;
130 case eCSSKeyword_rotatey:
132 double theta = array->Item(1).GetAngleValueInRadians();
133 aFunctions.AppendElement(RotationY(theta));
134 break;
136 case eCSSKeyword_rotatez:
138 double theta = array->Item(1).GetAngleValueInRadians();
139 aFunctions.AppendElement(RotationZ(theta));
140 break;
142 case eCSSKeyword_rotate:
144 double theta = array->Item(1).GetAngleValueInRadians();
145 aFunctions.AppendElement(Rotation(theta));
146 break;
148 case eCSSKeyword_rotate3d:
150 double x = array->Item(1).GetFloatValue();
151 double y = array->Item(2).GetFloatValue();
152 double z = array->Item(3).GetFloatValue();
153 double theta = array->Item(4).GetAngleValueInRadians();
154 aFunctions.AppendElement(Rotation3D(x, y, z, theta));
155 break;
157 case eCSSKeyword_scalex:
159 double x = array->Item(1).GetFloatValue();
160 aFunctions.AppendElement(Scale(x, 1, 1));
161 break;
163 case eCSSKeyword_scaley:
165 double y = array->Item(1).GetFloatValue();
166 aFunctions.AppendElement(Scale(1, y, 1));
167 break;
169 case eCSSKeyword_scalez:
171 double z = array->Item(1).GetFloatValue();
172 aFunctions.AppendElement(Scale(1, 1, z));
173 break;
175 case eCSSKeyword_scale:
177 double x = array->Item(1).GetFloatValue();
178 // scale(x) is shorthand for scale(x, x);
179 double y = array->Count() == 2 ? x : array->Item(2).GetFloatValue();
180 aFunctions.AppendElement(Scale(x, y, 1));
181 break;
183 case eCSSKeyword_scale3d:
185 double x = array->Item(1).GetFloatValue();
186 double y = array->Item(2).GetFloatValue();
187 double z = array->Item(3).GetFloatValue();
188 aFunctions.AppendElement(Scale(x, y, z));
189 break;
191 case eCSSKeyword_translatex:
193 double x = nsStyleTransformMatrix::ProcessTranslatePart(
194 array->Item(1), aContext, aPresContext, canStoreInRuleTree,
195 aBounds.Width());
196 aFunctions.AppendElement(Translation(x, 0, 0));
197 break;
199 case eCSSKeyword_translatey:
201 double y = nsStyleTransformMatrix::ProcessTranslatePart(
202 array->Item(1), aContext, aPresContext, canStoreInRuleTree,
203 aBounds.Height());
204 aFunctions.AppendElement(Translation(0, y, 0));
205 break;
207 case eCSSKeyword_translatez:
209 double z = nsStyleTransformMatrix::ProcessTranslatePart(
210 array->Item(1), aContext, aPresContext, canStoreInRuleTree,
212 aFunctions.AppendElement(Translation(0, 0, z));
213 break;
215 case eCSSKeyword_translate:
217 double x = nsStyleTransformMatrix::ProcessTranslatePart(
218 array->Item(1), aContext, aPresContext, canStoreInRuleTree,
219 aBounds.Width());
220 // translate(x) is shorthand for translate(x, 0)
221 double y = 0;
222 if (array->Count() == 3) {
223 y = nsStyleTransformMatrix::ProcessTranslatePart(
224 array->Item(2), aContext, aPresContext, canStoreInRuleTree,
225 aBounds.Height());
227 aFunctions.AppendElement(Translation(x, y, 0));
228 break;
230 case eCSSKeyword_translate3d:
232 double x = nsStyleTransformMatrix::ProcessTranslatePart(
233 array->Item(1), aContext, aPresContext, canStoreInRuleTree,
234 aBounds.Width());
235 double y = nsStyleTransformMatrix::ProcessTranslatePart(
236 array->Item(2), aContext, aPresContext, canStoreInRuleTree,
237 aBounds.Height());
238 double z = nsStyleTransformMatrix::ProcessTranslatePart(
239 array->Item(3), aContext, aPresContext, canStoreInRuleTree,
242 aFunctions.AppendElement(Translation(x, y, z));
243 break;
245 case eCSSKeyword_skewx:
247 double x = array->Item(1).GetAngleValueInRadians();
248 aFunctions.AppendElement(SkewX(x));
249 break;
251 case eCSSKeyword_skewy:
253 double y = array->Item(1).GetAngleValueInRadians();
254 aFunctions.AppendElement(SkewY(y));
255 break;
257 case eCSSKeyword_skew:
259 double x = array->Item(1).GetAngleValueInRadians();
260 // skew(x) is shorthand for skew(x, 0)
261 double y = 0;
262 if (array->Count() == 3) {
263 y = array->Item(2).GetAngleValueInRadians();
265 aFunctions.AppendElement(Skew(x, y));
266 break;
268 case eCSSKeyword_matrix:
270 gfx::Matrix4x4 matrix;
271 matrix._11 = array->Item(1).GetFloatValue();
272 matrix._12 = array->Item(2).GetFloatValue();
273 matrix._13 = 0;
274 matrix._14 = 0;
275 matrix._21 = array->Item(3).GetFloatValue();
276 matrix._22 = array->Item(4).GetFloatValue();
277 matrix._23 = 0;
278 matrix._24 = 0;
279 matrix._31 = 0;
280 matrix._32 = 0;
281 matrix._33 = 1;
282 matrix._34 = 0;
283 matrix._41 = array->Item(5).GetFloatValue();
284 matrix._42 = array->Item(6).GetFloatValue();
285 matrix._43 = 0;
286 matrix._44 = 1;
287 aFunctions.AppendElement(TransformMatrix(matrix));
288 break;
290 case eCSSKeyword_matrix3d:
292 gfx::Matrix4x4 matrix;
293 matrix._11 = array->Item(1).GetFloatValue();
294 matrix._12 = array->Item(2).GetFloatValue();
295 matrix._13 = array->Item(3).GetFloatValue();
296 matrix._14 = array->Item(4).GetFloatValue();
297 matrix._21 = array->Item(5).GetFloatValue();
298 matrix._22 = array->Item(6).GetFloatValue();
299 matrix._23 = array->Item(7).GetFloatValue();
300 matrix._24 = array->Item(8).GetFloatValue();
301 matrix._31 = array->Item(9).GetFloatValue();
302 matrix._32 = array->Item(10).GetFloatValue();
303 matrix._33 = array->Item(11).GetFloatValue();
304 matrix._34 = array->Item(12).GetFloatValue();
305 matrix._41 = array->Item(13).GetFloatValue();
306 matrix._42 = array->Item(14).GetFloatValue();
307 matrix._43 = array->Item(15).GetFloatValue();
308 matrix._44 = array->Item(16).GetFloatValue();
309 aFunctions.AppendElement(TransformMatrix(matrix));
310 break;
312 case eCSSKeyword_interpolatematrix:
314 gfx3DMatrix matrix;
315 nsStyleTransformMatrix::ProcessInterpolateMatrix(matrix, array,
316 aContext,
317 aPresContext,
318 canStoreInRuleTree,
319 aBounds);
320 aFunctions.AppendElement(TransformMatrix(gfx::ToMatrix4x4(matrix)));
321 break;
323 case eCSSKeyword_perspective:
325 aFunctions.AppendElement(Perspective(array->Item(1).GetFloatValue()));
326 break;
328 default:
329 NS_ERROR("Function not handled yet!");
334 static TimingFunction
335 ToTimingFunction(ComputedTimingFunction& aCTF)
337 if (aCTF.GetType() == nsTimingFunction::Function) {
338 const nsSMILKeySpline* spline = aCTF.GetFunction();
339 return TimingFunction(CubicBezierFunction(spline->X1(), spline->Y1(),
340 spline->X2(), spline->Y2()));
343 uint32_t type = aCTF.GetType() == nsTimingFunction::StepStart ? 1 : 2;
344 return TimingFunction(StepFunction(aCTF.GetSteps(), type));
347 static void
348 AddAnimationForProperty(nsIFrame* aFrame, nsCSSProperty aProperty,
349 AnimationPlayer* aPlayer, Layer* aLayer,
350 AnimationData& aData, bool aPending)
352 MOZ_ASSERT(aLayer->AsContainerLayer(), "Should only animate ContainerLayer");
353 MOZ_ASSERT(aPlayer->GetSource(),
354 "Should not be adding an animation for a player without"
355 " an animation");
356 nsStyleContext* styleContext = aFrame->StyleContext();
357 nsPresContext* presContext = aFrame->PresContext();
358 nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame);
360 layers::Animation* animation =
361 aPending ?
362 aLayer->AddAnimationForNextTransaction() :
363 aLayer->AddAnimation();
365 const AnimationTiming& timing = aPlayer->GetSource()->Timing();
366 Nullable<TimeDuration> startTime = aPlayer->GetCurrentOrPendingStartTime();
367 animation->startTime() = startTime.IsNull()
368 ? TimeStamp()
369 : aPlayer->Timeline()->ToTimeStamp(
370 startTime.Value() + timing.mDelay);
371 animation->initialCurrentTime() = aPlayer->GetCurrentTime().Value()
372 - timing.mDelay;
373 animation->duration() = timing.mIterationDuration;
374 animation->iterationCount() = timing.mIterationCount;
375 animation->direction() = timing.mDirection;
376 animation->property() = aProperty;
377 animation->data() = aData;
379 dom::Animation* anim = aPlayer->GetSource();
380 for (size_t propIdx = 0;
381 propIdx < anim->Properties().Length();
382 propIdx++) {
383 AnimationProperty& property = anim->Properties()[propIdx];
385 if (aProperty != property.mProperty) {
386 continue;
389 for (uint32_t segIdx = 0; segIdx < property.mSegments.Length(); segIdx++) {
390 AnimationPropertySegment& segment = property.mSegments[segIdx];
392 AnimationSegment* animSegment = animation->segments().AppendElement();
393 if (aProperty == eCSSProperty_transform) {
394 animSegment->startState() = InfallibleTArray<TransformFunction>();
395 animSegment->endState() = InfallibleTArray<TransformFunction>();
397 nsCSSValueSharedList* list =
398 segment.mFromValue.GetCSSValueSharedListValue();
399 AddTransformFunctions(list->mHead, styleContext, presContext, bounds,
400 animSegment->startState().get_ArrayOfTransformFunction());
402 list = segment.mToValue.GetCSSValueSharedListValue();
403 AddTransformFunctions(list->mHead, styleContext, presContext, bounds,
404 animSegment->endState().get_ArrayOfTransformFunction());
405 } else if (aProperty == eCSSProperty_opacity) {
406 animSegment->startState() = segment.mFromValue.GetFloatValue();
407 animSegment->endState() = segment.mToValue.GetFloatValue();
410 animSegment->startPortion() = segment.mFromKey;
411 animSegment->endPortion() = segment.mToKey;
412 animSegment->sampleFn() = ToTimingFunction(segment.mTimingFunction);
417 static void
418 AddAnimationsForProperty(nsIFrame* aFrame, nsCSSProperty aProperty,
419 AnimationPlayerPtrArray& aPlayers,
420 Layer* aLayer, AnimationData& aData,
421 bool aPending) {
422 for (size_t playerIdx = 0; playerIdx < aPlayers.Length(); playerIdx++) {
423 AnimationPlayer* player = aPlayers[playerIdx];
424 dom::Animation* anim = player->GetSource();
425 if (!(anim && anim->HasAnimationOfProperty(aProperty) &&
426 player->IsRunning())) {
427 continue;
430 // Don't add animations that are pending when their corresponding
431 // refresh driver is under test control. This is because any pending
432 // animations on layers will have their start time updated with the
433 // current timestamp but when the refresh driver is under test control
434 // its refresh times are unrelated to timestamp values.
436 // Instead we leave the animation running on the main thread and the
437 // next time the refresh driver is advanced it will trigger any pending
438 // animations.
439 if (player->PlayState() == AnimationPlayState::Pending) {
440 nsRefreshDriver* driver = player->Timeline()->GetRefreshDriver();
441 if (driver && driver->IsTestControllingRefreshesEnabled()) {
442 continue;
446 AddAnimationForProperty(aFrame, aProperty, player, aLayer, aData, aPending);
447 player->SetIsRunningOnCompositor();
451 /* static */ void
452 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
453 nsDisplayListBuilder* aBuilder,
454 nsDisplayItem* aItem,
455 nsIFrame* aFrame,
456 nsCSSProperty aProperty)
458 // This function can be called in two ways: from
459 // nsDisplay*::BuildLayer while constructing a layer (with all
460 // pointers non-null), or from RestyleManager's handling of
461 // UpdateOpacityLayer/UpdateTransformLayer hints.
462 MOZ_ASSERT(!aBuilder == !aItem,
463 "should only be called in two configurations, with both "
464 "aBuilder and aItem, or with neither");
465 MOZ_ASSERT(!aItem || aFrame == aItem->Frame(), "frame mismatch");
467 bool pending = !aBuilder;
469 if (pending) {
470 aLayer->ClearAnimationsForNextTransaction();
471 } else {
472 aLayer->ClearAnimations();
475 // Update the animation generation on the layer. We need to do this before
476 // any early returns since even if we don't add any animations to the
477 // layer, we still need to mark it as up-to-date with regards to animations.
478 // Otherwise, in RestyleManager we'll notice the discrepancy between the
479 // animation generation numbers and update the layer indefinitely.
480 uint64_t animationGeneration =
481 RestyleManager::GetMaxAnimationGenerationForFrame(aFrame);
482 aLayer->SetAnimationGeneration(animationGeneration);
484 nsIContent* content = aFrame->GetContent();
485 if (!content) {
486 return;
488 AnimationPlayerCollection* transitions =
489 nsTransitionManager::GetAnimationsForCompositor(content, aProperty);
490 AnimationPlayerCollection* animations =
491 nsAnimationManager::GetAnimationsForCompositor(content, aProperty);
493 if (!animations && !transitions) {
494 return;
497 // If the frame is not prerendered, bail out.
498 // Do this check only during layer construction; during updating the
499 // caller is required to check it appropriately.
500 if (aItem && !aItem->CanUseAsyncAnimations(aBuilder)) {
501 // AnimationManager or TransitionManager need to know that we refused to
502 // run this animation asynchronously so that they will not throttle the
503 // main thread animation.
504 aFrame->Properties().Set(nsIFrame::RefusedAsyncAnimation(),
505 reinterpret_cast<void*>(intptr_t(true)));
507 // We need to schedule another refresh driver run so that AnimationManager
508 // or TransitionManager get a chance to unthrottle the animation.
509 aFrame->SchedulePaint();
510 return;
513 AnimationData data;
514 if (aProperty == eCSSProperty_transform) {
515 nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame);
516 // all data passed directly to the compositor should be in css pixels
517 float scale = nsDeviceContext::AppUnitsPerCSSPixel();
518 Point3D offsetToTransformOrigin =
519 nsDisplayTransform::GetDeltaToTransformOrigin(aFrame, scale, &bounds);
520 Point3D offsetToPerspectiveOrigin =
521 nsDisplayTransform::GetDeltaToPerspectiveOrigin(aFrame, scale);
522 nscoord perspective = 0.0;
523 nsStyleContext* parentStyleContext = aFrame->StyleContext()->GetParent();
524 if (parentStyleContext) {
525 const nsStyleDisplay* disp = parentStyleContext->StyleDisplay();
526 if (disp && disp->mChildPerspective.GetUnit() == eStyleUnit_Coord) {
527 perspective = disp->mChildPerspective.GetCoordValue();
530 nsPoint origin;
531 if (aItem) {
532 origin = aItem->ToReferenceFrame();
533 } else {
534 // transform display items used a reference frame computed from
535 // their GetTransformRootFrame().
536 nsIFrame* referenceFrame =
537 nsLayoutUtils::GetReferenceFrame(GetTransformRootFrame(aFrame));
538 origin = aFrame->GetOffsetToCrossDoc(referenceFrame);
541 data = TransformData(origin, offsetToTransformOrigin,
542 offsetToPerspectiveOrigin, bounds, perspective,
543 aFrame->PresContext()->AppUnitsPerDevPixel());
544 } else if (aProperty == eCSSProperty_opacity) {
545 data = null_t();
548 if (transitions) {
549 AddAnimationsForProperty(aFrame, aProperty, transitions->mPlayers,
550 aLayer, data, pending);
553 if (animations) {
554 AddAnimationsForProperty(aFrame, aProperty, animations->mPlayers,
555 aLayer, data, pending);
559 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
560 Mode aMode, bool aBuildCaret)
561 : mReferenceFrame(aReferenceFrame),
562 mIgnoreScrollFrame(nullptr),
563 mLayerEventRegions(nullptr),
564 mCurrentTableItem(nullptr),
565 mCurrentFrame(aReferenceFrame),
566 mCurrentReferenceFrame(aReferenceFrame),
567 mCurrentAnimatedGeometryRoot(nullptr),
568 mWillChangeBudgetCalculated(false),
569 mDirtyRect(-1,-1,-1,-1),
570 mGlassDisplayItem(nullptr),
571 mMode(aMode),
572 mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
573 mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
574 mCurrentScrollbarFlags(0),
575 mBuildCaret(aBuildCaret),
576 mIgnoreSuppression(false),
577 mHadToIgnoreSuppression(false),
578 mIsAtRootOfPseudoStackingContext(false),
579 mIncludeAllOutOfFlows(false),
580 mDescendIntoSubdocuments(true),
581 mSelectedFramesOnly(false),
582 mAccurateVisibleRegions(false),
583 mAllowMergingAndFlattening(true),
584 mWillComputePluginGeometry(false),
585 mInTransform(false),
586 mSyncDecodeImages(false),
587 mIsPaintingToWindow(false),
588 mIsCompositingCheap(false),
589 mContainsPluginItem(false),
590 mAncestorHasApzAwareEventHandler(false),
591 mHaveScrollableDisplayPort(false)
593 MOZ_COUNT_CTOR(nsDisplayListBuilder);
594 PL_InitArenaPool(&mPool, "displayListArena", 1024,
595 std::max(NS_ALIGNMENT_OF(void*),NS_ALIGNMENT_OF(double))-1);
596 RecomputeCurrentAnimatedGeometryRoot();
598 nsPresContext* pc = aReferenceFrame->PresContext();
599 nsIPresShell *shell = pc->PresShell();
600 if (pc->IsRenderingOnlySelection()) {
601 nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(shell));
602 if (selcon) {
603 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
604 getter_AddRefs(mBoundingSelection));
608 nsCSSRendering::BeginFrameTreesLocked();
609 PR_STATIC_ASSERT(nsDisplayItem::TYPE_MAX < (1 << nsDisplayItem::TYPE_BITS));
612 static void MarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame) {
613 for (nsIFrame* f = aFrame; f;
614 f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) {
615 if (f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)
616 return;
617 f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
618 if (f == aStopAtFrame) {
619 // we've reached a frame that we know will be painted, so we can stop.
620 break;
625 void nsDisplayListBuilder::SetContainsBlendMode(uint8_t aBlendMode)
627 MOZ_ASSERT(aBlendMode != NS_STYLE_BLEND_NORMAL);
628 gfxContext::GraphicsOperator op = nsCSSRendering::GetGFXBlendMode(aBlendMode);
629 mContainedBlendModes += gfx::CompositionOpForOp(op);
632 bool nsDisplayListBuilder::NeedToForceTransparentSurfaceForItem(nsDisplayItem* aItem)
634 return aItem == mGlassDisplayItem || aItem->ClearsBackground();
637 void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame,
638 nsIFrame* aFrame,
639 const nsRect& aDirtyRect)
641 nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
642 if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame) &&
643 IsPaintingToWindow()) {
644 NS_ASSERTION(aDirtyFrame == aFrame->GetParent(), "Dirty frame should be viewport frame");
645 // position: fixed items are reflowed into and only drawn inside the
646 // viewport, or the scroll position clamping scrollport size, if one is
647 // set.
648 nsIPresShell* ps = aFrame->PresContext()->PresShell();
649 dirtyRectRelativeToDirtyFrame.MoveTo(0, 0);
650 if (ps->IsScrollPositionClampingScrollPortSizeSet()) {
651 dirtyRectRelativeToDirtyFrame.SizeTo(ps->GetScrollPositionClampingScrollPortSize());
652 } else {
653 dirtyRectRelativeToDirtyFrame.SizeTo(aDirtyFrame->GetSize());
657 nsRect dirty = dirtyRectRelativeToDirtyFrame - aFrame->GetOffsetTo(aDirtyFrame);
658 nsRect overflowRect = aFrame->GetVisualOverflowRect();
660 if (aFrame->IsTransformed() &&
661 nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(),
662 eCSSProperty_transform)) {
664 * Add a fuzz factor to the overflow rectangle so that elements only just
665 * out of view are pulled into the display list, so they can be
666 * prerendered if necessary.
668 overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
671 if (!dirty.IntersectRect(dirty, overflowRect))
672 return;
673 const DisplayItemClip* clip = mClipState.GetClipForContainingBlockDescendants();
674 OutOfFlowDisplayData* data = clip ? new OutOfFlowDisplayData(*clip, dirty)
675 : new OutOfFlowDisplayData(dirty);
676 aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
678 MarkFrameForDisplay(aFrame, aDirtyFrame);
681 static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
682 nsPresContext* presContext = aFrame->PresContext();
683 presContext->PropertyTable()->
684 Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty());
686 for (nsIFrame* f = aFrame; f;
687 f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) {
688 if (!(f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO))
689 return;
690 f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
694 /* static */ FrameMetrics
695 nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame,
696 nsIFrame* aScrollFrame,
697 const nsIFrame* aReferenceFrame,
698 Layer* aLayer,
699 ViewID aScrollParentId,
700 const nsRect& aViewport,
701 bool aIsRoot,
702 const ContainerLayerParameters& aContainerParameters)
704 nsPresContext* presContext = aForFrame->PresContext();
705 int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
707 nsIPresShell* presShell = presContext->GetPresShell();
708 FrameMetrics metrics;
709 metrics.SetViewport(CSSRect::FromAppUnits(aViewport));
711 ViewID scrollId = FrameMetrics::NULL_SCROLL_ID;
712 nsIContent* content = aScrollFrame ? aScrollFrame->GetContent() : nullptr;
713 if (content) {
714 scrollId = nsLayoutUtils::FindOrCreateIDFor(content);
715 nsRect dp;
716 if (nsLayoutUtils::GetDisplayPort(content, &dp)) {
717 metrics.SetDisplayPort(CSSRect::FromAppUnits(dp));
718 nsLayoutUtils::LogTestDataForPaint(aLayer->Manager(), scrollId, "displayport",
719 metrics.GetDisplayPort());
721 if (nsLayoutUtils::GetCriticalDisplayPort(content, &dp)) {
722 metrics.SetCriticalDisplayPort(CSSRect::FromAppUnits(dp));
724 DisplayPortMarginsPropertyData* marginsData =
725 static_cast<DisplayPortMarginsPropertyData*>(content->GetProperty(nsGkAtoms::DisplayPortMargins));
726 if (marginsData) {
727 metrics.SetDisplayPortMargins(marginsData->mMargins);
731 nsIScrollableFrame* scrollableFrame = nullptr;
732 if (aScrollFrame)
733 scrollableFrame = aScrollFrame->GetScrollTargetFrame();
735 metrics.SetScrollableRect(CSSRect::FromAppUnits(
736 nsLayoutUtils::CalculateScrollableRectForFrame(scrollableFrame, aForFrame)));
738 if (scrollableFrame) {
739 nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
740 metrics.SetScrollOffset(CSSPoint::FromAppUnits(scrollPosition));
742 nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination();
743 metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));
745 // If the frame was scrolled since the last layers update, and by
746 // something other than the APZ code, we want to tell the APZ to update
747 // its scroll offset.
748 nsIAtom* lastScrollOrigin = scrollableFrame->LastScrollOrigin();
749 if (lastScrollOrigin && lastScrollOrigin != nsGkAtoms::apz) {
750 metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
752 nsIAtom* lastSmoothScrollOrigin = scrollableFrame->LastSmoothScrollOrigin();
753 if (lastSmoothScrollOrigin) {
754 metrics.SetSmoothScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
757 nsSize lineScrollAmount = scrollableFrame->GetLineScrollAmount();
758 LayoutDeviceIntSize lineScrollAmountInDevPixels =
759 LayoutDeviceIntSize::FromAppUnitsRounded(lineScrollAmount, presContext->AppUnitsPerDevPixel());
760 metrics.SetLineScrollAmount(lineScrollAmountInDevPixels);
763 metrics.SetScrollId(scrollId);
764 metrics.SetIsRoot(aIsRoot);
765 metrics.SetScrollParentId(aScrollParentId);
767 // Only the root scrollable frame for a given presShell should pick up
768 // the presShell's resolution. All the other frames are 1.0.
769 if (aScrollFrame == presShell->GetRootScrollFrame()) {
770 metrics.mPresShellResolution = presShell->GetXResolution();
771 } else {
772 metrics.mPresShellResolution = 1.0f;
774 // The cumulative resolution is the resolution at which the scroll frame's
775 // content is actually rendered. It includes the pres shell resolutions of
776 // all the pres shells from here up to the root, as well as any css-driven
777 // resolution. We don't need to compute it as it's already stored in the
778 // container parameters.
779 metrics.SetCumulativeResolution(LayoutDeviceToLayerScale(aContainerParameters.mXScale,
780 aContainerParameters.mYScale));
782 LayoutDeviceToScreenScale resolutionToScreen(
783 presShell->GetCumulativeResolution().width
784 * nsLayoutUtils::GetTransformToAncestorScale(aScrollFrame ? aScrollFrame : aForFrame).width);
785 metrics.SetExtraResolution(metrics.GetCumulativeResolution() / resolutionToScreen);
787 metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(
788 (float)nsPresContext::AppUnitsPerCSSPixel() / auPerDevPixel));
790 // Initially, AsyncPanZoomController should render the content to the screen
791 // at the painted resolution.
792 const LayerToParentLayerScale layerToParentLayerScale(1.0f);
793 metrics.SetZoom(metrics.GetCumulativeResolution() * metrics.GetDevPixelsPerCSSPixel()
794 * layerToParentLayerScale);
796 if (presShell) {
797 nsIDocument* document = nullptr;
798 document = presShell->GetDocument();
799 if (document) {
800 nsCOMPtr<nsPIDOMWindow> innerWin(document->GetInnerWindow());
801 if (innerWin) {
802 metrics.SetMayHaveTouchListeners(innerWin->HasApzAwareEventListeners());
805 metrics.SetMayHaveTouchCaret(presShell->MayHaveTouchCaret());
808 // Calculate the composition bounds as the size of the scroll frame and
809 // its origin relative to the reference frame.
810 // If aScrollFrame is null, we are in a document without a root scroll frame,
811 // so it's a xul document. In this case, use the size of the viewport frame.
812 nsIFrame* frameForCompositionBoundsCalculation = aScrollFrame ? aScrollFrame : aForFrame;
813 nsRect compositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame),
814 frameForCompositionBoundsCalculation->GetSize());
815 if (scrollableFrame) {
816 // If we have a scrollable frame, restrict the composition bounds to its
817 // scroll port. The scroll port excludes the frame borders and the scroll
818 // bars, which we don't want to be part of the composition bounds.
819 nsRect scrollPort = scrollableFrame->GetScrollPortRect();
820 compositionBounds = nsRect(compositionBounds.TopLeft() + scrollPort.TopLeft(),
821 scrollPort.Size());
823 ParentLayerRect frameBounds = LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel)
824 * metrics.GetCumulativeResolution()
825 * layerToParentLayerScale;
826 metrics.mCompositionBounds = frameBounds;
828 // For the root scroll frame of the root content document (RCD-RSF), the above calculation
829 // will yield the size of the viewport frame as the composition bounds, which
830 // doesn't actually correspond to what is visible when
831 // nsIDOMWindowUtils::setCSSViewport has been called to modify the visible area of
832 // the prescontext that the viewport frame is reflowed into. In that case if our
833 // document has a widget then the widget's bounds will correspond to what is
834 // visible. If we don't have a widget the root view's bounds correspond to what
835 // would be visible because they don't get modified by setCSSViewport.
836 bool isRootScrollFrame = aScrollFrame == presShell->GetRootScrollFrame();
837 bool isRootContentDocRootScrollFrame = isRootScrollFrame
838 && presContext->IsRootContentDocument();
839 if (isRootContentDocRootScrollFrame) {
840 if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
841 // On Android, we need to do things a bit differently to get things
842 // right (see bug 983208, bug 988882). We use the bounds of the nearest
843 // widget, but clamp the height to the frame bounds height. This clamping
844 // is done to get correct results for a page where the page is sized to
845 // the screen and thus the dynamic toolbar never disappears. In such a
846 // case, we want the composition bounds to exclude the toolbar height,
847 // but the widget bounds includes it. We don't currently have a good way
848 // of knowing about the toolbar height, but clamping to the frame bounds
849 // height gives the correct answer in the cases we care about.
850 #ifdef MOZ_WIDGET_ANDROID
851 nsIWidget* widget = rootFrame->GetNearestWidget();
852 #else
853 nsView* view = rootFrame->GetView();
854 nsIWidget* widget = view ? view->GetWidget() : nullptr;
855 #endif
856 if (widget) {
857 nsIntRect widgetBounds;
858 widget->GetBounds(widgetBounds);
859 metrics.mCompositionBounds = ParentLayerRect(ViewAs<ParentLayerPixel>(widgetBounds));
860 #ifdef MOZ_WIDGET_ANDROID
861 if (frameBounds.height < metrics.mCompositionBounds.height) {
862 metrics.mCompositionBounds.height = frameBounds.height;
864 #endif
865 } else {
866 LayoutDeviceIntSize contentSize;
867 if (nsLayoutUtils::GetContentViewerSize(presContext, contentSize)) {
868 LayoutDeviceToParentLayerScale scale(1.0f);
869 if (presContext->GetParentPresContext()) {
870 gfxSize res = presContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();
871 scale = LayoutDeviceToParentLayerScale(res.width, res.height);
873 metrics.mCompositionBounds.SizeTo(contentSize * scale);
877 // Exclude any non-overlay scroll bars from the composition bounds.
878 // This is only done for the RCD-RSF case, because otherwise the scroll
879 // port size is used and that already excludes the scroll bars.
880 if (scrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) {
881 nsMargin sizes = scrollableFrame->GetActualScrollbarSizes();
882 // Scrollbars are not subject to scaling, so CSS pixels = layer pixels for them.
883 ParentLayerMargin boundMargins = CSSMargin::FromAppUnits(sizes) * CSSToParentLayerScale(1.0f);
884 metrics.mCompositionBounds.Deflate(boundMargins);
889 metrics.SetRootCompositionSize(
890 nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame ? aScrollFrame : aForFrame,
891 isRootContentDocRootScrollFrame, metrics));
893 if (gfxPrefs::APZPrintTree()) {
894 if (nsIContent* content = frameForCompositionBoundsCalculation->GetContent()) {
895 nsAutoString contentDescription;
896 content->Describe(contentDescription);
897 metrics.SetContentDescription(NS_LossyConvertUTF16toASCII(contentDescription));
901 metrics.SetPresShellId(presShell->GetPresShellId());
903 // If the scroll frame's content is marked 'scrollgrab', record this
904 // in the FrameMetrics so APZ knows to provide the scroll grabbing
905 // behaviour.
906 if (aScrollFrame && nsContentUtils::HasScrollgrab(aScrollFrame->GetContent())) {
907 metrics.SetHasScrollgrab(true);
910 // Also compute and set the background color.
911 // This is needed for APZ overscrolling support.
912 if (aScrollFrame) {
913 if (isRootScrollFrame) {
914 metrics.SetBackgroundColor(presShell->GetCanvasBackground());
915 } else {
916 nsStyleContext* backgroundStyle;
917 if (nsCSSRendering::FindBackground(aScrollFrame, &backgroundStyle)) {
918 metrics.SetBackgroundColor(backgroundStyle->StyleBackground()->mBackgroundColor);
923 return metrics;
926 nsDisplayListBuilder::~nsDisplayListBuilder() {
927 NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
928 "All frames should have been unmarked");
929 NS_ASSERTION(mPresShellStates.Length() == 0,
930 "All presshells should have been exited");
931 NS_ASSERTION(!mCurrentTableItem, "No table item should be active");
933 nsCSSRendering::EndFrameTreesLocked();
935 for (uint32_t i = 0; i < mDisplayItemClipsToDestroy.Length(); ++i) {
936 mDisplayItemClipsToDestroy[i]->DisplayItemClip::~DisplayItemClip();
939 PL_FinishArenaPool(&mPool);
940 MOZ_COUNT_DTOR(nsDisplayListBuilder);
943 uint32_t
944 nsDisplayListBuilder::GetBackgroundPaintFlags() {
945 uint32_t flags = 0;
946 if (mSyncDecodeImages) {
947 flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
949 if (mIsPaintingToWindow) {
950 flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
952 return flags;
955 void
956 nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
957 const nsRegion& aRegion)
959 if (aRegion.IsEmpty())
960 return;
962 nsRegion tmp;
963 tmp.Sub(*aVisibleRegion, aRegion);
964 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
965 // to its bounds either, which can be very bad (see bug 516740).
966 // Do let aVisibleRegion get more complex if by doing so we reduce its
967 // area by at least half.
968 if (GetAccurateVisibleRegions() || tmp.GetNumRects() <= 15 ||
969 tmp.Area() <= aVisibleRegion->Area()/2) {
970 *aVisibleRegion = tmp;
974 nsCaret *
975 nsDisplayListBuilder::GetCaret() {
976 nsRefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
977 return caret;
980 void
981 nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
982 bool aPointerEventsNoneDoc)
984 PresShellState* state = mPresShellStates.AppendElement();
985 state->mPresShell = aReferenceFrame->PresContext()->PresShell();
986 state->mCaretFrame = nullptr;
987 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
989 state->mPresShell->UpdateCanvasBackground();
991 if (mIsPaintingToWindow) {
992 mReferenceFrame->AddPaintedPresShell(state->mPresShell);
994 state->mPresShell->IncrementPaintCount();
997 bool buildCaret = mBuildCaret;
998 if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
999 if (state->mPresShell->IsPaintingSuppressed()) {
1000 mHadToIgnoreSuppression = true;
1002 state->mIsBackgroundOnly = false;
1003 } else {
1004 state->mIsBackgroundOnly = true;
1005 buildCaret = false;
1008 bool pointerEventsNone = aPointerEventsNoneDoc;
1009 if (IsInSubdocument()) {
1010 pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2].mInsidePointerEventsNoneDoc;
1012 state->mInsidePointerEventsNoneDoc = pointerEventsNone;
1014 if (!buildCaret)
1015 return;
1017 nsRefPtr<nsCaret> caret = state->mPresShell->GetCaret();
1018 state->mCaretFrame = caret->GetPaintGeometry(&state->mCaretRect);
1019 if (state->mCaretFrame) {
1020 mFramesMarkedForDisplay.AppendElement(state->mCaretFrame);
1021 MarkFrameForDisplay(state->mCaretFrame, nullptr);
1025 void
1026 nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame)
1028 NS_ASSERTION(CurrentPresShellState()->mPresShell ==
1029 aReferenceFrame->PresContext()->PresShell(),
1030 "Presshell mismatch");
1031 ResetMarkedFramesForDisplayList();
1032 mPresShellStates.SetLength(mPresShellStates.Length() - 1);
1035 void
1036 nsDisplayListBuilder::ResetMarkedFramesForDisplayList()
1038 // Unmark and pop off the frames marked for display in this pres shell.
1039 uint32_t firstFrameForShell = CurrentPresShellState()->mFirstFrameMarkedForDisplay;
1040 for (uint32_t i = firstFrameForShell;
1041 i < mFramesMarkedForDisplay.Length(); ++i) {
1042 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]);
1044 mFramesMarkedForDisplay.SetLength(firstFrameForShell);
1047 void
1048 nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame,
1049 const nsFrameList& aFrames,
1050 const nsRect& aDirtyRect) {
1051 mFramesMarkedForDisplay.SetCapacity(mFramesMarkedForDisplay.Length() + aFrames.GetLength());
1052 for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) {
1053 mFramesMarkedForDisplay.AppendElement(e.get());
1054 MarkOutOfFlowFrameForDisplay(aDirtyFrame, e.get(), aDirtyRect);
1058 void
1059 nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect)
1061 nsAutoTArray<nsIFrame::ChildList,4> childListArray;
1062 aDirtyFrame->GetChildLists(&childListArray);
1063 nsIFrame::ChildListArrayIterator lists(childListArray);
1064 for (; !lists.IsDone(); lists.Next()) {
1065 nsFrameList::Enumerator childFrames(lists.CurrentList());
1066 for (; !childFrames.AtEnd(); childFrames.Next()) {
1067 nsIFrame *child = childFrames.get();
1068 if (child->Preserves3D()) {
1069 mFramesMarkedForDisplay.AppendElement(child);
1070 nsRect dirty = aDirtyRect - child->GetOffsetTo(aDirtyFrame);
1072 child->Properties().Set(nsDisplayListBuilder::Preserve3DDirtyRectProperty(),
1073 new nsRect(dirty));
1075 MarkFrameForDisplay(child, aDirtyFrame);
1081 void*
1082 nsDisplayListBuilder::Allocate(size_t aSize)
1084 void *tmp;
1085 PL_ARENA_ALLOCATE(tmp, &mPool, aSize);
1086 if (!tmp) {
1087 NS_ABORT_OOM(aSize);
1089 return tmp;
1092 const DisplayItemClip*
1093 nsDisplayListBuilder::AllocateDisplayItemClip(const DisplayItemClip& aOriginal)
1095 void* p = Allocate(sizeof(DisplayItemClip));
1096 if (!aOriginal.GetRoundedRectCount()) {
1097 memcpy(p, &aOriginal, sizeof(DisplayItemClip));
1098 return static_cast<DisplayItemClip*>(p);
1101 DisplayItemClip* c = new (p) DisplayItemClip(aOriginal);
1102 mDisplayItemClipsToDestroy.AppendElement(c);
1103 return c;
1106 const nsIFrame*
1107 nsDisplayListBuilder::FindReferenceFrameFor(const nsIFrame *aFrame,
1108 nsPoint* aOffset)
1110 if (aFrame == mCurrentFrame) {
1111 if (aOffset) {
1112 *aOffset = mCurrentOffsetToReferenceFrame;
1114 return mCurrentReferenceFrame;
1116 for (const nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f))
1118 if (f == mReferenceFrame || f->IsTransformed()) {
1119 if (aOffset) {
1120 *aOffset = aFrame->GetOffsetToCrossDoc(f);
1122 return f;
1125 if (aOffset) {
1126 *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
1128 return mReferenceFrame;
1131 // Sticky frames are active if their nearest scrollable frame is also active.
1132 static bool
1133 IsStickyFrameActive(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aParent)
1135 MOZ_ASSERT(aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY);
1137 // Find the nearest scrollframe.
1138 nsIFrame* cursor = aFrame;
1139 nsIFrame* parent = aParent;
1140 while (parent->GetType() != nsGkAtoms::scrollFrame) {
1141 cursor = parent;
1142 if ((parent = nsLayoutUtils::GetCrossDocParentFrame(cursor)) == nullptr) {
1143 return false;
1147 nsIScrollableFrame* sf = do_QueryFrame(parent);
1148 return sf->IsScrollingActive(aBuilder) && sf->GetScrolledFrame() == cursor;
1151 bool
1152 nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParent)
1154 if (nsLayoutUtils::IsPopup(aFrame))
1155 return true;
1156 if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(aFrame))
1157 return true;
1158 if (!aFrame->GetParent() &&
1159 nsLayoutUtils::ViewportHasDisplayPort(aFrame->PresContext())) {
1160 // Viewport frames in a display port need to be animated geometry roots
1161 // for background-attachment:fixed elements.
1162 return true;
1165 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
1166 if (!parent)
1167 return true;
1169 nsIAtom* parentType = parent->GetType();
1170 // Treat the slider thumb as being as an active scrolled root when it wants
1171 // its own layer so that it can move without repainting.
1172 if (parentType == nsGkAtoms::sliderFrame && nsLayoutUtils::IsScrollbarThumbLayerized(aFrame)) {
1173 return true;
1176 if (aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY &&
1177 IsStickyFrameActive(this, aFrame, parent))
1179 return true;
1182 if (parentType == nsGkAtoms::scrollFrame || parentType == nsGkAtoms::listControlFrame) {
1183 nsIScrollableFrame* sf = do_QueryFrame(parent);
1184 if (sf->IsScrollingActive(this) && sf->GetScrolledFrame() == aFrame) {
1185 return true;
1189 // Fixed-pos frames are parented by the viewport frame, which has no parent.
1190 if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame)) {
1191 return true;
1194 if (aParent) {
1195 *aParent = parent;
1197 return false;
1200 bool
1201 nsDisplayListBuilder::GetCachedAnimatedGeometryRoot(const nsIFrame* aFrame,
1202 const nsIFrame* aStopAtAncestor,
1203 nsIFrame** aOutResult)
1205 AnimatedGeometryRootLookup lookup(aFrame, aStopAtAncestor);
1206 return mAnimatedGeometryRootCache.Get(lookup, aOutResult);
1209 static nsIFrame*
1210 ComputeAnimatedGeometryRootFor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
1211 const nsIFrame* aStopAtAncestor = nullptr,
1212 bool aUseCache = false)
1214 nsIFrame* cursor = aFrame;
1215 while (cursor != aStopAtAncestor) {
1216 if (aUseCache) {
1217 nsIFrame* result;
1218 if (aBuilder->GetCachedAnimatedGeometryRoot(cursor, aStopAtAncestor, &result)) {
1219 return result;
1222 nsIFrame* next;
1223 if (aBuilder->IsAnimatedGeometryRoot(cursor, &next))
1224 return cursor;
1225 cursor = next;
1227 return cursor;
1230 nsIFrame*
1231 nsDisplayListBuilder::FindAnimatedGeometryRootFor(nsIFrame* aFrame, const nsIFrame* aStopAtAncestor)
1233 if (aFrame == mCurrentFrame) {
1234 return mCurrentAnimatedGeometryRoot;
1237 nsIFrame* result = ComputeAnimatedGeometryRootFor(this, aFrame, aStopAtAncestor, true);
1238 AnimatedGeometryRootLookup lookup(aFrame, aStopAtAncestor);
1239 mAnimatedGeometryRootCache.Put(lookup, result);
1240 return result;
1243 void
1244 nsDisplayListBuilder::RecomputeCurrentAnimatedGeometryRoot()
1246 mCurrentAnimatedGeometryRoot = ComputeAnimatedGeometryRootFor(this, const_cast<nsIFrame *>(mCurrentFrame));
1247 AnimatedGeometryRootLookup lookup(mCurrentFrame, nullptr);
1248 mAnimatedGeometryRootCache.Put(lookup, mCurrentAnimatedGeometryRoot);
1251 void
1252 nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame)
1254 if (!IsForPainting() || IsInSubdocument()) {
1255 return;
1258 Matrix4x4 referenceFrameToRootReferenceFrame;
1260 // The const_cast is for nsLayoutUtils::GetTransformToAncestor.
1261 nsIFrame* referenceFrame = const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
1263 if (IsInTransform()) {
1264 // Only support 2d rectilinear transforms. Transform support is needed for
1265 // the horizontal flip transform that's applied to the urlbar textbox in
1266 // RTL mode - it should be able to exclude itself from the draggable region.
1267 referenceFrameToRootReferenceFrame =
1268 nsLayoutUtils::GetTransformToAncestor(referenceFrame, mReferenceFrame);
1269 Matrix referenceFrameToRootReferenceFrame2d;
1270 if (!referenceFrameToRootReferenceFrame.Is2D(&referenceFrameToRootReferenceFrame2d) ||
1271 !referenceFrameToRootReferenceFrame2d.IsRectilinear()) {
1272 return;
1274 } else {
1275 MOZ_ASSERT(referenceFrame == mReferenceFrame,
1276 "referenceFrameToRootReferenceFrame needs to be adjusted");
1279 // We do some basic visibility checking on the frame's border box here.
1280 // We intersect it both with the current dirty rect and with the current
1281 // clip. Either one is just a conservative approximation on its own, but
1282 // their intersection luckily works well enough for our purposes, so that
1283 // we don't have to do full-blown visibility computations.
1284 // The most important case we need to handle is the scrolled-off tab:
1285 // If the tab bar overflows, tab parts that are clipped by the scrollbox
1286 // should not be allowed to interfere with the window dragging region. Using
1287 // just the current DisplayItemClip is not enough to cover this case
1288 // completely because clips are reset while building stacking context
1289 // contents, so for example we'd fail to clip frames that have a clip path
1290 // applied to them. But the current dirty rect doesn't get reset in that
1291 // case, so we use it to make this case work.
1292 nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mDirtyRect);
1293 borderBox += ToReferenceFrame(aFrame);
1294 const DisplayItemClip* clip = ClipState().GetCurrentCombinedClip(this);
1295 if (clip) {
1296 borderBox = clip->ApplyNonRoundedIntersection(borderBox);
1298 if (!borderBox.IsEmpty()) {
1299 LayoutDeviceRect devPixelBorderBox =
1300 LayoutDevicePixel::FromAppUnits(borderBox, aFrame->PresContext()->AppUnitsPerDevPixel());
1301 LayoutDeviceRect transformedDevPixelBorderBox =
1302 TransformTo<LayoutDevicePixel>(referenceFrameToRootReferenceFrame, devPixelBorderBox);
1303 transformedDevPixelBorderBox.Round();
1304 LayoutDeviceIntRect transformedDevPixelBorderBoxInt;
1305 if (transformedDevPixelBorderBox.ToIntRect(&transformedDevPixelBorderBoxInt)) {
1306 const nsStyleUserInterface* styleUI = aFrame->StyleUserInterface();
1307 if (styleUI->mWindowDragging == NS_STYLE_WINDOW_DRAGGING_DRAG) {
1308 mWindowDraggingRegion.OrWith(LayoutDevicePixel::ToUntyped(transformedDevPixelBorderBoxInt));
1309 } else {
1310 mWindowDraggingRegion.SubOut(LayoutDevicePixel::ToUntyped(transformedDevPixelBorderBoxInt));
1316 void
1317 nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame, const nsSize& aRect) {
1318 // Make sure that we don't query the budget before the display list is fully
1319 // built and that the will change budget is locked in.
1320 NS_ASSERTION(!mWillChangeBudgetCalculated,
1321 "Can't modify the budget once it's been used.");
1323 DocumentWillChangeBudget budget;
1325 nsPresContext* key = aFrame->PresContext();
1326 if (mWillChangeBudget.Contains(key)) {
1327 mWillChangeBudget.Get(key, &budget);
1330 // There's significant overhead for each layer created from Gecko
1331 // (IPC+Shared Objects) and from the backend (like an OpenGL texture).
1332 // Therefore we set a minimum cost threshold of a 64x64 area.
1333 int minBudgetCost = 64 * 64;
1335 budget.mBudget +=
1336 std::max(minBudgetCost,
1337 nsPresContext::AppUnitsToIntCSSPixels(aRect.width) *
1338 nsPresContext::AppUnitsToIntCSSPixels(aRect.height));
1340 mWillChangeBudget.Put(key, budget);
1343 bool
1344 nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame) const {
1345 uint32_t multiplier = 3;
1347 mWillChangeBudgetCalculated = true;
1349 nsPresContext* key = aFrame->PresContext();
1350 if (!mWillChangeBudget.Contains(key)) {
1351 NS_ASSERTION(false, "If we added nothing to our budget then this "
1352 "shouldn't be called.");
1353 return false;
1356 DocumentWillChangeBudget budget;
1357 mWillChangeBudget.Get(key, &budget);
1359 nsRect area = aFrame->PresContext()->GetVisibleArea();
1360 uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) *
1361 nsPresContext::AppUnitsToIntCSSPixels(area.height);
1363 bool onBudget = budget.mBudget / multiplier < budgetLimit;
1364 if (!onBudget) {
1365 nsString usageStr;
1366 usageStr.AppendInt(budget.mBudget);
1368 nsString multiplierStr;
1369 multiplierStr.AppendInt(multiplier);
1371 nsString limitStr;
1372 limitStr.AppendInt(budgetLimit);
1374 const char16_t* params[] = { usageStr.get(), multiplierStr.get(), limitStr.get() };
1375 key->Document()->WarnOnceAbout(nsIDocument::eWillChangeBudget, false,
1376 params, ArrayLength(params));
1378 return onBudget;
1381 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const
1383 aDestination.BorderBackground()->AppendToTop(BorderBackground());
1384 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
1385 aDestination.Floats()->AppendToTop(Floats());
1386 aDestination.Content()->AppendToTop(Content());
1387 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
1388 aDestination.Outlines()->AppendToTop(Outlines());
1391 static void
1392 MoveListTo(nsDisplayList* aList, nsTArray<nsDisplayItem*>* aElements) {
1393 nsDisplayItem* item;
1394 while ((item = aList->RemoveBottom()) != nullptr) {
1395 aElements->AppendElement(item);
1399 nsRect
1400 nsDisplayList::GetBounds(nsDisplayListBuilder* aBuilder) const {
1401 nsRect bounds;
1402 for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) {
1403 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
1405 return bounds;
1408 nsRect
1409 nsDisplayList::GetVisibleRect() const {
1410 nsRect result;
1411 for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) {
1412 result.UnionRect(result, i->GetVisibleRect());
1414 return result;
1417 bool
1418 nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder,
1419 nsRegion* aVisibleRegion,
1420 nsIFrame* aDisplayPortFrame) {
1421 PROFILER_LABEL("nsDisplayList", "ComputeVisibilityForRoot",
1422 js::ProfileEntry::Category::GRAPHICS);
1424 nsRegion r;
1425 r.And(*aVisibleRegion, GetBounds(aBuilder));
1426 return ComputeVisibilityForSublist(aBuilder, aVisibleRegion,
1427 r.GetBounds(), aDisplayPortFrame);
1430 static nsRegion
1431 TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
1433 bool snap;
1434 nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap);
1435 if (aBuilder->IsForPluginGeometry() &&
1436 aItem->GetType() != nsDisplayItem::TYPE_LAYER_EVENT_REGIONS)
1438 // Treat all leaf chrome items as opaque, unless their frames are opacity:0.
1439 // Since opacity:0 frames generate an nsDisplayOpacity, that item will
1440 // not be treated as opaque here, so opacity:0 chrome content will be
1441 // effectively ignored, as it should be.
1442 // We treat leaf chrome items as opaque to ensure that they cover
1443 // content plugins, for security reasons.
1444 // Non-leaf chrome items don't render contents of their own so shouldn't
1445 // be treated as opaque (and their bounds is just the union of their
1446 // children, which might be a large area their contents don't really cover).
1447 nsIFrame* f = aItem->Frame();
1448 if (f->PresContext()->IsChrome() && !aItem->GetChildren() &&
1449 f->StyleDisplay()->mOpacity != 0.0) {
1450 opaque = aItem->GetBounds(aBuilder, &snap);
1453 if (opaque.IsEmpty()) {
1454 return opaque;
1456 nsRegion opaqueClipped;
1457 nsRegionRectIterator iter(opaque);
1458 for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
1459 opaqueClipped.Or(opaqueClipped, aItem->GetClip().ApproximateIntersectInward(*r));
1461 return opaqueClipped;
1464 bool
1465 nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
1466 nsRegion* aVisibleRegion,
1467 const nsRect& aListVisibleBounds,
1468 nsIFrame* aDisplayPortFrame) {
1469 #ifdef DEBUG
1470 nsRegion r;
1471 r.And(*aVisibleRegion, GetBounds(aBuilder));
1472 NS_ASSERTION(r.GetBounds().IsEqualInterior(aListVisibleBounds),
1473 "bad aListVisibleBounds");
1474 #endif
1476 bool anyVisible = false;
1478 nsAutoTArray<nsDisplayItem*, 512> elements;
1479 MoveListTo(this, &elements);
1481 for (int32_t i = elements.Length() - 1; i >= 0; --i) {
1482 nsDisplayItem* item = elements[i];
1483 nsRect bounds = item->GetClippedBounds(aBuilder);
1485 nsRegion itemVisible;
1486 itemVisible.And(*aVisibleRegion, bounds);
1487 item->mVisibleRect = itemVisible.GetBounds();
1489 if (item->ComputeVisibility(aBuilder, aVisibleRegion)) {
1490 anyVisible = true;
1492 nsRegion opaque = TreatAsOpaque(item, aBuilder);
1493 // Subtract opaque item from the visible region
1494 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque);
1496 AppendToBottom(item);
1499 mIsOpaque = !aVisibleRegion->Intersects(aListVisibleBounds);
1500 return anyVisible;
1503 static bool
1504 StartPendingAnimationsOnSubDocuments(nsIDocument* aDocument, void* aReadyTime)
1506 PendingPlayerTracker* tracker = aDocument->GetPendingPlayerTracker();
1507 if (tracker) {
1508 nsIPresShell* shell = aDocument->GetShell();
1509 // If paint-suppression is in effect then we haven't finished painting
1510 // this document yet so we shouldn't start animations
1511 if (!shell || !shell->IsPaintingSuppressed()) {
1512 const TimeStamp& readyTime = *static_cast<TimeStamp*>(aReadyTime);
1513 tracker->StartPendingPlayersOnNextTick(readyTime);
1516 aDocument->EnumerateSubDocuments(StartPendingAnimationsOnSubDocuments,
1517 aReadyTime);
1518 return true;
1521 static void
1522 StartPendingAnimations(nsIDocument* aDocument,
1523 const TimeStamp& aReadyTime) {
1524 MOZ_ASSERT(!aReadyTime.IsNull(),
1525 "Animation ready time is not set. Perhaps we're using a layer"
1526 " manager that doesn't update it");
1527 StartPendingAnimationsOnSubDocuments(aDocument,
1528 const_cast<TimeStamp*>(&aReadyTime));
1532 * We paint by executing a layer manager transaction, constructing a
1533 * single layer representing the display list, and then making it the
1534 * root of the layer manager, drawing into the PaintedLayers.
1536 already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder,
1537 nsRenderingContext* aCtx,
1538 uint32_t aFlags) {
1539 PROFILER_LABEL("nsDisplayList", "PaintRoot",
1540 js::ProfileEntry::Category::GRAPHICS);
1542 nsRefPtr<LayerManager> layerManager;
1543 bool widgetTransaction = false;
1544 bool allowRetaining = false;
1545 bool doBeginTransaction = true;
1546 nsView *view = nullptr;
1547 if (aFlags & PAINT_USE_WIDGET_LAYERS) {
1548 nsIFrame* rootReferenceFrame = aBuilder->RootReferenceFrame();
1549 view = rootReferenceFrame->GetView();
1550 NS_ASSERTION(rootReferenceFrame == nsLayoutUtils::GetDisplayRootFrame(rootReferenceFrame),
1551 "Reference frame must be a display root for us to use the layer manager");
1552 nsIWidget* window = rootReferenceFrame->GetNearestWidget();
1553 if (window) {
1554 layerManager = window->GetLayerManager(&allowRetaining);
1555 if (layerManager) {
1556 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
1557 widgetTransaction = true;
1561 if (!layerManager) {
1562 if (!aCtx) {
1563 NS_WARNING("Nowhere to paint into");
1564 return nullptr;
1566 layerManager = new BasicLayerManager(BasicLayerManager::BLM_OFFSCREEN);
1569 // Store the existing layer builder to reinstate it on return.
1570 FrameLayerBuilder *oldBuilder = layerManager->GetLayerBuilder();
1572 FrameLayerBuilder *layerBuilder = new FrameLayerBuilder();
1573 layerBuilder->Init(aBuilder, layerManager);
1575 if (aFlags & PAINT_COMPRESSED) {
1576 layerBuilder->SetLayerTreeCompressionMode();
1579 if (aFlags & PAINT_FLUSH_LAYERS) {
1580 FrameLayerBuilder::InvalidateAllLayers(layerManager);
1583 if (doBeginTransaction) {
1584 if (aCtx) {
1585 layerManager->BeginTransactionWithTarget(aCtx->ThebesContext());
1586 } else {
1587 layerManager->BeginTransaction();
1590 if (widgetTransaction) {
1591 layerBuilder->DidBeginRetainedLayerTransaction(layerManager);
1594 nsIFrame* frame = aBuilder->RootReferenceFrame();
1595 nsPresContext* presContext = frame->PresContext();
1596 nsIPresShell* presShell = presContext->GetPresShell();
1598 NotifySubDocInvalidationFunc computeInvalidFunc =
1599 presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0;
1600 bool computeInvalidRect = (computeInvalidFunc ||
1601 (!layerManager->IsCompositingCheap() && layerManager->NeedsWidgetInvalidation())) &&
1602 widgetTransaction;
1604 UniquePtr<LayerProperties> props;
1605 if (computeInvalidRect) {
1606 props = Move(LayerProperties::CloneFrom(layerManager->GetRoot()));
1609 ContainerLayerParameters containerParameters
1610 (presShell->GetXResolution(), presShell->GetYResolution());
1611 nsRefPtr<ContainerLayer> root = layerBuilder->
1612 BuildContainerLayerFor(aBuilder, layerManager, frame, nullptr, this,
1613 containerParameters, nullptr);
1615 nsIDocument* document = nullptr;
1616 if (presShell) {
1617 document = presShell->GetDocument();
1620 if (!root) {
1621 layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
1622 return nullptr;
1624 // Root is being scaled up by the X/Y resolution. Scale it back down.
1625 root->SetPostScale(1.0f/containerParameters.mXScale,
1626 1.0f/containerParameters.mYScale);
1627 root->SetScaleToResolution(presShell->ScaleToResolution(),
1628 containerParameters.mXScale);
1629 if (aBuilder->IsBuildingLayerEventRegions() &&
1630 nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell)) {
1631 root->SetEventRegionsOverride(EventRegionsOverride::ForceDispatchToContent);
1632 } else {
1633 root->SetEventRegionsOverride(EventRegionsOverride::NoOverride);
1636 if (gfxPrefs::LayoutUseContainersForRootFrames()) {
1637 bool isRoot = presContext->IsRootContentDocument();
1639 nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
1641 nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize());
1643 root->SetFrameMetrics(
1644 nsDisplayScrollLayer::ComputeFrameMetrics(frame, rootScrollFrame,
1645 aBuilder->FindReferenceFrameFor(frame),
1646 root, FrameMetrics::NULL_SCROLL_ID, viewport,
1647 isRoot, containerParameters));
1650 // NS_WARNING is debug-only, so don't even bother checking the conditions in
1651 // a release build.
1652 #ifdef DEBUG
1653 bool usingDisplayport = false;
1654 if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
1655 nsIContent* content = rootScrollFrame->GetContent();
1656 if (content) {
1657 usingDisplayport = nsLayoutUtils::GetDisplayPort(content, nullptr);
1660 if (usingDisplayport &&
1661 !(root->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
1662 SpammyLayoutWarningsEnabled()) {
1663 // See bug 693938, attachment 567017
1664 NS_WARNING("Transparent content with displayports can be expensive.");
1666 #endif
1668 layerManager->SetRoot(root);
1669 layerBuilder->WillEndTransaction();
1671 if (widgetTransaction ||
1672 // SVG-as-an-image docs don't paint as part of the retained layer tree,
1673 // but they still need the invalidation state bits cleared in order for
1674 // invalidation for CSS/SMIL animation to work properly.
1675 (document && document->IsBeingUsedAsImage())) {
1676 frame->ClearInvalidationStateBits();
1679 bool temp = aBuilder->SetIsCompositingCheap(layerManager->IsCompositingCheap());
1680 LayerManager::EndTransactionFlags flags = LayerManager::END_DEFAULT;
1681 if (layerManager->NeedsWidgetInvalidation()) {
1682 if (aFlags & PAINT_NO_COMPOSITE) {
1683 flags = LayerManager::END_NO_COMPOSITE;
1685 } else {
1686 // Client layer managers never composite directly, so
1687 // we don't need to worry about END_NO_COMPOSITE.
1688 if (aBuilder->WillComputePluginGeometry()) {
1689 flags = LayerManager::END_NO_REMOTE_COMPOSITE;
1693 MaybeSetupTransactionIdAllocator(layerManager, view);
1695 layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
1696 aBuilder, flags);
1697 aBuilder->SetIsCompositingCheap(temp);
1698 layerBuilder->DidEndTransaction();
1700 if (document && widgetTransaction) {
1701 StartPendingAnimations(document, layerManager->GetAnimationReadyTime());
1704 nsIntRegion invalid;
1705 if (props) {
1706 invalid = props->ComputeDifferences(root, computeInvalidFunc);
1707 } else if (widgetTransaction) {
1708 LayerProperties::ClearInvalidations(root);
1711 bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
1712 if (view) {
1713 if (props) {
1714 if (!invalid.IsEmpty()) {
1715 nsIntRect bounds = invalid.GetBounds();
1716 nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
1717 presContext->DevPixelsToAppUnits(bounds.y),
1718 presContext->DevPixelsToAppUnits(bounds.width),
1719 presContext->DevPixelsToAppUnits(bounds.height));
1720 if (shouldInvalidate) {
1721 view->GetViewManager()->InvalidateViewNoSuppression(view, rect);
1723 presContext->NotifyInvalidation(bounds, 0);
1725 } else if (shouldInvalidate) {
1726 view->GetViewManager()->InvalidateView(view);
1730 if (aFlags & PAINT_FLUSH_LAYERS) {
1731 FrameLayerBuilder::InvalidateAllLayers(layerManager);
1734 layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
1735 return layerManager.forget();
1738 uint32_t nsDisplayList::Count() const {
1739 uint32_t count = 0;
1740 for (nsDisplayItem* i = GetBottom(); i; i = i->GetAbove()) {
1741 ++count;
1743 return count;
1746 nsDisplayItem* nsDisplayList::RemoveBottom() {
1747 nsDisplayItem* item = mSentinel.mAbove;
1748 if (!item)
1749 return nullptr;
1750 mSentinel.mAbove = item->mAbove;
1751 if (item == mTop) {
1752 // must have been the only item
1753 mTop = &mSentinel;
1755 item->mAbove = nullptr;
1756 return item;
1759 void nsDisplayList::DeleteAll() {
1760 nsDisplayItem* item;
1761 while ((item = RemoveBottom()) != nullptr) {
1762 item->~nsDisplayItem();
1766 static bool
1767 GetMouseThrough(const nsIFrame* aFrame)
1769 if (!aFrame->IsBoxFrame())
1770 return false;
1772 const nsIFrame* frame = aFrame;
1773 while (frame) {
1774 if (frame->GetStateBits() & NS_FRAME_MOUSE_THROUGH_ALWAYS) {
1775 return true;
1776 } else if (frame->GetStateBits() & NS_FRAME_MOUSE_THROUGH_NEVER) {
1777 return false;
1779 frame = nsBox::GetParentBox(frame);
1781 return false;
1784 static bool
1785 IsFrameReceivingPointerEvents(nsIFrame* aFrame)
1787 nsSubDocumentFrame* frame = do_QueryFrame(aFrame);
1788 if (frame && frame->PassPointerEventsToChildren()) {
1789 return true;
1791 return NS_STYLE_POINTER_EVENTS_NONE !=
1792 aFrame->StyleVisibility()->GetEffectivePointerEvents(aFrame);
1795 // A list of frames, and their z depth. Used for sorting
1796 // the results of hit testing.
1797 struct FramesWithDepth
1799 explicit FramesWithDepth(float aDepth) :
1800 mDepth(aDepth)
1803 bool operator<(const FramesWithDepth& aOther) const {
1804 if (mDepth != aOther.mDepth) {
1805 // We want to sort so that the shallowest item (highest depth value) is first
1806 return mDepth > aOther.mDepth;
1808 return this < &aOther;
1810 bool operator==(const FramesWithDepth& aOther) const {
1811 return this == &aOther;
1814 float mDepth;
1815 nsTArray<nsIFrame*> mFrames;
1818 // Sort the frames by depth and then moves all the contained frames to the destination
1819 void FlushFramesArray(nsTArray<FramesWithDepth>& aSource, nsTArray<nsIFrame*>* aDest)
1821 if (aSource.IsEmpty()) {
1822 return;
1824 aSource.Sort();
1825 uint32_t length = aSource.Length();
1826 for (uint32_t i = 0; i < length; i++) {
1827 aDest->MoveElementsFrom(aSource[i].mFrames);
1829 aSource.Clear();
1832 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
1833 nsDisplayItem::HitTestState* aState,
1834 nsTArray<nsIFrame*> *aOutFrames) const {
1835 int32_t itemBufferStart = aState->mItemBuffer.Length();
1836 nsDisplayItem* item;
1837 for (item = GetBottom(); item; item = item->GetAbove()) {
1838 aState->mItemBuffer.AppendElement(item);
1840 nsAutoTArray<FramesWithDepth, 16> temp;
1841 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart; --i) {
1842 // Pop element off the end of the buffer. We want to shorten the buffer
1843 // so that recursive calls to HitTest have more buffer space.
1844 item = aState->mItemBuffer[i];
1845 aState->mItemBuffer.SetLength(i);
1847 bool snap;
1848 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
1849 if (item->GetClip().MayIntersect(r)) {
1850 nsAutoTArray<nsIFrame*, 16> outFrames;
1851 item->HitTest(aBuilder, aRect, aState, &outFrames);
1853 // For 3d transforms with preserve-3d we add hit frames into the temp list
1854 // so we can sort them later, otherwise we add them directly to the output list.
1855 nsTArray<nsIFrame*> *writeFrames = aOutFrames;
1856 if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
1857 item->Frame()->Preserves3D()) {
1858 if (outFrames.Length()) {
1859 nsDisplayTransform *transform = static_cast<nsDisplayTransform*>(item);
1860 nsPoint point = aRect.TopLeft();
1861 // A 1x1 rect means a point, otherwise use the center of the rect
1862 if (aRect.width != 1 || aRect.height != 1) {
1863 point = aRect.Center();
1865 temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
1866 writeFrames = &temp[temp.Length() - 1].mFrames;
1868 } else {
1869 // We may have just finished a run of consecutive preserve-3d transforms,
1870 // so flush these into the destination array before processing our frame list.
1871 FlushFramesArray(temp, aOutFrames);
1874 for (uint32_t j = 0; j < outFrames.Length(); j++) {
1875 nsIFrame *f = outFrames.ElementAt(j);
1876 // Handle the XUL 'mousethrough' feature and 'pointer-events'.
1877 if (!GetMouseThrough(f) && IsFrameReceivingPointerEvents(f)) {
1878 writeFrames->AppendElement(f);
1883 // Clear any remaining preserve-3d transforms.
1884 FlushFramesArray(temp, aOutFrames);
1885 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
1886 "How did we forget to pop some elements?");
1889 static void Sort(nsDisplayList* aList, int32_t aCount, nsDisplayList::SortLEQ aCmp,
1890 void* aClosure) {
1891 if (aCount < 2)
1892 return;
1894 nsDisplayList list1;
1895 nsDisplayList list2;
1896 int i;
1897 int32_t half = aCount/2;
1898 bool sorted = true;
1899 nsDisplayItem* prev = nullptr;
1900 for (i = 0; i < aCount; ++i) {
1901 nsDisplayItem* item = aList->RemoveBottom();
1902 (i < half ? &list1 : &list2)->AppendToTop(item);
1903 if (sorted && prev && !aCmp(prev, item, aClosure)) {
1904 sorted = false;
1906 prev = item;
1908 if (sorted) {
1909 aList->AppendToTop(&list1);
1910 aList->AppendToTop(&list2);
1911 return;
1914 Sort(&list1, half, aCmp, aClosure);
1915 Sort(&list2, aCount - half, aCmp, aClosure);
1917 for (i = 0; i < aCount; ++i) {
1918 if (list1.GetBottom() &&
1919 (!list2.GetBottom() ||
1920 aCmp(list1.GetBottom(), list2.GetBottom(), aClosure))) {
1921 aList->AppendToTop(list1.RemoveBottom());
1922 } else {
1923 aList->AppendToTop(list2.RemoveBottom());
1928 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, nsIDocument* aDoc) {
1929 nsIFrame* f = aItem->Frame();
1930 while (f) {
1931 nsPresContext* pc = f->PresContext();
1932 if (pc->Document() == aDoc) {
1933 return f->GetContent();
1935 f = nsLayoutUtils::GetCrossDocParentFrame(pc->PresShell()->GetRootFrame());
1937 return nullptr;
1940 static bool IsContentLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
1941 void* aClosure) {
1942 nsIContent* commonAncestor = static_cast<nsIContent*>(aClosure);
1943 // It's possible that the nsIContent for aItem1 or aItem2 is in a subdocument
1944 // of commonAncestor, because display items for subdocuments have been
1945 // mixed into the same list. Ensure that we're looking at content
1946 // in commonAncestor's document.
1947 nsIDocument* commonAncestorDoc = commonAncestor->OwnerDoc();
1948 nsIContent* content1 = FindContentInDocument(aItem1, commonAncestorDoc);
1949 nsIContent* content2 = FindContentInDocument(aItem2, commonAncestorDoc);
1950 if (!content1 || !content2) {
1951 NS_ERROR("Document trees are mixed up!");
1952 // Something weird going on
1953 return true;
1955 return nsLayoutUtils::CompareTreePosition(content1, content2, commonAncestor) <= 0;
1958 static bool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
1959 void* aClosure) {
1960 // Note that we can't just take the difference of the two
1961 // z-indices here, because that might overflow a 32-bit int.
1962 return aItem1->ZIndex() <= aItem2->ZIndex();
1965 void nsDisplayList::SortByZOrder(nsDisplayListBuilder* aBuilder,
1966 nsIContent* aCommonAncestor) {
1967 Sort(aBuilder, IsZOrderLEQ, aCommonAncestor);
1970 void nsDisplayList::SortByContentOrder(nsDisplayListBuilder* aBuilder,
1971 nsIContent* aCommonAncestor) {
1972 Sort(aBuilder, IsContentLEQ, aCommonAncestor);
1975 void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder,
1976 SortLEQ aCmp, void* aClosure) {
1977 ::Sort(this, Count(), aCmp, aClosure);
1980 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1981 : mFrame(aFrame)
1982 , mClip(aBuilder->ClipState().GetCurrentCombinedClip(aBuilder))
1983 #ifdef MOZ_DUMP_PAINTING
1984 , mPainted(false)
1985 #endif
1987 mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
1988 NS_ASSERTION(aBuilder->GetDirtyRect().width >= 0 ||
1989 !aBuilder->IsForPainting(), "dirty rect not set");
1990 // The dirty rect is for mCurrentFrame, so we have to use
1991 // mCurrentOffsetToReferenceFrame
1992 mVisibleRect = aBuilder->GetDirtyRect() +
1993 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
1996 /* static */ bool
1997 nsDisplayItem::ForceActiveLayers()
1999 static bool sForce = false;
2000 static bool sForceCached = false;
2002 if (!sForceCached) {
2003 Preferences::AddBoolVarCache(&sForce, "layers.force-active", false);
2004 sForceCached = true;
2007 return sForce;
2010 /* static */ int32_t
2011 nsDisplayItem::MaxActiveLayers()
2013 static int32_t sMaxLayers = false;
2014 static bool sMaxLayersCached = false;
2016 if (!sMaxLayersCached) {
2017 Preferences::AddIntVarCache(&sMaxLayers, "layers.max-active", -1);
2018 sMaxLayersCached = true;
2021 return sMaxLayers;
2024 int32_t
2025 nsDisplayItem::ZIndex() const
2027 if (!mFrame->IsPositioned() && !mFrame->IsFlexOrGridItem())
2028 return 0;
2030 const nsStylePosition* position = mFrame->StylePosition();
2031 if (position->mZIndex.GetUnit() == eStyleUnit_Integer)
2032 return position->mZIndex.GetIntValue();
2034 // sort the auto and 0 elements together
2035 return 0;
2038 bool
2039 nsDisplayItem::ComputeVisibility(nsDisplayListBuilder* aBuilder,
2040 nsRegion* aVisibleRegion)
2042 return !mVisibleRect.IsEmpty() &&
2043 !IsInvisibleInRect(aVisibleRegion->GetBounds());
2046 bool
2047 nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
2048 nsRegion* aVisibleRegion) {
2049 nsRect bounds = GetClippedBounds(aBuilder);
2051 nsRegion itemVisible;
2052 itemVisible.And(*aVisibleRegion, bounds);
2053 mVisibleRect = itemVisible.GetBounds();
2055 // When we recompute visibility within layers we don't need to
2056 // expand the visible region for content behind plugins (the plugin
2057 // is not in the layer).
2058 if (!ComputeVisibility(aBuilder, aVisibleRegion)) {
2059 mVisibleRect = nsRect();
2060 return false;
2063 nsRegion opaque = TreatAsOpaque(this, aBuilder);
2064 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque);
2065 return true;
2068 nsRect
2069 nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder)
2071 bool snap;
2072 nsRect r = GetBounds(aBuilder, &snap);
2073 return GetClip().ApplyNonRoundedIntersection(r);
2076 nsRect
2077 nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
2079 *aSnap = true;
2080 return mBounds;
2083 void
2084 nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
2085 nsRenderingContext* aCtx)
2087 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
2088 DrawTarget* drawTarget = aCtx->GetDrawTarget();
2089 Rect rect =
2090 NSRectToSnappedRect(mVisibleRect, appUnitsPerDevPixel, *drawTarget);
2091 drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor)));
2094 void
2095 nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream)
2097 aStream << " (rgba "
2098 << (int)NS_GET_R(mColor) << ","
2099 << (int)NS_GET_G(mColor) << ","
2100 << (int)NS_GET_B(mColor) << ","
2101 << (int)NS_GET_A(mColor) << ")";
2104 static void
2105 RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
2107 if (!aBuilder->IsInSubdocument() && !aBuilder->IsInTransform()) {
2108 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2109 nsRect borderBox(aFrame->GetOffsetTo(displayRoot), aFrame->GetSize());
2110 aBuilder->RegisterThemeGeometry(aFrame->StyleDisplay()->mAppearance,
2111 borderBox.ToNearestPixels(aFrame->PresContext()->AppUnitsPerDevPixel()));
2115 nsDisplayBackgroundImage::nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilder,
2116 nsIFrame* aFrame,
2117 uint32_t aLayer,
2118 const nsStyleBackground* aBackgroundStyle)
2119 : nsDisplayImageContainer(aBuilder, aFrame)
2120 , mBackgroundStyle(aBackgroundStyle)
2121 , mLayer(aLayer)
2123 MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
2125 mBounds = GetBoundsInternal(aBuilder);
2128 nsDisplayBackgroundImage::~nsDisplayBackgroundImage()
2130 #ifdef NS_BUILD_REFCNT_LOGGING
2131 MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
2132 #endif
2135 static nsStyleContext* GetBackgroundStyleContext(nsIFrame* aFrame)
2137 nsStyleContext *sc;
2138 if (!nsCSSRendering::FindBackground(aFrame, &sc)) {
2139 // We don't want to bail out if moz-appearance is set on a root
2140 // node. If it has a parent content node, bail because it's not
2141 // a root, other wise keep going in order to let the theme stuff
2142 // draw the background. The canvas really should be drawing the
2143 // bg, but there's no way to hook that up via css.
2144 if (!aFrame->StyleDisplay()->mAppearance) {
2145 return nullptr;
2148 nsIContent* content = aFrame->GetContent();
2149 if (!content || content->GetParent()) {
2150 return nullptr;
2153 sc = aFrame->StyleContext();
2155 return sc;
2158 /* static */ void
2159 SetBackgroundClipRegion(DisplayListClipState::AutoSaveRestore& aClipState,
2160 nsIFrame* aFrame, const nsPoint& aToReferenceFrame,
2161 const nsStyleBackground::Layer& aLayer,
2162 bool aWillPaintBorder)
2164 nsRect borderBox = nsRect(aToReferenceFrame, aFrame->GetSize());
2166 nsCSSRendering::BackgroundClipState clip;
2167 nsCSSRendering::GetBackgroundClip(aLayer, aFrame, *aFrame->StyleBorder(),
2168 borderBox, borderBox, aWillPaintBorder,
2169 aFrame->PresContext()->AppUnitsPerDevPixel(),
2170 &clip);
2172 if (clip.mHasAdditionalBGClipArea) {
2173 aClipState.ClipContentDescendants(clip.mAdditionalBGClipArea, clip.mBGClipArea,
2174 clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2175 } else {
2176 aClipState.ClipContentDescendants(clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
2181 /*static*/ bool
2182 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuilder,
2183 nsIFrame* aFrame,
2184 nsDisplayList* aList)
2186 nsStyleContext* bgSC = nullptr;
2187 const nsStyleBackground* bg = nullptr;
2188 nsPresContext* presContext = aFrame->PresContext();
2189 bool isThemed = aFrame->IsThemed();
2190 if (!isThemed) {
2191 bgSC = GetBackgroundStyleContext(aFrame);
2192 if (bgSC) {
2193 bg = bgSC->StyleBackground();
2197 bool drawBackgroundColor = false;
2198 nscolor color;
2199 if (!nsCSSRendering::IsCanvasFrame(aFrame) && bg) {
2200 bool drawBackgroundImage;
2201 color =
2202 nsCSSRendering::DetermineBackgroundColor(presContext, bgSC, aFrame,
2203 drawBackgroundImage, drawBackgroundColor);
2206 const nsStyleBorder* borderStyle = aFrame->StyleBorder();
2207 bool hasInsetShadow = borderStyle->mBoxShadow &&
2208 borderStyle->mBoxShadow->HasShadowWithInset(true);
2209 bool willPaintBorder = !isThemed && !hasInsetShadow &&
2210 borderStyle->HasBorder();
2212 nsPoint toRef = aBuilder->ToReferenceFrame(aFrame);
2214 // An auxiliary list is necessary in case we have background blending; if that
2215 // is the case, background items need to be wrapped by a blend container to
2216 // isolate blending to the background
2217 nsDisplayList bgItemList;
2218 // Even if we don't actually have a background color to paint, we may still need
2219 // to create an item for hit testing.
2220 if ((drawBackgroundColor && color != NS_RGBA(0,0,0,0)) ||
2221 aBuilder->IsForEventDelivery()) {
2222 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
2223 if (bg && !aBuilder->IsForEventDelivery()) {
2224 // Disable the will-paint-border optimization for background
2225 // colors with no border-radius. Enabling it for background colors
2226 // doesn't help much (there are no tiling issues) and clipping the
2227 // background breaks detection of the element's border-box being
2228 // opaque. For nonzero border-radius we still need it because we
2229 // want to inset the background if possible to avoid antialiasing
2230 // artifacts along the rounded corners.
2231 bool useWillPaintBorderOptimization = willPaintBorder &&
2232 nsLayoutUtils::HasNonZeroCorner(borderStyle->mBorderRadius);
2233 SetBackgroundClipRegion(clipState, aFrame, toRef,
2234 bg->BottomLayer(),
2235 useWillPaintBorderOptimization);
2237 bgItemList.AppendNewToTop(
2238 new (aBuilder) nsDisplayBackgroundColor(aBuilder, aFrame, bg,
2239 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0)));
2242 if (isThemed) {
2243 nsITheme* theme = presContext->GetTheme();
2244 if (theme->NeedToClearBackgroundBehindWidget(aFrame->StyleDisplay()->mAppearance)) {
2245 bgItemList.AppendNewToTop(
2246 new (aBuilder) nsDisplayClearBackground(aBuilder, aFrame));
2248 nsDisplayThemedBackground* bgItem =
2249 new (aBuilder) nsDisplayThemedBackground(aBuilder, aFrame);
2250 bgItemList.AppendNewToTop(bgItem);
2251 aList->AppendToTop(&bgItemList);
2252 return true;
2255 if (!bg) {
2256 aList->AppendToTop(&bgItemList);
2257 return false;
2260 bool needBlendContainer = false;
2262 // Passing bg == nullptr in this macro will result in one iteration with
2263 // i = 0.
2264 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
2265 if (bg->mLayers[i].mImage.IsEmpty()) {
2266 continue;
2269 if (bg->mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
2270 needBlendContainer = true;
2273 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
2274 if (!aBuilder->IsForEventDelivery()) {
2275 const nsStyleBackground::Layer& layer = bg->mLayers[i];
2276 SetBackgroundClipRegion(clipState, aFrame, toRef,
2277 layer, willPaintBorder);
2280 nsDisplayBackgroundImage* bgItem =
2281 new (aBuilder) nsDisplayBackgroundImage(aBuilder, aFrame, i, bg);
2282 bgItemList.AppendNewToTop(bgItem);
2285 if (needBlendContainer) {
2286 bgItemList.AppendNewToTop(
2287 new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, &bgItemList));
2290 aList->AppendToTop(&bgItemList);
2291 return false;
2294 // Check that the rounded border of aFrame, added to aToReferenceFrame,
2295 // intersects aRect. Assumes that the unrounded border has already
2296 // been checked for intersection.
2297 static bool
2298 RoundedBorderIntersectsRect(nsIFrame* aFrame,
2299 const nsPoint& aFrameToReferenceFrame,
2300 const nsRect& aTestRect)
2302 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize()).Intersects(aTestRect))
2303 return false;
2305 nscoord radii[8];
2306 return !aFrame->GetBorderRadii(radii) ||
2307 nsLayoutUtils::RoundedRectIntersectsRect(nsRect(aFrameToReferenceFrame,
2308 aFrame->GetSize()),
2309 radii, aTestRect);
2312 // Returns TRUE if aContainedRect is guaranteed to be contained in
2313 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
2314 // handled conservatively by returning FALSE in some situations where
2315 // a more thorough analysis could return TRUE.
2317 // See also RoundedRectIntersectsRect.
2318 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
2319 const nscoord aRadii[8],
2320 const nsRect& aContainedRect) {
2321 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii, aContainedRect);
2322 return rgn.Contains(aContainedRect);
2325 bool
2326 nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
2327 const nsRect& aClipRect,
2328 gfxRect* aDestRect)
2330 if (!mBackgroundStyle)
2331 return false;
2333 if (mBackgroundStyle->mLayers.Length() != 1)
2334 return false;
2336 nsPresContext* presContext = mFrame->PresContext();
2337 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2338 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
2339 const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
2341 if (layer.mAttachment != NS_STYLE_BG_ATTACHMENT_FIXED)
2342 return false;
2344 nsBackgroundLayerState state =
2345 nsCSSRendering::PrepareBackgroundLayer(presContext, mFrame, flags,
2346 borderArea, aClipRect, layer);
2347 nsImageRenderer* imageRenderer = &state.mImageRenderer;
2348 // We only care about images here, not gradients.
2349 if (!imageRenderer->IsRasterImage())
2350 return false;
2352 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
2353 *aDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
2355 return true;
2358 bool
2359 nsDisplayBackgroundImage::ShouldFixToViewport(LayerManager* aManager)
2361 // APZ doesn't (yet) know how to scroll the visible region for these type of
2362 // items, so don't layerize them if it's enabled.
2363 if (nsLayoutUtils::UsesAsyncScrolling() ||
2364 (aManager && aManager->ShouldAvoidComponentAlphaLayers())) {
2365 return false;
2368 // Put background-attachment:fixed background images in their own
2369 // compositing layer, unless we have APZ enabled
2370 return mBackgroundStyle->mLayers[mLayer].mAttachment == NS_STYLE_BG_ATTACHMENT_FIXED &&
2371 !mBackgroundStyle->mLayers[mLayer].mImage.IsEmpty();
2374 bool
2375 nsDisplayBackgroundImage::TryOptimizeToImageLayer(LayerManager* aManager,
2376 nsDisplayListBuilder* aBuilder)
2378 if (!mBackgroundStyle)
2379 return false;
2381 nsPresContext* presContext = mFrame->PresContext();
2382 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2383 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
2384 const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
2386 if (layer.mClip != NS_STYLE_BG_CLIP_BORDER) {
2387 return false;
2389 nscoord radii[8];
2390 if (mFrame->GetBorderRadii(radii)) {
2391 return false;
2394 nsBackgroundLayerState state =
2395 nsCSSRendering::PrepareBackgroundLayer(presContext, mFrame, flags,
2396 borderArea, borderArea, layer);
2397 nsImageRenderer* imageRenderer = &state.mImageRenderer;
2398 // We only care about images here, not gradients.
2399 if (!imageRenderer->IsRasterImage())
2400 return false;
2402 nsRefPtr<ImageContainer> imageContainer = imageRenderer->GetContainer(aManager);
2403 // Image is not ready to be made into a layer yet
2404 if (!imageContainer)
2405 return false;
2407 // We currently can't handle tiled or partial backgrounds.
2408 if (!state.mDestArea.IsEqualEdges(state.mFillArea)) {
2409 return false;
2412 // XXX Ignoring state.mAnchor. ImageLayer drawing snaps mDestArea edges to
2413 // layer pixel boundaries. This should be OK for now.
2415 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
2416 mDestRect = nsLayoutUtils::RectToGfxRect(state.mDestArea, appUnitsPerDevPixel);
2417 mImageContainer = imageContainer;
2419 // Ok, we can turn this into a layer if needed.
2420 return true;
2423 already_AddRefed<ImageContainer>
2424 nsDisplayBackgroundImage::GetContainer(LayerManager* aManager,
2425 nsDisplayListBuilder *aBuilder)
2427 if (!TryOptimizeToImageLayer(aManager, aBuilder)) {
2428 return nullptr;
2431 nsRefPtr<ImageContainer> container = mImageContainer;
2433 return container.forget();
2436 LayerState
2437 nsDisplayBackgroundImage::GetLayerState(nsDisplayListBuilder* aBuilder,
2438 LayerManager* aManager,
2439 const ContainerLayerParameters& aParameters)
2441 bool animated = false;
2442 if (mBackgroundStyle) {
2443 const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
2444 const nsStyleImage* image = &layer.mImage;
2445 if (image->GetType() == eStyleImageType_Image) {
2446 imgIRequest* imgreq = image->GetImageData();
2447 nsCOMPtr<imgIContainer> image;
2448 if (NS_SUCCEEDED(imgreq->GetImage(getter_AddRefs(image))) && image) {
2449 if (NS_FAILED(image->GetAnimated(&animated))) {
2450 animated = false;
2456 if (!animated ||
2457 !nsLayoutUtils::AnimatedImageLayersEnabled()) {
2458 if (!aManager->IsCompositingCheap() ||
2459 !nsLayoutUtils::GPUImageScalingEnabled()) {
2460 return LAYER_NONE;
2464 if (!TryOptimizeToImageLayer(aManager, aBuilder)) {
2465 return LAYER_NONE;
2468 if (!animated) {
2469 mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize();
2470 NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!");
2472 gfxRect destRect = mDestRect;
2474 destRect.width *= aParameters.mXScale;
2475 destRect.height *= aParameters.mYScale;
2477 // Calculate the scaling factor for the frame.
2478 gfxSize scale = gfxSize(destRect.width / imageSize.width, destRect.height / imageSize.height);
2480 // If we are not scaling at all, no point in separating this into a layer.
2481 if (scale.width == 1.0f && scale.height == 1.0f) {
2482 return LAYER_NONE;
2485 // If the target size is pretty small, no point in using a layer.
2486 if (destRect.width * destRect.height < 64 * 64) {
2487 return LAYER_NONE;
2491 return LAYER_ACTIVE;
2494 already_AddRefed<Layer>
2495 nsDisplayBackgroundImage::BuildLayer(nsDisplayListBuilder* aBuilder,
2496 LayerManager* aManager,
2497 const ContainerLayerParameters& aParameters)
2499 nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
2500 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
2501 if (!layer) {
2502 layer = aManager->CreateImageLayer();
2503 if (!layer)
2504 return nullptr;
2506 layer->SetContainer(mImageContainer);
2507 ConfigureLayer(layer, aParameters.mOffset);
2508 return layer.forget();
2511 void
2512 nsDisplayBackgroundImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset)
2514 aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
2516 mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize();
2517 NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!");
2518 if (imageSize.width > 0 && imageSize.height > 0) {
2519 // We're actually using the ImageContainer. Let our frame know that it
2520 // should consider itself to have painted successfully.
2521 nsDisplayBackgroundGeometry::UpdateDrawResult(this, DrawResult::SUCCESS);
2524 gfxPoint p = mDestRect.TopLeft() + aOffset;
2525 Matrix transform = Matrix::Translation(p.x, p.y);
2526 transform.PreScale(mDestRect.width / imageSize.width,
2527 mDestRect.height / imageSize.height);
2528 aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
2531 void
2532 nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
2533 const nsRect& aRect,
2534 HitTestState* aState,
2535 nsTArray<nsIFrame*> *aOutFrames)
2537 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
2538 aOutFrames->AppendElement(mFrame);
2542 bool
2543 nsDisplayBackgroundImage::ComputeVisibility(nsDisplayListBuilder* aBuilder,
2544 nsRegion* aVisibleRegion)
2546 if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion)) {
2547 return false;
2550 // Return false if the background was propagated away from this
2551 // frame. We don't want this display item to show up and confuse
2552 // anything.
2553 return mBackgroundStyle;
2556 /* static */ nsRegion
2557 nsDisplayBackgroundImage::GetInsideClipRegion(nsDisplayItem* aItem,
2558 nsPresContext* aPresContext,
2559 uint8_t aClip, const nsRect& aRect,
2560 bool* aSnap)
2562 nsRegion result;
2563 if (aRect.IsEmpty())
2564 return result;
2566 nsIFrame *frame = aItem->Frame();
2568 nsRect clipRect;
2569 if (frame->GetType() == nsGkAtoms::canvasFrame) {
2570 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame);
2571 clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame();
2572 } else {
2573 switch (aClip) {
2574 case NS_STYLE_BG_CLIP_BORDER:
2575 clipRect = nsRect(aItem->ToReferenceFrame(), frame->GetSize());
2576 break;
2577 case NS_STYLE_BG_CLIP_PADDING:
2578 clipRect = frame->GetPaddingRect() - frame->GetPosition() + aItem->ToReferenceFrame();
2579 break;
2580 case NS_STYLE_BG_CLIP_CONTENT:
2581 clipRect = frame->GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
2582 break;
2583 default:
2584 NS_NOTREACHED("Unknown clip type");
2585 return result;
2589 return clipRect.Intersect(aRect);
2592 nsRegion
2593 nsDisplayBackgroundImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2594 bool* aSnap) {
2595 nsRegion result;
2596 *aSnap = false;
2598 if (!mBackgroundStyle)
2599 return result;
2602 *aSnap = true;
2604 // For NS_STYLE_BOX_DECORATION_BREAK_SLICE, don't try to optimize here, since
2605 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
2606 // which expects frames to be sent to it in content order, not reverse
2607 // content order which we'll produce here.
2608 // Of course, if there's only one frame in the flow, it doesn't matter.
2609 if (mFrame->StyleBorder()->mBoxDecorationBreak ==
2610 NS_STYLE_BOX_DECORATION_BREAK_CLONE ||
2611 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
2612 const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer];
2613 if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL) {
2614 nsPresContext* presContext = mFrame->PresContext();
2615 result = GetInsideClipRegion(this, presContext, layer.mClip, mBounds, aSnap);
2619 return result;
2622 bool
2623 nsDisplayBackgroundImage::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) {
2624 if (!mBackgroundStyle) {
2625 *aColor = NS_RGBA(0,0,0,0);
2626 return true;
2628 return false;
2631 nsRect
2632 nsDisplayBackgroundImage::GetPositioningArea()
2634 if (!mBackgroundStyle) {
2635 return nsRect();
2637 nsIFrame* attachedToFrame;
2638 return nsCSSRendering::ComputeBackgroundPositioningArea(
2639 mFrame->PresContext(), mFrame,
2640 nsRect(ToReferenceFrame(), mFrame->GetSize()),
2641 mBackgroundStyle->mLayers[mLayer],
2642 &attachedToFrame) + ToReferenceFrame();
2645 bool
2646 nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
2648 if (!mBackgroundStyle)
2649 return false;
2651 nscoord radii[8];
2652 if (mFrame->GetBorderRadii(radii)) {
2653 // A change in the size of the positioning area might change the position
2654 // of the rounded corners.
2655 return true;
2658 const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
2659 if (layer.RenderingMightDependOnPositioningAreaSizeChange()) {
2660 return true;
2662 return false;
2665 static void CheckForBorderItem(nsDisplayItem *aItem, uint32_t& aFlags)
2667 nsDisplayItem* nextItem = aItem->GetAbove();
2668 while (nextItem && nextItem->GetType() == nsDisplayItem::TYPE_BACKGROUND) {
2669 nextItem = nextItem->GetAbove();
2671 if (nextItem &&
2672 nextItem->Frame() == aItem->Frame() &&
2673 nextItem->GetType() == nsDisplayItem::TYPE_BORDER) {
2674 aFlags |= nsCSSRendering::PAINTBG_WILL_PAINT_BORDER;
2678 void
2679 nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
2680 nsRenderingContext* aCtx) {
2681 PaintInternal(aBuilder, aCtx, mVisibleRect, &mBounds);
2684 void
2685 nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
2686 nsRenderingContext* aCtx, const nsRect& aBounds,
2687 nsRect* aClipRect) {
2688 nsPoint offset = ToReferenceFrame();
2689 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2690 CheckForBorderItem(this, flags);
2692 DrawResult result =
2693 nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame,
2694 aBounds,
2695 nsRect(offset, mFrame->GetSize()),
2696 flags, aClipRect, mLayer);
2698 nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
2701 void nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
2702 const nsDisplayItemGeometry* aGeometry,
2703 nsRegion* aInvalidRegion)
2705 if (!mBackgroundStyle) {
2706 return;
2709 const nsDisplayBackgroundGeometry* geometry = static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
2711 bool snap;
2712 nsRect bounds = GetBounds(aBuilder, &snap);
2713 nsRect positioningArea = GetPositioningArea();
2714 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
2715 (positioningArea.Size() != geometry->mPositioningArea.Size() &&
2716 RenderingMightDependOnPositioningAreaSizeChange())) {
2717 // Positioning area changed in a way that could cause everything to change,
2718 // so invalidate everything (both old and new painting areas).
2719 aInvalidRegion->Or(bounds, geometry->mBounds);
2721 if (positioningArea.Size() != geometry->mPositioningArea.Size()) {
2722 NotifyRenderingChanged();
2724 return;
2726 if (aBuilder->ShouldSyncDecodeImages()) {
2727 const nsStyleImage& image = mBackgroundStyle->mLayers[mLayer].mImage;
2728 if (image.GetType() == eStyleImageType_Image &&
2729 geometry->ShouldInvalidateToSyncDecodeImages()) {
2730 aInvalidRegion->Or(*aInvalidRegion, bounds);
2732 NotifyRenderingChanged();
2735 if (!bounds.IsEqualInterior(geometry->mBounds)) {
2736 // Positioning area is unchanged, so invalidate just the change in the
2737 // painting area.
2738 aInvalidRegion->Xor(bounds, geometry->mBounds);
2740 NotifyRenderingChanged();
2744 nsRect
2745 nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
2746 *aSnap = true;
2747 return mBounds;
2750 nsRect
2751 nsDisplayBackgroundImage::GetBoundsInternal(nsDisplayListBuilder* aBuilder) {
2752 nsPresContext* presContext = mFrame->PresContext();
2754 if (!mBackgroundStyle) {
2755 return nsRect();
2758 nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize());
2759 nsRect clipRect = borderBox;
2760 if (mFrame->GetType() == nsGkAtoms::canvasFrame) {
2761 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
2762 clipRect = frame->CanvasArea() + ToReferenceFrame();
2764 const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer];
2765 return nsCSSRendering::GetBackgroundLayerRect(presContext, mFrame,
2766 borderBox, clipRect, layer,
2767 aBuilder->GetBackgroundPaintFlags());
2770 uint32_t
2771 nsDisplayBackgroundImage::GetPerFrameKey()
2773 return (mLayer << nsDisplayItem::TYPE_BITS) |
2774 nsDisplayItem::GetPerFrameKey();
2777 nsDisplayThemedBackground::nsDisplayThemedBackground(nsDisplayListBuilder* aBuilder,
2778 nsIFrame* aFrame)
2779 : nsDisplayItem(aBuilder, aFrame)
2781 MOZ_COUNT_CTOR(nsDisplayThemedBackground);
2783 const nsStyleDisplay* disp = mFrame->StyleDisplay();
2784 mAppearance = disp->mAppearance;
2785 mFrame->IsThemed(disp, &mThemeTransparency);
2787 // Perform necessary RegisterThemeGeometry
2788 switch (disp->mAppearance) {
2789 case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
2790 case NS_THEME_TOOLBAR:
2791 case NS_THEME_TOOLTIP:
2792 case NS_THEME_WINDOW_TITLEBAR:
2793 case NS_THEME_WINDOW_BUTTON_BOX:
2794 case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON:
2795 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
2796 case NS_THEME_MAC_VIBRANCY_LIGHT:
2797 case NS_THEME_MAC_VIBRANCY_DARK:
2798 RegisterThemeGeometry(aBuilder, aFrame);
2799 break;
2800 case NS_THEME_WIN_BORDERLESS_GLASS:
2801 case NS_THEME_WIN_GLASS:
2802 aBuilder->SetGlassDisplayItem(this);
2803 break;
2806 mBounds = GetBoundsInternal();
2809 nsDisplayThemedBackground::~nsDisplayThemedBackground()
2811 #ifdef NS_BUILD_REFCNT_LOGGING
2812 MOZ_COUNT_DTOR(nsDisplayThemedBackground);
2813 #endif
2816 void
2817 nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream)
2819 aStream << " (themed, appearance:" << (int)mAppearance << ")";
2822 void
2823 nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
2824 const nsRect& aRect,
2825 HitTestState* aState,
2826 nsTArray<nsIFrame*> *aOutFrames)
2828 // Assume that any point in our border rect is a hit.
2829 if (nsRect(ToReferenceFrame(), mFrame->GetSize()).Intersects(aRect)) {
2830 aOutFrames->AppendElement(mFrame);
2834 nsRegion
2835 nsDisplayThemedBackground::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2836 bool* aSnap) {
2837 nsRegion result;
2838 *aSnap = false;
2840 if (mThemeTransparency == nsITheme::eOpaque) {
2841 result = nsRect(ToReferenceFrame(), mFrame->GetSize());
2843 return result;
2846 bool
2847 nsDisplayThemedBackground::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) {
2848 if (mAppearance == NS_THEME_WIN_BORDERLESS_GLASS ||
2849 mAppearance == NS_THEME_WIN_GLASS) {
2850 *aColor = NS_RGBA(0,0,0,0);
2851 return true;
2853 return false;
2856 bool
2857 nsDisplayThemedBackground::ProvidesFontSmoothingBackgroundColor(nsDisplayListBuilder* aBuilder,
2858 nscolor* aColor)
2860 nsITheme* theme = mFrame->PresContext()->GetTheme();
2861 return theme->WidgetProvidesFontSmoothingBackgroundColor(mFrame, mAppearance, aColor);
2864 nsRect
2865 nsDisplayThemedBackground::GetPositioningArea()
2867 return nsRect(ToReferenceFrame(), mFrame->GetSize());
2870 void
2871 nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
2872 nsRenderingContext* aCtx)
2874 PaintInternal(aBuilder, aCtx, mVisibleRect, nullptr);
2878 void
2879 nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
2880 nsRenderingContext* aCtx, const nsRect& aBounds,
2881 nsRect* aClipRect)
2883 // XXXzw this ignores aClipRect.
2884 nsPresContext* presContext = mFrame->PresContext();
2885 nsITheme *theme = presContext->GetTheme();
2886 nsRect borderArea(ToReferenceFrame(), mFrame->GetSize());
2887 nsRect drawing(borderArea);
2888 theme->GetWidgetOverflow(presContext->DeviceContext(), mFrame, mAppearance,
2889 &drawing);
2890 drawing.IntersectRect(drawing, aBounds);
2891 theme->DrawWidgetBackground(aCtx, mFrame, mAppearance, borderArea, drawing);
2894 bool nsDisplayThemedBackground::IsWindowActive()
2896 EventStates docState = mFrame->GetContent()->OwnerDoc()->GetDocumentState();
2897 return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
2900 void nsDisplayThemedBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
2901 const nsDisplayItemGeometry* aGeometry,
2902 nsRegion* aInvalidRegion)
2904 const nsDisplayThemedBackgroundGeometry* geometry = static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
2906 bool snap;
2907 nsRect bounds = GetBounds(aBuilder, &snap);
2908 nsRect positioningArea = GetPositioningArea();
2909 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
2910 // Invalidate everything (both old and new painting areas).
2911 aInvalidRegion->Or(bounds, geometry->mBounds);
2912 return;
2914 if (!bounds.IsEqualInterior(geometry->mBounds)) {
2915 // Positioning area is unchanged, so invalidate just the change in the
2916 // painting area.
2917 aInvalidRegion->Xor(bounds, geometry->mBounds);
2919 nsITheme* theme = mFrame->PresContext()->GetTheme();
2920 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
2921 IsWindowActive() != geometry->mWindowIsActive) {
2922 aInvalidRegion->Or(*aInvalidRegion, bounds);
2926 nsRect
2927 nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
2928 *aSnap = true;
2929 return mBounds;
2932 nsRect
2933 nsDisplayThemedBackground::GetBoundsInternal() {
2934 nsPresContext* presContext = mFrame->PresContext();
2936 nsRect r(nsPoint(0,0), mFrame->GetSize());
2937 presContext->GetTheme()->
2938 GetWidgetOverflow(presContext->DeviceContext(), mFrame,
2939 mFrame->StyleDisplay()->mAppearance, &r);
2940 #ifdef XP_MACOSX
2941 // Bug 748219
2942 r.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
2943 #endif
2945 return r + ToReferenceFrame();
2948 void
2949 nsDisplayBackgroundColor::ApplyOpacity(nsDisplayListBuilder* aBuilder,
2950 float aOpacity,
2951 const DisplayItemClip* aClip)
2953 NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
2954 mColor.a = mColor.a * aOpacity;
2955 if (aClip) {
2956 IntersectClip(aBuilder, *aClip);
2960 bool
2961 nsDisplayBackgroundColor::CanApplyOpacity() const
2963 return true;
2966 void
2967 nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
2968 nsRenderingContext* aCtx)
2970 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
2972 if (mColor == NS_RGBA(0, 0, 0, 0)) {
2973 return;
2976 nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize());
2978 Rect rect = NSRectToSnappedRect(borderBox,
2979 mFrame->PresContext()->AppUnitsPerDevPixel(),
2980 aDrawTarget);
2981 ColorPattern color(ToDeviceColor(mColor));
2982 aDrawTarget.FillRect(rect, color);
2985 nsRegion
2986 nsDisplayBackgroundColor::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2987 bool* aSnap)
2989 if (mColor.a != 1) {
2990 return nsRegion();
2993 if (!mBackgroundStyle)
2994 return nsRegion();
2996 *aSnap = true;
2998 const nsStyleBackground::Layer& bottomLayer = mBackgroundStyle->BottomLayer();
2999 nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize());
3000 nsPresContext* presContext = mFrame->PresContext();
3001 return nsDisplayBackgroundImage::GetInsideClipRegion(this, presContext, bottomLayer.mClip, borderBox, aSnap);
3004 bool
3005 nsDisplayBackgroundColor::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor)
3007 *aColor = NS_RGBA_FROM_GFXRGBA(mColor);
3008 return true;
3011 void
3012 nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
3013 const nsRect& aRect,
3014 HitTestState* aState,
3015 nsTArray<nsIFrame*> *aOutFrames)
3017 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3018 // aRect doesn't intersect our border-radius curve.
3019 return;
3022 aOutFrames->AppendElement(mFrame);
3025 void
3026 nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
3028 aStream << " (rgba " << mColor.r << "," << mColor.g << ","
3029 << mColor.b << "," << mColor.a << ")";
3032 already_AddRefed<Layer>
3033 nsDisplayClearBackground::BuildLayer(nsDisplayListBuilder* aBuilder,
3034 LayerManager* aManager,
3035 const ContainerLayerParameters& aParameters)
3037 nsRefPtr<ColorLayer> layer = static_cast<ColorLayer*>
3038 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
3039 if (!layer) {
3040 layer = aManager->CreateColorLayer();
3041 if (!layer)
3042 return nullptr;
3044 layer->SetColor(NS_RGBA(0, 0, 0, 0));
3045 layer->SetMixBlendMode(gfx::CompositionOp::OP_SOURCE);
3047 bool snap;
3048 nsRect bounds = GetBounds(aBuilder, &snap);
3049 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
3050 layer->SetBounds(bounds.ToNearestPixels(appUnitsPerDevPixel)); // XXX Do we need to respect the parent layer's scale here?
3052 return layer.forget();
3055 nsRect
3056 nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
3057 *aSnap = false;
3058 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
3061 void
3062 nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder,
3063 nsRenderingContext* aCtx) {
3064 // TODO join outlines together
3065 nsPoint offset = ToReferenceFrame();
3066 nsCSSRendering::PaintOutline(mFrame->PresContext(), *aCtx, mFrame,
3067 mVisibleRect,
3068 nsRect(offset, mFrame->GetSize()),
3069 mFrame->StyleContext());
3072 bool
3073 nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect)
3075 const nsStyleOutline* outline = mFrame->StyleOutline();
3076 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
3077 if (borderBox.Contains(aRect) &&
3078 !nsLayoutUtils::HasNonZeroCorner(outline->mOutlineRadius)) {
3079 if (outline->mOutlineOffset >= 0) {
3080 // aRect is entirely inside the border-rect, and the outline isn't
3081 // rendered inside the border-rect, so the outline is not visible.
3082 return true;
3086 return false;
3089 void
3090 nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
3091 const nsRect& aRect,
3092 HitTestState* aState,
3093 nsTArray<nsIFrame*> *aOutFrames)
3095 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
3096 // aRect doesn't intersect our border-radius curve.
3097 return;
3100 aOutFrames->AppendElement(mFrame);
3103 void
3104 nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
3105 nsIFrame* aFrame)
3107 NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) == aBuilder->FindReferenceFrameFor(mFrame),
3108 "Reference frame mismatch");
3109 if (aBuilder->IsInsidePointerEventsNoneDoc()) {
3110 // Somewhere up the parent document chain is a subdocument with pointer-
3111 // events:none set on it (and without a mozpasspointerevents).
3112 return;
3114 if (!aFrame->GetParent()) {
3115 MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::viewportFrame);
3116 nsSubDocumentFrame* subdoc = static_cast<nsSubDocumentFrame*>(
3117 nsLayoutUtils::GetCrossDocParentFrame(aFrame));
3118 if (subdoc && subdoc->PassPointerEventsToChildren()) {
3119 // If this viewport frame is for a subdocument with
3120 // mozpasspointerevents, then we don't want to add the viewport itself
3121 // to the event regions. Instead we want to add only subframes.
3122 return;
3125 uint8_t pointerEvents = aFrame->StyleVisibility()->GetEffectivePointerEvents(aFrame);
3126 if (pointerEvents == NS_STYLE_POINTER_EVENTS_NONE) {
3127 return;
3129 if (!aFrame->StyleVisibility()->IsVisible()) {
3130 return;
3132 // XXX handle other pointerEvents values for SVG
3134 // XXX Do something clever here for the common case where the border box
3135 // is obviously entirely inside mHitRegion.
3136 nsRect borderBox;
3137 if (nsLayoutUtils::GetScrollableFrameFor(aFrame)) {
3138 // If the frame is content of a scrollframe, then we need to pick up the
3139 // area corresponding to the overflow rect as well. Otherwise the parts of
3140 // the overflow that are not occupied by descendants get skipped and the
3141 // APZ code sends touch events to the content underneath instead.
3142 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
3143 borderBox = aFrame->GetScrollableOverflowRect();
3144 } else {
3145 borderBox = nsRect(nsPoint(0, 0), aFrame->GetSize());
3147 borderBox += aBuilder->ToReferenceFrame(aFrame);
3149 const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder);
3150 bool borderBoxHasRoundedCorners =
3151 nsLayoutUtils::HasNonZeroCorner(aFrame->StyleBorder()->mBorderRadius);
3152 if (clip) {
3153 borderBox = clip->ApplyNonRoundedIntersection(borderBox);
3154 if (clip->GetRoundedRectCount() > 0) {
3155 borderBoxHasRoundedCorners = true;
3158 if (borderBoxHasRoundedCorners ||
3159 (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
3160 mMaybeHitRegion.Or(mMaybeHitRegion, borderBox);
3161 } else {
3162 mHitRegion.Or(mHitRegion, borderBox);
3164 if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
3165 mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox);
3169 void
3170 nsDisplayLayerEventRegions::AddInactiveScrollPort(const nsRect& aRect)
3172 mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aRect);
3175 void
3176 nsDisplayLayerEventRegions::WriteDebugInfo(std::stringstream& aStream)
3178 if (!mHitRegion.IsEmpty()) {
3179 AppendToString(aStream, mHitRegion, " (hitRegion ", ")");
3181 if (!mMaybeHitRegion.IsEmpty()) {
3182 AppendToString(aStream, mMaybeHitRegion, " (maybeHitRegion ", ")");
3184 if (!mDispatchToContentHitRegion.IsEmpty()) {
3185 AppendToString(aStream, mDispatchToContentHitRegion, " (dispatchToContentRegion ", ")");
3189 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
3190 nsIFrame* aCaretFrame)
3191 : nsDisplayItem(aBuilder, aCaretFrame)
3192 , mCaret(aBuilder->GetCaret())
3193 , mBounds(aBuilder->GetCaretRect() + ToReferenceFrame())
3195 MOZ_COUNT_CTOR(nsDisplayCaret);
3198 #ifdef NS_BUILD_REFCNT_LOGGING
3199 nsDisplayCaret::~nsDisplayCaret()
3201 MOZ_COUNT_DTOR(nsDisplayCaret);
3203 #endif
3205 nsRect
3206 nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
3208 *aSnap = true;
3209 // The caret returns a rect in the coordinates of mFrame.
3210 return mBounds;
3213 void
3214 nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder,
3215 nsRenderingContext* aCtx) {
3216 // Note: Because we exist, we know that the caret is visible, so we don't
3217 // need to check for the caret's visibility.
3218 mCaret->PaintCaret(aBuilder, *aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
3221 bool
3222 nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect)
3224 nsRect paddingRect = mFrame->GetPaddingRect() - mFrame->GetPosition() +
3225 ToReferenceFrame();
3226 const nsStyleBorder *styleBorder;
3227 if (paddingRect.Contains(aRect) &&
3228 !(styleBorder = mFrame->StyleBorder())->IsBorderImageLoaded() &&
3229 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
3230 // aRect is entirely inside the content rect, and no part
3231 // of the border is rendered inside the content rect, so we are not
3232 // visible
3233 // Skip this if there's a border-image (which draws a background
3234 // too) or if there is a border-radius (which makes the border draw
3235 // further in).
3236 return true;
3239 return false;
3242 nsDisplayItemGeometry*
3243 nsDisplayBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder)
3245 return new nsDisplayBorderGeometry(this, aBuilder);
3248 void
3249 nsDisplayBorder::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
3250 const nsDisplayItemGeometry* aGeometry,
3251 nsRegion* aInvalidRegion)
3253 const nsDisplayBorderGeometry* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
3254 bool snap;
3255 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
3256 !geometry->mContentRect.IsEqualInterior(GetContentRect())) {
3257 // We can probably get away with only invalidating the difference
3258 // between the border and padding rects, but the XUL ui at least
3259 // is apparently painting a background with this?
3260 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
3264 void
3265 nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder,
3266 nsRenderingContext* aCtx) {
3267 nsPoint offset = ToReferenceFrame();
3268 nsCSSRendering::PaintBorder(mFrame->PresContext(), *aCtx, mFrame,
3269 mVisibleRect,
3270 nsRect(offset, mFrame->GetSize()),
3271 mFrame->StyleContext(),
3272 mFrame->GetSkipSides());
3275 nsRect
3276 nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
3278 *aSnap = true;
3279 return CalculateBounds(*mFrame->StyleBorder());
3282 nsRect
3283 nsDisplayBorder::CalculateBounds(const nsStyleBorder& aStyleBorder)
3285 nsRect borderBounds(ToReferenceFrame(), mFrame->GetSize());
3286 if (aStyleBorder.IsBorderImageLoaded()) {
3287 borderBounds.Inflate(aStyleBorder.GetImageOutset());
3288 return borderBounds;
3289 } else {
3290 nsMargin border = aStyleBorder.GetComputedBorder();
3291 nsRect result;
3292 if (border.top > 0) {
3293 result = nsRect(borderBounds.X(), borderBounds.Y(), borderBounds.Width(), border.top);
3295 if (border.right > 0) {
3296 result.UnionRect(result, nsRect(borderBounds.XMost() - border.right, borderBounds.Y(), border.right, borderBounds.Height()));
3298 if (border.bottom > 0) {
3299 result.UnionRect(result, nsRect(borderBounds.X(), borderBounds.YMost() - border.bottom, borderBounds.Width(), border.bottom));
3301 if (border.left > 0) {
3302 result.UnionRect(result, nsRect(borderBounds.X(), borderBounds.Y(), border.left, borderBounds.Height()));
3305 return result;
3309 // Given a region, compute a conservative approximation to it as a list
3310 // of rectangles that aren't vertically adjacent (i.e., vertically
3311 // adjacent or overlapping rectangles are combined).
3312 // Right now this is only approximate, some vertically overlapping rectangles
3313 // aren't guaranteed to be combined.
3314 static void
3315 ComputeDisjointRectangles(const nsRegion& aRegion,
3316 nsTArray<nsRect>* aRects) {
3317 nscoord accumulationMargin = nsPresContext::CSSPixelsToAppUnits(25);
3318 nsRect accumulated;
3319 nsRegionRectIterator iter(aRegion);
3320 while (true) {
3321 const nsRect* r = iter.Next();
3322 if (r && !accumulated.IsEmpty() &&
3323 accumulated.YMost() >= r->y - accumulationMargin) {
3324 accumulated.UnionRect(accumulated, *r);
3325 continue;
3328 if (!accumulated.IsEmpty()) {
3329 aRects->AppendElement(accumulated);
3330 accumulated.SetEmpty();
3333 if (!r)
3334 break;
3336 accumulated = *r;
3340 void
3341 nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
3342 nsRenderingContext* aCtx) {
3343 nsPoint offset = ToReferenceFrame();
3344 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
3345 nsPresContext* presContext = mFrame->PresContext();
3346 nsAutoTArray<nsRect,10> rects;
3347 ComputeDisjointRectangles(mVisibleRegion, &rects);
3349 PROFILER_LABEL("nsDisplayBoxShadowOuter", "Paint",
3350 js::ProfileEntry::Category::GRAPHICS);
3352 for (uint32_t i = 0; i < rects.Length(); ++i) {
3353 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame,
3354 borderRect, rects[i], mOpacity);
3358 nsRect
3359 nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
3360 *aSnap = false;
3361 return mBounds;
3364 nsRect
3365 nsDisplayBoxShadowOuter::GetBoundsInternal() {
3366 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
3367 ToReferenceFrame();
3370 bool
3371 nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect)
3373 nsPoint origin = ToReferenceFrame();
3374 nsRect frameRect(origin, mFrame->GetSize());
3375 if (!frameRect.Contains(aRect))
3376 return false;
3378 // the visible region is entirely inside the border-rect, and box shadows
3379 // never render within the border-rect (unless there's a border radius).
3380 nscoord twipsRadii[8];
3381 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
3382 if (!hasBorderRadii)
3383 return true;
3385 return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
3388 bool
3389 nsDisplayBoxShadowOuter::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3390 nsRegion* aVisibleRegion) {
3391 if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion)) {
3392 return false;
3395 // Store the actual visible region
3396 mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
3397 return true;
3400 void
3401 nsDisplayBoxShadowOuter::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
3402 const nsDisplayItemGeometry* aGeometry,
3403 nsRegion* aInvalidRegion)
3405 const nsDisplayBoxShadowOuterGeometry* geometry =
3406 static_cast<const nsDisplayBoxShadowOuterGeometry*>(aGeometry);
3407 bool snap;
3408 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
3409 !geometry->mBorderRect.IsEqualInterior(GetBorderRect()) ||
3410 mOpacity != geometry->mOpacity) {
3411 nsRegion oldShadow, newShadow;
3412 nscoord dontCare[8];
3413 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
3414 if (hasBorderRadius) {
3415 // If we have rounded corners then we need to invalidate the frame area
3416 // too since we paint into it.
3417 oldShadow = geometry->mBounds;
3418 newShadow = GetBounds(aBuilder, &snap);
3419 } else {
3420 oldShadow = oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
3421 newShadow = newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
3423 aInvalidRegion->Or(oldShadow, newShadow);
3428 void
3429 nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
3430 nsRenderingContext* aCtx) {
3431 nsPoint offset = ToReferenceFrame();
3432 nsRect borderRect = nsRect(offset, mFrame->GetSize());
3433 nsPresContext* presContext = mFrame->PresContext();
3434 nsAutoTArray<nsRect,10> rects;
3435 ComputeDisjointRectangles(mVisibleRegion, &rects);
3437 PROFILER_LABEL("nsDisplayBoxShadowInner", "Paint",
3438 js::ProfileEntry::Category::GRAPHICS);
3440 DrawTarget* drawTarget = aCtx->GetDrawTarget();
3441 gfxContext* gfx = aCtx->ThebesContext();
3442 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
3444 for (uint32_t i = 0; i < rects.Length(); ++i) {
3445 gfx->Save();
3446 gfx->Clip(NSRectToSnappedRect(rects[i], appUnitsPerDevPixel, *drawTarget));
3447 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame,
3448 borderRect, rects[i]);
3449 gfx->Restore();
3453 bool
3454 nsDisplayBoxShadowInner::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3455 nsRegion* aVisibleRegion) {
3456 if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion)) {
3457 return false;
3460 // Store the actual visible region
3461 mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
3462 return true;
3465 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
3466 nsIFrame* aFrame, nsDisplayList* aList)
3467 : nsDisplayItem(aBuilder, aFrame)
3468 , mOverrideZIndex(0)
3469 , mHasZIndexOverride(false)
3471 MOZ_COUNT_CTOR(nsDisplayWrapList);
3473 mBaseVisibleRect = mVisibleRect;
3475 mList.AppendToTop(aList);
3476 UpdateBounds(aBuilder);
3478 if (!aFrame || !aFrame->IsTransformed()) {
3479 return;
3482 // If the frame is a preserve-3d parent, then we will create transforms
3483 // inside this list afterwards (see WrapPreserve3DList in nsFrame.cpp).
3484 // In this case we will always be outside of the transform, so share
3485 // our parents reference frame.
3486 if (aFrame->Preserves3DChildren()) {
3487 mReferenceFrame =
3488 aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame));
3489 mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
3490 } else {
3491 // If we're a transformed frame, then we need to find out if we're inside
3492 // the nsDisplayTransform or outside of it. Frames inside the transform
3493 // need mReferenceFrame == mFrame, outside needs the next ancestor
3494 // reference frame.
3495 // If we're inside the transform, then the nsDisplayItem constructor
3496 // will have done the right thing.
3497 // If we're outside the transform, then we should have only one child
3498 // (since nsDisplayTransform wraps all actual content), and that child
3499 // will have the correct reference frame set (since nsDisplayTransform
3500 // handles this explictly).
3502 // Preserve-3d can cause us to have multiple nsDisplayTransform
3503 // children.
3504 nsDisplayItem *i = mList.GetBottom();
3505 if (i && (!i->GetAbove() || i->GetType() == TYPE_TRANSFORM) &&
3506 i->Frame() == mFrame) {
3507 mReferenceFrame = i->ReferenceFrame();
3508 mToReferenceFrame = i->ToReferenceFrame();
3511 mVisibleRect = aBuilder->GetDirtyRect() +
3512 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
3515 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
3516 nsIFrame* aFrame, nsDisplayItem* aItem)
3517 : nsDisplayItem(aBuilder, aFrame)
3518 , mOverrideZIndex(0)
3519 , mHasZIndexOverride(false)
3521 MOZ_COUNT_CTOR(nsDisplayWrapList);
3523 mBaseVisibleRect = mVisibleRect;
3525 mList.AppendToTop(aItem);
3526 UpdateBounds(aBuilder);
3528 if (!aFrame || !aFrame->IsTransformed()) {
3529 return;
3532 if (aFrame->Preserves3DChildren()) {
3533 mReferenceFrame =
3534 aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame));
3535 mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
3536 } else {
3537 // See the previous nsDisplayWrapList constructor
3538 if (aItem->Frame() == aFrame) {
3539 mReferenceFrame = aItem->ReferenceFrame();
3540 mToReferenceFrame = aItem->ToReferenceFrame();
3543 mVisibleRect = aBuilder->GetDirtyRect() +
3544 aBuilder->GetCurrentFrameOffsetToReferenceFrame();
3547 nsDisplayWrapList::~nsDisplayWrapList() {
3548 mList.DeleteAll();
3550 MOZ_COUNT_DTOR(nsDisplayWrapList);
3553 void
3554 nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
3555 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
3556 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
3559 nsRect
3560 nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
3561 *aSnap = false;
3562 return mBounds;
3565 bool
3566 nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3567 nsRegion* aVisibleRegion) {
3568 // Convert the passed in visible region to our appunits.
3569 nsRegion visibleRegion;
3570 // mVisibleRect has been clipped to GetClippedBounds
3571 visibleRegion.And(*aVisibleRegion, mVisibleRect);
3572 nsRegion originalVisibleRegion = visibleRegion;
3574 bool retval =
3575 mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion,
3576 mVisibleRect);
3578 nsRegion removed;
3579 // removed = originalVisibleRegion - visibleRegion
3580 removed.Sub(originalVisibleRegion, visibleRegion);
3581 // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications
3582 // SubtractFromVisibleRegion does)
3583 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed);
3585 return retval;
3588 nsRegion
3589 nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
3590 bool* aSnap) {
3591 *aSnap = false;
3592 nsRegion result;
3593 if (mList.IsOpaque()) {
3594 // Everything within GetBounds that's visible is opaque.
3595 result = GetBounds(aBuilder, aSnap);
3597 return result;
3600 bool nsDisplayWrapList::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) {
3601 // We could try to do something but let's conservatively just return false.
3602 return false;
3605 void nsDisplayWrapList::Paint(nsDisplayListBuilder* aBuilder,
3606 nsRenderingContext* aCtx) {
3607 NS_ERROR("nsDisplayWrapList should have been flattened away for painting");
3611 * Returns true if all descendant display items can be placed in the same
3612 * PaintedLayer --- GetLayerState returns LAYER_INACTIVE or LAYER_NONE,
3613 * and they all have the expected animated geometry root.
3615 static LayerState
3616 RequiredLayerStateForChildren(nsDisplayListBuilder* aBuilder,
3617 LayerManager* aManager,
3618 const ContainerLayerParameters& aParameters,
3619 const nsDisplayList& aList,
3620 nsIFrame* aExpectedAnimatedGeometryRootForChildren)
3622 LayerState result = LAYER_INACTIVE;
3623 for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
3624 if (result == LAYER_INACTIVE &&
3625 nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder, aManager) !=
3626 aExpectedAnimatedGeometryRootForChildren) {
3627 result = LAYER_ACTIVE;
3630 LayerState state = i->GetLayerState(aBuilder, aManager, aParameters);
3631 if ((state == LAYER_ACTIVE || state == LAYER_ACTIVE_FORCE) &&
3632 state > result) {
3633 result = state;
3635 if (state == LAYER_ACTIVE_EMPTY && state > result) {
3636 result = LAYER_ACTIVE_FORCE;
3638 if (state == LAYER_NONE) {
3639 nsDisplayList* list = i->GetSameCoordinateSystemChildren();
3640 if (list) {
3641 LayerState childState =
3642 RequiredLayerStateForChildren(aBuilder, aManager, aParameters, *list,
3643 aExpectedAnimatedGeometryRootForChildren);
3644 if (childState > result) {
3645 result = childState;
3650 return result;
3653 nsRect nsDisplayWrapList::GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder)
3655 nsRect bounds;
3656 for (nsDisplayItem* i = mList.GetBottom(); i; i = i->GetAbove()) {
3657 bounds.UnionRect(bounds, i->GetComponentAlphaBounds(aBuilder));
3659 return bounds;
3662 void
3663 nsDisplayWrapList::SetVisibleRect(const nsRect& aRect)
3665 mVisibleRect = aRect;
3668 void
3669 nsDisplayWrapList::SetReferenceFrame(const nsIFrame* aFrame)
3671 mReferenceFrame = aFrame;
3672 mToReferenceFrame = mFrame->GetOffsetToCrossDoc(mReferenceFrame);
3675 static nsresult
3676 WrapDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3677 nsDisplayList* aList, nsDisplayWrapper* aWrapper) {
3678 if (!aList->GetTop())
3679 return NS_OK;
3680 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
3681 if (!item)
3682 return NS_ERROR_OUT_OF_MEMORY;
3683 // aList was emptied
3684 aList->AppendToTop(item);
3685 return NS_OK;
3688 static nsresult
3689 WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
3690 nsDisplayList* aList, nsDisplayWrapper* aWrapper) {
3691 nsDisplayList newList;
3692 nsDisplayItem* item;
3693 while ((item = aList->RemoveBottom())) {
3694 item = aWrapper->WrapItem(aBuilder, item);
3695 if (!item)
3696 return NS_ERROR_OUT_OF_MEMORY;
3697 newList.AppendToTop(item);
3699 // aList was emptied
3700 aList->AppendToTop(&newList);
3701 return NS_OK;
3704 nsresult nsDisplayWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
3705 nsIFrame* aFrame, const nsDisplayListSet& aIn, const nsDisplayListSet& aOut)
3707 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
3708 NS_ENSURE_SUCCESS(rv, rv);
3710 if (&aOut == &aIn)
3711 return NS_OK;
3712 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
3713 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
3714 aOut.Floats()->AppendToTop(aIn.Floats());
3715 aOut.Content()->AppendToTop(aIn.Content());
3716 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
3717 aOut.Outlines()->AppendToTop(aIn.Outlines());
3718 return NS_OK;
3721 nsresult nsDisplayWrapper::WrapListsInPlace(nsDisplayListBuilder* aBuilder,
3722 nsIFrame* aFrame, const nsDisplayListSet& aLists)
3724 nsresult rv;
3725 if (WrapBorderBackground()) {
3726 // Our border-backgrounds are in-flow
3727 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
3728 NS_ENSURE_SUCCESS(rv, rv);
3730 // Our block border-backgrounds are in-flow
3731 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
3732 NS_ENSURE_SUCCESS(rv, rv);
3733 // The floats are not in flow
3734 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
3735 NS_ENSURE_SUCCESS(rv, rv);
3736 // Our child content is in flow
3737 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
3738 NS_ENSURE_SUCCESS(rv, rv);
3739 // The positioned descendants may not be in-flow
3740 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
3741 NS_ENSURE_SUCCESS(rv, rv);
3742 // The outlines may not be in-flow
3743 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
3746 nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
3747 nsIFrame* aFrame, nsDisplayList* aList)
3748 : nsDisplayWrapList(aBuilder, aFrame, aList)
3749 , mOpacity(aFrame->StyleDisplay()->mOpacity) {
3750 MOZ_COUNT_CTOR(nsDisplayOpacity);
3753 #ifdef NS_BUILD_REFCNT_LOGGING
3754 nsDisplayOpacity::~nsDisplayOpacity() {
3755 MOZ_COUNT_DTOR(nsDisplayOpacity);
3757 #endif
3759 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
3760 bool* aSnap) {
3761 *aSnap = false;
3762 // The only time where mOpacity == 1.0 should be when we have will-change.
3763 // We could report this as opaque then but when the will-change value starts
3764 // animating the element would become non opaque and could cause repaints.
3765 return nsRegion();
3768 // nsDisplayOpacity uses layers for rendering
3769 already_AddRefed<Layer>
3770 nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
3771 LayerManager* aManager,
3772 const ContainerLayerParameters& aContainerParameters) {
3773 nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
3774 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
3775 aContainerParameters, nullptr);
3776 if (!container)
3777 return nullptr;
3779 container->SetOpacity(mOpacity);
3780 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
3781 this, mFrame,
3782 eCSSProperty_opacity);
3783 return container.forget();
3787 * This doesn't take into account layer scaling --- the layer may be
3788 * rendered at a higher (or lower) resolution, affecting the retained layer
3789 * size --- but this should be good enough.
3791 static bool
3792 IsItemTooSmallForActiveLayer(nsDisplayItem* aItem)
3794 nsIntRect visibleDevPixels = aItem->GetVisibleRect().ToOutsidePixels(
3795 aItem->Frame()->PresContext()->AppUnitsPerDevPixel());
3796 static const int MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS = 16;
3797 return visibleDevPixels.Size() <
3798 nsIntSize(MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS, MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS);
3801 bool
3802 nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder)
3804 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, eCSSProperty_opacity) &&
3805 !IsItemTooSmallForActiveLayer(this))
3806 return true;
3807 if (mFrame->GetContent()) {
3808 if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(),
3809 eCSSProperty_opacity)) {
3810 return true;
3813 return false;
3816 void
3817 nsDisplayOpacity::ApplyOpacity(nsDisplayListBuilder* aBuilder,
3818 float aOpacity,
3819 const DisplayItemClip* aClip)
3821 NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
3822 mOpacity = mOpacity * aOpacity;
3823 if (aClip) {
3824 IntersectClip(aBuilder, *aClip);
3828 bool
3829 nsDisplayOpacity::CanApplyOpacity() const
3831 return true;
3834 bool
3835 nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder* aBuilder)
3837 if (NeedsActiveLayer(aBuilder))
3838 return false;
3840 nsDisplayItem* child = mList.GetBottom();
3841 // Only try folding our opacity down if we have at most three children
3842 // that don't overlap and can all apply the opacity to themselves.
3843 if (!child) {
3844 return false;
3846 struct {
3847 nsDisplayItem* item;
3848 nsRect bounds;
3849 } children[3];
3850 bool snap;
3851 uint32_t numChildren = 0;
3852 for (; numChildren < ArrayLength(children) && child; numChildren++, child = child->GetAbove()) {
3853 if (!child->CanApplyOpacity()) {
3854 return false;
3856 children[numChildren].item = child;
3857 children[numChildren].bounds = child->GetBounds(aBuilder, &snap);
3859 if (child) {
3860 // we have a fourth (or more) child
3861 return false;
3864 for (uint32_t i = 0; i < numChildren; i++) {
3865 for (uint32_t j = i+1; j < numChildren; j++) {
3866 if (children[i].bounds.Intersects(children[j].bounds)) {
3867 return false;
3872 for (uint32_t i = 0; i < numChildren; i++) {
3873 children[i].item->ApplyOpacity(aBuilder, mOpacity, mClip);
3875 return true;
3878 nsDisplayItem::LayerState
3879 nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder,
3880 LayerManager* aManager,
3881 const ContainerLayerParameters& aParameters) {
3882 if (NeedsActiveLayer(aBuilder))
3883 return LAYER_ACTIVE;
3885 return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList,
3886 nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder, aManager));
3889 bool
3890 nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3891 nsRegion* aVisibleRegion) {
3892 // Our children are translucent so we should not allow them to subtract
3893 // area from aVisibleRegion. We do need to find out what is visible under
3894 // our children in the temporary compositing buffer, because if our children
3895 // paint our entire bounds opaquely then we don't need an alpha channel in
3896 // the temporary compositing buffer.
3897 nsRect bounds = GetClippedBounds(aBuilder);
3898 nsRegion visibleUnderChildren;
3899 visibleUnderChildren.And(*aVisibleRegion, bounds);
3900 return
3901 nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren);
3904 bool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
3905 if (aItem->GetType() != TYPE_OPACITY)
3906 return false;
3907 // items for the same content element should be merged into a single
3908 // compositing group
3909 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
3910 if (aItem->Frame()->GetContent() != mFrame->GetContent())
3911 return false;
3912 if (aItem->GetClip() != GetClip())
3913 return false;
3914 MergeFromTrackingMergedFrames(static_cast<nsDisplayOpacity*>(aItem));
3915 return true;
3918 void
3919 nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream)
3921 aStream << " (opacity " << mOpacity << ")";
3924 nsDisplayMixBlendMode::nsDisplayMixBlendMode(nsDisplayListBuilder* aBuilder,
3925 nsIFrame* aFrame, nsDisplayList* aList,
3926 uint32_t aFlags)
3927 : nsDisplayWrapList(aBuilder, aFrame, aList) {
3928 MOZ_COUNT_CTOR(nsDisplayMixBlendMode);
3931 #ifdef NS_BUILD_REFCNT_LOGGING
3932 nsDisplayMixBlendMode::~nsDisplayMixBlendMode() {
3933 MOZ_COUNT_DTOR(nsDisplayMixBlendMode);
3935 #endif
3937 nsRegion nsDisplayMixBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
3938 bool* aSnap) {
3939 *aSnap = false;
3940 // We are never considered opaque
3941 return nsRegion();
3944 LayerState
3945 nsDisplayMixBlendMode::GetLayerState(nsDisplayListBuilder* aBuilder,
3946 LayerManager* aManager,
3947 const ContainerLayerParameters& aParameters)
3949 gfxContext::GraphicsOperator op = nsCSSRendering::GetGFXBlendMode(mFrame->StyleDisplay()->mMixBlendMode);
3950 if (aManager->SupportsMixBlendMode(gfx::CompositionOpForOp(op))) {
3951 return LAYER_ACTIVE;
3953 return LAYER_INACTIVE;
3956 // nsDisplayMixBlendMode uses layers for rendering
3957 already_AddRefed<Layer>
3958 nsDisplayMixBlendMode::BuildLayer(nsDisplayListBuilder* aBuilder,
3959 LayerManager* aManager,
3960 const ContainerLayerParameters& aContainerParameters) {
3961 ContainerLayerParameters newContainerParameters = aContainerParameters;
3962 newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
3964 nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
3965 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
3966 newContainerParameters, nullptr);
3967 if (!container) {
3968 return nullptr;
3971 container->DeprecatedSetMixBlendMode(nsCSSRendering::GetGFXBlendMode(mFrame->StyleDisplay()->mMixBlendMode));
3973 return container.forget();
3976 bool nsDisplayMixBlendMode::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3977 nsRegion* aVisibleRegion) {
3978 // Our children are need their backdrop so we should not allow them to subtract
3979 // area from aVisibleRegion. We do need to find out what is visible under
3980 // our children in the temporary compositing buffer, because if our children
3981 // paint our entire bounds opaquely then we don't need an alpha channel in
3982 // the temporary compositing buffer.
3983 nsRect bounds = GetClippedBounds(aBuilder);
3984 nsRegion visibleUnderChildren;
3985 visibleUnderChildren.And(*aVisibleRegion, bounds);
3986 return nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren);
3989 bool nsDisplayMixBlendMode::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
3990 if (aItem->GetType() != TYPE_MIX_BLEND_MODE)
3991 return false;
3992 // items for the same content element should be merged into a single
3993 // compositing group
3994 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
3995 if (aItem->Frame()->GetContent() != mFrame->GetContent())
3996 return false;
3997 if (aItem->GetClip() != GetClip())
3998 return false;
3999 MergeFromTrackingMergedFrames(static_cast<nsDisplayMixBlendMode*>(aItem));
4000 return true;
4003 nsDisplayBlendContainer::nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder,
4004 nsIFrame* aFrame, nsDisplayList* aList,
4005 BlendModeSet& aContainedBlendModes)
4006 : nsDisplayWrapList(aBuilder, aFrame, aList)
4007 , mContainedBlendModes(aContainedBlendModes)
4008 , mCanBeActive(true)
4010 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
4013 nsDisplayBlendContainer::nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder,
4014 nsIFrame* aFrame, nsDisplayList* aList)
4015 : nsDisplayWrapList(aBuilder, aFrame, aList)
4016 , mCanBeActive(false)
4018 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
4021 #ifdef NS_BUILD_REFCNT_LOGGING
4022 nsDisplayBlendContainer::~nsDisplayBlendContainer() {
4023 MOZ_COUNT_DTOR(nsDisplayBlendContainer);
4025 #endif
4027 // nsDisplayBlendContainer uses layers for rendering
4028 already_AddRefed<Layer>
4029 nsDisplayBlendContainer::BuildLayer(nsDisplayListBuilder* aBuilder,
4030 LayerManager* aManager,
4031 const ContainerLayerParameters& aContainerParameters) {
4032 // turn off anti-aliasing in the parent stacking context because it changes
4033 // how the group is initialized.
4034 ContainerLayerParameters newContainerParameters = aContainerParameters;
4035 newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
4037 nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
4038 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
4039 newContainerParameters, nullptr);
4040 if (!container) {
4041 return nullptr;
4044 container->SetForceIsolatedGroup(true);
4045 return container.forget();
4048 bool nsDisplayBlendContainer::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
4049 if (aItem->GetType() != TYPE_BLEND_CONTAINER)
4050 return false;
4051 // items for the same content element should be merged into a single
4052 // compositing group
4053 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
4054 if (aItem->Frame()->GetContent() != mFrame->GetContent())
4055 return false;
4056 if (aItem->GetClip() != GetClip())
4057 return false;
4058 MergeFromTrackingMergedFrames(static_cast<nsDisplayBlendContainer*>(aItem));
4059 return true;
4062 nsDisplayOwnLayer::nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder,
4063 nsIFrame* aFrame, nsDisplayList* aList,
4064 uint32_t aFlags, ViewID aScrollTarget)
4065 : nsDisplayWrapList(aBuilder, aFrame, aList)
4066 , mFlags(aFlags)
4067 , mScrollTarget(aScrollTarget) {
4068 MOZ_COUNT_CTOR(nsDisplayOwnLayer);
4071 #ifdef NS_BUILD_REFCNT_LOGGING
4072 nsDisplayOwnLayer::~nsDisplayOwnLayer() {
4073 MOZ_COUNT_DTOR(nsDisplayOwnLayer);
4075 #endif
4077 // nsDisplayOpacity uses layers for rendering
4078 already_AddRefed<Layer>
4079 nsDisplayOwnLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
4080 LayerManager* aManager,
4081 const ContainerLayerParameters& aContainerParameters) {
4082 nsRefPtr<ContainerLayer> layer = aManager->GetLayerBuilder()->
4083 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
4084 aContainerParameters, nullptr);
4085 if (mFlags & VERTICAL_SCROLLBAR) {
4086 layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::VERTICAL);
4088 if (mFlags & HORIZONTAL_SCROLLBAR) {
4089 layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::HORIZONTAL);
4092 if (mFlags & GENERATE_SUBDOC_INVALIDATIONS) {
4093 mFrame->PresContext()->SetNotifySubDocInvalidationData(layer);
4095 return layer.forget();
4098 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
4099 nsIFrame* aFrame, nsDisplayList* aList,
4100 uint32_t aFlags)
4101 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aFlags)
4102 , mScrollParentId(aBuilder->GetCurrentScrollParentId())
4104 MOZ_COUNT_CTOR(nsDisplaySubDocument);
4105 mForceDispatchToContentRegion =
4106 aBuilder->IsBuildingLayerEventRegions() &&
4107 nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(aFrame->PresContext()->PresShell());
4110 #ifdef NS_BUILD_REFCNT_LOGGING
4111 nsDisplaySubDocument::~nsDisplaySubDocument() {
4112 MOZ_COUNT_DTOR(nsDisplaySubDocument);
4114 #endif
4116 already_AddRefed<Layer>
4117 nsDisplaySubDocument::BuildLayer(nsDisplayListBuilder* aBuilder,
4118 LayerManager* aManager,
4119 const ContainerLayerParameters& aContainerParameters) {
4120 nsPresContext* presContext = mFrame->PresContext();
4121 nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame();
4122 ContainerLayerParameters params = aContainerParameters;
4123 if ((mFlags & GENERATE_SCROLLABLE_LAYER) &&
4124 rootScrollFrame->GetContent() &&
4125 nsLayoutUtils::GetCriticalDisplayPort(rootScrollFrame->GetContent(), nullptr)) {
4126 params.mInLowPrecisionDisplayPort = true;
4129 nsRefPtr<Layer> layer = nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, params);
4130 layer->AsContainerLayer()->SetEventRegionsOverride(mForceDispatchToContentRegion
4131 ? EventRegionsOverride::ForceDispatchToContent
4132 : EventRegionsOverride::NoOverride);
4133 return layer.forget();
4136 UniquePtr<FrameMetrics>
4137 nsDisplaySubDocument::ComputeFrameMetrics(Layer* aLayer,
4138 const ContainerLayerParameters& aContainerParameters)
4140 if (!(mFlags & GENERATE_SCROLLABLE_LAYER)) {
4141 return UniquePtr<FrameMetrics>(nullptr);
4144 nsPresContext* presContext = mFrame->PresContext();
4145 nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame();
4146 bool isRootContentDocument = presContext->IsRootContentDocument();
4147 nsIPresShell* presShell = presContext->PresShell();
4148 ContainerLayerParameters params(
4149 aContainerParameters.mXScale * presShell->GetXResolution(),
4150 aContainerParameters.mYScale * presShell->GetYResolution(),
4151 nsIntPoint(), aContainerParameters);
4152 if ((mFlags & GENERATE_SCROLLABLE_LAYER) &&
4153 rootScrollFrame->GetContent() &&
4154 nsLayoutUtils::GetCriticalDisplayPort(rootScrollFrame->GetContent(), nullptr)) {
4155 params.mInLowPrecisionDisplayPort = true;
4158 nsRect viewport = mFrame->GetRect() -
4159 mFrame->GetPosition() +
4160 mFrame->GetOffsetToCrossDoc(ReferenceFrame());
4162 return MakeUnique<FrameMetrics>(
4163 nsDisplayScrollLayer::ComputeFrameMetrics(mFrame, rootScrollFrame, ReferenceFrame(),
4164 aLayer, mScrollParentId, viewport,
4165 isRootContentDocument, params));
4168 static bool
4169 UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
4170 nsRect* aDisplayPort = nullptr)
4172 return aBuilder->IsPaintingToWindow() &&
4173 nsLayoutUtils::ViewportHasDisplayPort(aFrame->PresContext(), aDisplayPort);
4176 nsRect
4177 nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
4179 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
4181 if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) {
4182 *aSnap = false;
4183 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
4186 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
4189 bool
4190 nsDisplaySubDocument::ComputeVisibility(nsDisplayListBuilder* aBuilder,
4191 nsRegion* aVisibleRegion)
4193 nsRect displayport;
4194 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame, &displayport);
4196 if (!(mFlags & GENERATE_SCROLLABLE_LAYER) || !usingDisplayPort) {
4197 return nsDisplayWrapList::ComputeVisibility(aBuilder, aVisibleRegion);
4200 nsRegion childVisibleRegion;
4201 // The visible region for the children may be much bigger than the hole we
4202 // are viewing the children from, so that the compositor process has enough
4203 // content to asynchronously pan while content is being refreshed.
4204 childVisibleRegion = displayport + mFrame->GetOffsetToCrossDoc(ReferenceFrame());
4206 nsRect boundedRect =
4207 childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder));
4208 bool visible = mList.ComputeVisibilityForSublist(
4209 aBuilder, &childVisibleRegion, boundedRect,
4210 usingDisplayPort ? mFrame : nullptr);
4212 // If APZ is enabled then don't allow this computation to influence
4213 // aVisibleRegion, on the assumption that the layer can be asynchronously
4214 // scrolled so we'll definitely need all the content under it.
4215 if (!nsLayoutUtils::UsesAsyncScrolling()) {
4216 bool snap;
4217 nsRect bounds = GetBounds(aBuilder, &snap);
4218 nsRegion removed;
4219 removed.Sub(bounds, childVisibleRegion);
4221 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed);
4224 return visible;
4227 bool
4228 nsDisplaySubDocument::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
4230 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
4232 if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) {
4233 return true;
4236 return nsDisplayOwnLayer::ShouldBuildLayerEvenIfInvisible(aBuilder);
4239 nsRegion
4240 nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap)
4242 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
4244 if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) {
4245 *aSnap = false;
4246 return nsRegion();
4249 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
4252 nsDisplayResolution::nsDisplayResolution(nsDisplayListBuilder* aBuilder,
4253 nsIFrame* aFrame, nsDisplayList* aList,
4254 uint32_t aFlags)
4255 : nsDisplaySubDocument(aBuilder, aFrame, aList, aFlags) {
4256 MOZ_COUNT_CTOR(nsDisplayResolution);
4259 #ifdef NS_BUILD_REFCNT_LOGGING
4260 nsDisplayResolution::~nsDisplayResolution() {
4261 MOZ_COUNT_DTOR(nsDisplayResolution);
4263 #endif
4265 already_AddRefed<Layer>
4266 nsDisplayResolution::BuildLayer(nsDisplayListBuilder* aBuilder,
4267 LayerManager* aManager,
4268 const ContainerLayerParameters& aContainerParameters) {
4269 nsIPresShell* presShell = mFrame->PresContext()->PresShell();
4270 ContainerLayerParameters containerParameters(
4271 presShell->GetXResolution(), presShell->GetYResolution(), nsIntPoint(),
4272 aContainerParameters);
4274 nsRefPtr<Layer> layer = nsDisplaySubDocument::BuildLayer(
4275 aBuilder, aManager, containerParameters);
4276 layer->SetPostScale(1.0f / presShell->GetXResolution(),
4277 1.0f / presShell->GetYResolution());
4278 layer->AsContainerLayer()->SetScaleToResolution(
4279 presShell->ScaleToResolution(), presShell->GetXResolution());
4280 return layer.forget();
4283 nsDisplayStickyPosition::nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder,
4284 nsIFrame* aFrame,
4285 nsDisplayList* aList)
4286 : nsDisplayOwnLayer(aBuilder, aFrame, aList)
4288 MOZ_COUNT_CTOR(nsDisplayStickyPosition);
4291 #ifdef NS_BUILD_REFCNT_LOGGING
4292 nsDisplayStickyPosition::~nsDisplayStickyPosition() {
4293 MOZ_COUNT_DTOR(nsDisplayStickyPosition);
4295 #endif
4297 already_AddRefed<Layer>
4298 nsDisplayStickyPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
4299 LayerManager* aManager,
4300 const ContainerLayerParameters& aContainerParameters) {
4301 nsRefPtr<Layer> layer =
4302 nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
4304 StickyScrollContainer* stickyScrollContainer = StickyScrollContainer::
4305 GetStickyScrollContainerForFrame(mFrame);
4306 if (!stickyScrollContainer) {
4307 return layer.forget();
4310 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
4311 nsPresContext* presContext = scrollFrame->PresContext();
4313 // Sticky position frames whose scroll frame is the root scroll frame are
4314 // reflowed into the scroll-port size if one has been set.
4315 nsSize scrollFrameSize = scrollFrame->GetSize();
4316 if (scrollFrame == presContext->PresShell()->GetRootScrollFrame() &&
4317 presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
4318 scrollFrameSize = presContext->PresShell()->
4319 GetScrollPositionClampingScrollPortSize();
4322 nsLayoutUtils::SetFixedPositionLayerData(layer, scrollFrame,
4323 nsRect(scrollFrame->GetOffsetToCrossDoc(ReferenceFrame()), scrollFrameSize),
4324 mFrame, presContext, aContainerParameters);
4326 ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(
4327 stickyScrollContainer->ScrollFrame()->GetScrolledFrame()->GetContent());
4329 float factor = presContext->AppUnitsPerDevPixel();
4330 nsRect outer;
4331 nsRect inner;
4332 stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
4333 LayerRect stickyOuter(NSAppUnitsToFloatPixels(outer.x, factor) *
4334 aContainerParameters.mXScale,
4335 NSAppUnitsToFloatPixels(outer.y, factor) *
4336 aContainerParameters.mYScale,
4337 NSAppUnitsToFloatPixels(outer.width, factor) *
4338 aContainerParameters.mXScale,
4339 NSAppUnitsToFloatPixels(outer.height, factor) *
4340 aContainerParameters.mYScale);
4341 LayerRect stickyInner(NSAppUnitsToFloatPixels(inner.x, factor) *
4342 aContainerParameters.mXScale,
4343 NSAppUnitsToFloatPixels(inner.y, factor) *
4344 aContainerParameters.mYScale,
4345 NSAppUnitsToFloatPixels(inner.width, factor) *
4346 aContainerParameters.mXScale,
4347 NSAppUnitsToFloatPixels(inner.height, factor) *
4348 aContainerParameters.mYScale);
4349 layer->SetStickyPositionData(scrollId, stickyOuter, stickyInner);
4351 return layer.forget();
4354 bool nsDisplayStickyPosition::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
4355 if (aItem->GetType() != TYPE_STICKY_POSITION)
4356 return false;
4357 // Items with the same fixed position frame can be merged.
4358 nsDisplayStickyPosition* other = static_cast<nsDisplayStickyPosition*>(aItem);
4359 if (other->mFrame != mFrame)
4360 return false;
4361 if (aItem->GetClip() != GetClip())
4362 return false;
4363 MergeFromTrackingMergedFrames(other);
4364 return true;
4367 nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder,
4368 nsDisplayList* aList,
4369 nsIFrame* aForFrame,
4370 nsIFrame* aScrolledFrame,
4371 nsIFrame* aScrollFrame)
4372 : nsDisplayWrapList(aBuilder, aForFrame, aList)
4373 , mScrollFrame(aScrollFrame)
4374 , mScrolledFrame(aScrolledFrame)
4375 , mScrollParentId(aBuilder->GetCurrentScrollParentId())
4376 , mDisplayPortContentsOpaque(false)
4378 #ifdef NS_BUILD_REFCNT_LOGGING
4379 MOZ_COUNT_CTOR(nsDisplayScrollLayer);
4380 #endif
4382 NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(),
4383 "Need a child frame with content");
4386 nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder,
4387 nsDisplayItem* aItem,
4388 nsIFrame* aForFrame,
4389 nsIFrame* aScrolledFrame,
4390 nsIFrame* aScrollFrame)
4391 : nsDisplayWrapList(aBuilder, aForFrame, aItem)
4392 , mScrollFrame(aScrollFrame)
4393 , mScrolledFrame(aScrolledFrame)
4394 , mScrollParentId(aBuilder->GetCurrentScrollParentId())
4395 , mDisplayPortContentsOpaque(false)
4397 #ifdef NS_BUILD_REFCNT_LOGGING
4398 MOZ_COUNT_CTOR(nsDisplayScrollLayer);
4399 #endif
4401 NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(),
4402 "Need a child frame with content");
4405 nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder,
4406 nsIFrame* aForFrame,
4407 nsIFrame* aScrolledFrame,
4408 nsIFrame* aScrollFrame)
4409 : nsDisplayWrapList(aBuilder, aForFrame)
4410 , mScrollFrame(aScrollFrame)
4411 , mScrolledFrame(aScrolledFrame)
4412 , mScrollParentId(aBuilder->GetCurrentScrollParentId())
4413 , mDisplayPortContentsOpaque(false)
4415 #ifdef NS_BUILD_REFCNT_LOGGING
4416 MOZ_COUNT_CTOR(nsDisplayScrollLayer);
4417 #endif
4419 NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(),
4420 "Need a child frame with content");
4423 #ifdef NS_BUILD_REFCNT_LOGGING
4424 nsDisplayScrollLayer::~nsDisplayScrollLayer()
4426 MOZ_COUNT_DTOR(nsDisplayScrollLayer);
4428 #endif
4430 nsRect
4431 nsDisplayScrollLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
4433 nsIScrollableFrame* sf = do_QueryFrame(mScrollFrame);
4434 if (sf) {
4435 *aSnap = false;
4436 return sf->GetScrollPortRect() + aBuilder->ToReferenceFrame(mScrollFrame);
4438 return nsDisplayWrapList::GetBounds(aBuilder, aSnap);
4441 nsRect
4442 nsDisplayScrollLayer::GetScrolledContentRectToDraw(nsDisplayListBuilder* aBuilder,
4443 nsRect* aDisplayPort)
4445 if (aDisplayPort) {
4446 // The visible region for the children may be much bigger than the hole we
4447 // are viewing the children from, so that the compositor process has enough
4448 // content to asynchronously pan while content is being refreshed.
4449 // XXX mScrollFrame seems wrong here; we should add the offset of the
4450 // scrollport
4451 return *aDisplayPort + mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
4453 bool snap;
4454 return GetBounds(aBuilder, &snap);
4457 already_AddRefed<Layer>
4458 nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
4459 LayerManager* aManager,
4460 const ContainerLayerParameters& aContainerParameters)
4462 ContainerLayerParameters params = aContainerParameters;
4463 if (mScrolledFrame->GetContent() &&
4464 nsLayoutUtils::GetCriticalDisplayPort(mScrolledFrame->GetContent(), nullptr)) {
4465 params.mInLowPrecisionDisplayPort = true;
4468 if (mList.IsOpaque()) {
4469 nsRect displayport;
4470 bool usingDisplayport =
4471 nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), &displayport);
4472 mDisplayPortContentsOpaque = mList.GetBounds(aBuilder).Contains(
4473 GetScrolledContentRectToDraw(aBuilder, usingDisplayport ? &displayport : nullptr));
4474 } else {
4475 mDisplayPortContentsOpaque = false;
4478 return aManager->GetLayerBuilder()->
4479 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
4480 params, nullptr);
4483 UniquePtr<FrameMetrics>
4484 nsDisplayScrollLayer::ComputeFrameMetrics(Layer* aLayer,
4485 const ContainerLayerParameters& aContainerParameters)
4487 ContainerLayerParameters params = aContainerParameters;
4488 if (mScrolledFrame->GetContent() &&
4489 nsLayoutUtils::GetCriticalDisplayPort(mScrolledFrame->GetContent(), nullptr)) {
4490 params.mInLowPrecisionDisplayPort = true;
4493 nsRect viewport = mScrollFrame->GetRect() -
4494 mScrollFrame->GetPosition() +
4495 mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
4497 return UniquePtr<FrameMetrics>(new FrameMetrics(
4498 ComputeFrameMetrics(mScrolledFrame, mScrollFrame, ReferenceFrame(), aLayer,
4499 mScrollParentId, viewport, false, params)));
4502 bool
4503 nsDisplayScrollLayer::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
4505 if (nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), nullptr)) {
4506 return true;
4509 return nsDisplayWrapList::ShouldBuildLayerEvenIfInvisible(aBuilder);
4512 bool
4513 nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder,
4514 nsRegion* aVisibleRegion)
4516 if (aBuilder->IsForPluginGeometry()) {
4517 return nsDisplayWrapList::ComputeVisibility(aBuilder, aVisibleRegion);
4519 nsRect displayport;
4520 bool usingDisplayPort =
4521 nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), &displayport);
4522 nsRect scrolledContentRect = GetScrolledContentRectToDraw(aBuilder,
4523 usingDisplayPort ? &displayport : nullptr);
4525 nsRect boundedRect = scrolledContentRect.Intersect(mList.GetBounds(aBuilder));
4526 nsRegion childVisibleRegion = scrolledContentRect;
4527 bool visible = mList.ComputeVisibilityForSublist(
4528 aBuilder, &childVisibleRegion, boundedRect,
4529 usingDisplayPort ? mScrollFrame : nullptr);
4531 // If APZ is enabled then don't allow this computation to influence
4532 // aVisibleRegion, on the assumption that the layer can be asynchronously
4533 // scrolled so we'll definitely need all the content under it.
4534 if (!nsLayoutUtils::UsesAsyncScrolling()) {
4535 bool snap;
4536 nsRect bounds = GetBounds(aBuilder, &snap);
4537 nsRegion removed;
4538 removed.Sub(bounds, childVisibleRegion);
4539 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed);
4542 return visible;
4545 LayerState
4546 nsDisplayScrollLayer::GetLayerState(nsDisplayListBuilder* aBuilder,
4547 LayerManager* aManager,
4548 const ContainerLayerParameters& aParameters)
4550 // Force this as a layer so we can scroll asynchronously.
4551 // This causes incorrect rendering for rounded clips!
4552 return LAYER_ACTIVE_FORCE;
4555 // Check if we are going to clip an abs pos item that we don't contain.
4556 // Root scroll frames clip all their descendants, so we don't need to worry
4557 // about them.
4558 bool
4559 WouldCauseIncorrectClippingOnAbsPosItem(nsDisplayListBuilder* aBuilder,
4560 nsDisplayScrollLayer* aItem)
4562 nsIFrame* scrollFrame = aItem->GetScrollFrame();
4563 nsIPresShell* presShell = scrollFrame->PresContext()->PresShell();
4564 if (scrollFrame == presShell->GetRootScrollFrame()) {
4565 return false;
4567 nsIFrame* scrolledFrame = aItem->GetScrolledFrame();
4568 nsIFrame* frame = aItem->Frame();
4569 if (frame == scrolledFrame || !frame->IsAbsolutelyPositioned() ||
4570 nsLayoutUtils::IsAncestorFrameCrossDoc(scrollFrame, frame, presShell->GetRootFrame())) {
4571 return false;
4573 if (!aItem->GetClip().IsRectAffectedByClip(aItem->GetChildren()->GetBounds(aBuilder))) {
4574 return false;
4576 return true;
4579 bool
4580 nsDisplayScrollLayer::TryMerge(nsDisplayListBuilder* aBuilder,
4581 nsDisplayItem* aItem)
4583 if (aItem->GetType() != TYPE_SCROLL_LAYER) {
4584 return false;
4586 nsDisplayScrollLayer* other = static_cast<nsDisplayScrollLayer*>(aItem);
4587 if (other->mScrolledFrame != this->mScrolledFrame) {
4588 return false;
4590 if (aItem->GetClip() != GetClip()) {
4591 return false;
4594 if (WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, this) ||
4595 WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, other)) {
4596 return false;
4599 NS_ASSERTION(other->mReferenceFrame == mReferenceFrame,
4600 "Must have the same reference frame!");
4602 FrameProperties props = mScrolledFrame->Properties();
4603 props.Set(nsIFrame::ScrollLayerCount(),
4604 reinterpret_cast<void*>(GetScrollLayerCount() - 1));
4606 // Swap frames with the other item before doing MergeFrom.
4607 // XXX - This ensures that the frame associated with a scroll layer after
4608 // merging is the first, rather than the last. This tends to change less,
4609 // ensuring we're more likely to retain the associated gfx layer.
4610 // See Bug 729534 and Bug 731641.
4611 nsIFrame* tmp = mFrame;
4612 mFrame = other->mFrame;
4613 other->mFrame = tmp;
4614 MergeFromTrackingMergedFrames(other);
4615 return true;
4618 void
4619 PropagateClip(nsDisplayListBuilder* aBuilder, const DisplayItemClip& aClip,
4620 nsDisplayList* aList)
4622 for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) {
4623 DisplayItemClip clip(i->GetClip());
4624 clip.IntersectWith(aClip);
4625 i->SetClip(aBuilder, clip);
4626 nsDisplayList* list = i->GetSameCoordinateSystemChildren();
4627 if (list) {
4628 PropagateClip(aBuilder, aClip, list);
4633 bool
4634 nsDisplayScrollLayer::ShouldFlattenAway(nsDisplayListBuilder* aBuilder)
4636 bool badAbsPosClip = WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, this);
4637 if (GetScrollLayerCount() > 1 || badAbsPosClip) {
4638 // Propagate our clip to our children. The clip for the scroll frame is
4639 // on this item, but not our child items so that they can draw non-visible
4640 // parts of the display port. But if we are flattening we failed and can't
4641 // draw the extra content, so it needs to be clipped.
4642 // But don't induce our clip on abs pos frames that we shouldn't be clipping.
4643 if (!badAbsPosClip) {
4644 PropagateClip(aBuilder, GetClip(), &mList);
4647 // Output something so the failure can be noted.
4648 nsresult status;
4649 mScrolledFrame->GetContent()->GetProperty(nsGkAtoms::AsyncScrollLayerCreationFailed, &status);
4650 if (status == NS_PROPTABLE_PROP_NOT_THERE) {
4651 mScrolledFrame->GetContent()->SetProperty(nsGkAtoms::AsyncScrollLayerCreationFailed, nullptr);
4652 if (badAbsPosClip) {
4653 printf_stderr("Async scrollable layer creation failed: scroll layer would induce incorrent clipping to an abs pos item.\n");
4654 } else {
4655 printf_stderr("Async scrollable layer creation failed: scroll layer can't have scrollable and non-scrollable items interleaved.\n");
4657 #ifdef MOZ_DUMP_PAINTING
4658 std::stringstream ss;
4659 nsFrame::PrintDisplayItem(aBuilder, this, ss, true, false);
4660 printf_stderr("%s\n", ss.str().c_str());
4661 #endif
4664 return true;
4666 if (mFrame != mScrolledFrame) {
4667 mMergedFrames.AppendElement(mFrame);
4668 mFrame = mScrolledFrame;
4670 return false;
4673 intptr_t
4674 nsDisplayScrollLayer::GetScrollLayerCount()
4676 FrameProperties props = mScrolledFrame->Properties();
4677 #ifdef DEBUG
4678 bool hasCount = false;
4679 intptr_t result = reinterpret_cast<intptr_t>(
4680 props.Get(nsIFrame::ScrollLayerCount(), &hasCount));
4681 // If this aborts, then the property was either not added before scroll
4682 // layers were created or the property was deleted to early. If the latter,
4683 // make sure that nsDisplayScrollInfoLayer is on the bottom of the list so
4684 // that it is processed last.
4685 NS_ABORT_IF_FALSE(hasCount, "nsDisplayScrollLayer should always be defined");
4686 return result;
4687 #else
4688 return reinterpret_cast<intptr_t>(props.Get(nsIFrame::ScrollLayerCount()));
4689 #endif
4692 void
4693 nsDisplayScrollLayer::WriteDebugInfo(std::stringstream& aStream)
4695 aStream << " (scrollframe " << mScrollFrame
4696 << " scrolledFrame " << mScrolledFrame << ")";
4699 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
4700 nsDisplayListBuilder* aBuilder,
4701 nsIFrame* aScrolledFrame,
4702 nsIFrame* aScrollFrame)
4703 : nsDisplayScrollLayer(aBuilder, aScrollFrame, aScrolledFrame, aScrollFrame)
4704 , mHoisted(false)
4706 #ifdef NS_BUILD_REFCNT_LOGGING
4707 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
4708 #endif
4711 nsDisplayScrollInfoLayer::~nsDisplayScrollInfoLayer()
4713 MOZ_COUNT_DTOR(nsDisplayScrollInfoLayer);
4716 nsRect
4717 nsDisplayScrollInfoLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
4719 return nsDisplayWrapList::GetBounds(aBuilder, aSnap);
4722 already_AddRefed<Layer>
4723 nsDisplayScrollInfoLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
4724 LayerManager* aManager,
4725 const ContainerLayerParameters& aContainerParameters)
4727 // Only build scrollinfo layers if event-regions are disabled, so that the
4728 // compositor knows where the inactive scrollframes are. When event-regions
4729 // are enabled, the dispatch-to-content regions generally provide this
4730 // information to the APZ code. However, in some cases, there might be
4731 // content that cannot be layerized, and so needs to scroll synchronously.
4732 // To handle those cases (which are indicated by setting mHoisted to true), we
4733 // still want to generate scrollinfo layers.
4734 if (gfxPrefs::LayoutEventRegionsEnabled() && !mHoisted) {
4735 return nullptr;
4737 return nsDisplayScrollLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
4740 LayerState
4741 nsDisplayScrollInfoLayer::GetLayerState(nsDisplayListBuilder* aBuilder,
4742 LayerManager* aManager,
4743 const ContainerLayerParameters& aParameters)
4745 // See comment in BuildLayer
4746 if (gfxPrefs::LayoutEventRegionsEnabled() && !mHoisted) {
4747 return LAYER_NONE;
4749 return LAYER_ACTIVE_EMPTY;
4752 bool
4753 nsDisplayScrollInfoLayer::TryMerge(nsDisplayListBuilder* aBuilder,
4754 nsDisplayItem* aItem)
4756 return false;
4759 bool
4760 nsDisplayScrollInfoLayer::ShouldFlattenAway(nsDisplayListBuilder* aBuilder)
4762 // Layer metadata for a particular scroll frame needs to be unique. Only
4763 // one nsDisplayScrollLayer (with rendered content) or one
4764 // nsDisplayScrollInfoLayer (with only the metadata) should survive the
4765 // visibility computation.
4766 return GetScrollLayerCount() == 1;
4769 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder,
4770 nsIFrame* aFrame, nsDisplayList* aList,
4771 int32_t aAPD, int32_t aParentAPD,
4772 uint32_t aFlags)
4773 : nsDisplaySubDocument(aBuilder, aFrame, aList, aFlags)
4774 , mAPD(aAPD), mParentAPD(aParentAPD) {
4775 MOZ_COUNT_CTOR(nsDisplayZoom);
4778 #ifdef NS_BUILD_REFCNT_LOGGING
4779 nsDisplayZoom::~nsDisplayZoom() {
4780 MOZ_COUNT_DTOR(nsDisplayZoom);
4782 #endif
4784 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
4786 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
4787 *aSnap = false;
4788 return bounds.ConvertAppUnitsRoundOut(mAPD, mParentAPD);
4791 void nsDisplayZoom::HitTest(nsDisplayListBuilder *aBuilder,
4792 const nsRect& aRect,
4793 HitTestState *aState,
4794 nsTArray<nsIFrame*> *aOutFrames)
4796 nsRect rect;
4797 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
4798 // rect as well instead of possibly rounding the width or height to zero.
4799 if (aRect.width == 1 && aRect.height == 1) {
4800 rect.MoveTo(aRect.TopLeft().ConvertAppUnits(mParentAPD, mAPD));
4801 rect.width = rect.height = 1;
4802 } else {
4803 rect = aRect.ConvertAppUnitsRoundOut(mParentAPD, mAPD);
4805 mList.HitTest(aBuilder, rect, aState, aOutFrames);
4808 bool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder *aBuilder,
4809 nsRegion *aVisibleRegion)
4811 // Convert the passed in visible region to our appunits.
4812 nsRegion visibleRegion;
4813 // mVisibleRect has been clipped to GetClippedBounds
4814 visibleRegion.And(*aVisibleRegion, mVisibleRect);
4815 visibleRegion = visibleRegion.ConvertAppUnitsRoundOut(mParentAPD, mAPD);
4816 nsRegion originalVisibleRegion = visibleRegion;
4818 nsRect transformedVisibleRect =
4819 mVisibleRect.ConvertAppUnitsRoundOut(mParentAPD, mAPD);
4820 bool retval;
4821 // If we are to generate a scrollable layer we call
4822 // nsDisplaySubDocument::ComputeVisibility to make the necessary adjustments
4823 // for ComputeVisibility, it does all it's calculations in the child APD.
4824 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame);
4825 if (!(mFlags & GENERATE_SCROLLABLE_LAYER) || !usingDisplayPort) {
4826 retval =
4827 mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion,
4828 transformedVisibleRect);
4829 } else {
4830 retval =
4831 nsDisplaySubDocument::ComputeVisibility(aBuilder, &visibleRegion);
4834 nsRegion removed;
4835 // removed = originalVisibleRegion - visibleRegion
4836 removed.Sub(originalVisibleRegion, visibleRegion);
4837 // Convert removed region to parent appunits.
4838 removed = removed.ConvertAppUnitsRoundIn(mAPD, mParentAPD);
4839 // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications
4840 // SubtractFromVisibleRegion does)
4841 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed);
4843 return retval;
4846 ///////////////////////////////////////////////////
4847 // nsDisplayTransform Implementation
4850 // Write #define UNIFIED_CONTINUATIONS here to have the transform property try
4851 // to transform content with continuations as one unified block instead of
4852 // several smaller ones. This is currently disabled because it doesn't work
4853 // correctly, since when the frames are initially being reflowed, their
4854 // continuations all compute their bounding rects independently of each other
4855 // and consequently get the wrong value. Write #define DEBUG_HIT here to have
4856 // the nsDisplayTransform class dump out a bunch of information about hit
4857 // detection.
4858 #undef UNIFIED_CONTINUATIONS
4859 #undef DEBUG_HIT
4861 /* Returns the bounds of a frame as defined for transforms. If
4862 * UNIFIED_CONTINUATIONS is not defined, this is simply the frame's bounding
4863 * rectangle, translated to the origin. Otherwise, returns the smallest
4864 * rectangle containing a frame and all of its continuations. For example, if
4865 * there is a <span> element with several continuations split over several
4866 * lines, this function will return the rectangle containing all of those
4867 * continuations. This rectangle is relative to the origin of the frame's local
4868 * coordinate space.
4870 #ifndef UNIFIED_CONTINUATIONS
4872 nsRect
4873 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
4875 NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
4877 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
4878 // TODO: SVG needs to define what percentage translations resolve against.
4879 return nsRect();
4882 return nsRect(nsPoint(0, 0), aFrame->GetSize());
4885 #else
4887 nsRect
4888 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
4890 NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
4892 nsRect result;
4894 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
4895 // TODO: SVG needs to define what percentage translations resolve against.
4896 return result;
4899 /* Iterate through the continuation list, unioning together all the
4900 * bounding rects.
4902 for (const nsIFrame *currFrame = aFrame->FirstContinuation();
4903 currFrame != nullptr;
4904 currFrame = currFrame->GetNextContinuation())
4906 /* Get the frame rect in local coordinates, then translate back to the
4907 * original coordinates.
4909 result.UnionRect(result, nsRect(currFrame->GetOffsetTo(aFrame),
4910 currFrame->GetSize()));
4913 return result;
4916 #endif
4918 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
4919 nsIFrame *aFrame, nsDisplayList *aList,
4920 const nsRect& aChildrenVisibleRect,
4921 ComputeTransformFunction aTransformGetter,
4922 uint32_t aIndex)
4923 : nsDisplayItem(aBuilder, aFrame)
4924 , mStoredList(aBuilder, aFrame, aList)
4925 , mTransformGetter(aTransformGetter)
4926 , mChildrenVisibleRect(aChildrenVisibleRect)
4927 , mIndex(aIndex)
4929 MOZ_COUNT_CTOR(nsDisplayTransform);
4930 NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
4931 NS_ABORT_IF_FALSE(!aFrame->IsTransformed(), "Can't specify a transform getter for a transformed frame!");
4932 Init(aBuilder);
4935 void
4936 nsDisplayTransform::SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder)
4938 mReferenceFrame =
4939 aBuilder->FindReferenceFrameFor(GetTransformRootFrame(mFrame));
4940 mToReferenceFrame = mFrame->GetOffsetToCrossDoc(mReferenceFrame);
4941 mVisibleRect = aBuilder->GetDirtyRect() + mToReferenceFrame;
4944 void
4945 nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder)
4947 mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip());
4948 mStoredList.SetVisibleRect(mChildrenVisibleRect);
4949 mMaybePrerender = ShouldPrerenderTransformedContent(aBuilder, mFrame);
4951 const nsStyleDisplay* disp = mFrame->StyleDisplay();
4952 if ((disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM)) {
4953 // We will only pre-render if this will-change is on budget.
4954 mMaybePrerender = true;
4957 if (mMaybePrerender) {
4958 bool snap;
4959 mVisibleRect = GetBounds(aBuilder, &snap);
4963 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
4964 nsIFrame *aFrame, nsDisplayList *aList,
4965 const nsRect& aChildrenVisibleRect,
4966 uint32_t aIndex)
4967 : nsDisplayItem(aBuilder, aFrame)
4968 , mStoredList(aBuilder, aFrame, aList)
4969 , mTransformGetter(nullptr)
4970 , mChildrenVisibleRect(aChildrenVisibleRect)
4971 , mIndex(aIndex)
4973 MOZ_COUNT_CTOR(nsDisplayTransform);
4974 NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
4975 SetReferenceFrameToAncestor(aBuilder);
4976 Init(aBuilder);
4979 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
4980 nsIFrame *aFrame, nsDisplayItem *aItem,
4981 const nsRect& aChildrenVisibleRect,
4982 uint32_t aIndex)
4983 : nsDisplayItem(aBuilder, aFrame)
4984 , mStoredList(aBuilder, aFrame, aItem)
4985 , mTransformGetter(nullptr)
4986 , mChildrenVisibleRect(aChildrenVisibleRect)
4987 , mIndex(aIndex)
4989 MOZ_COUNT_CTOR(nsDisplayTransform);
4990 NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
4991 SetReferenceFrameToAncestor(aBuilder);
4992 Init(aBuilder);
4995 /* Returns the delta specified by the -moz-transform-origin property.
4996 * This is a positive delta, meaning that it indicates the direction to move
4997 * to get from (0, 0) of the frame to the transform origin. This function is
4998 * called off the main thread.
5000 /* static */ Point3D
5001 nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame,
5002 float aAppUnitsPerPixel,
5003 const nsRect* aBoundsOverride)
5005 NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
5006 NS_PRECONDITION(aFrame->IsTransformed() || aFrame->StyleDisplay()->BackfaceIsHidden(),
5007 "Shouldn't get a delta for an untransformed frame!");
5009 if (!aFrame->IsTransformed()) {
5010 return Point3D();
5013 /* For both of the coordinates, if the value of -moz-transform is a
5014 * percentage, it's relative to the size of the frame. Otherwise, if it's
5015 * a distance, it's already computed for us!
5017 const nsStyleDisplay* display = aFrame->StyleDisplay();
5018 nsRect boundingRect = (aBoundsOverride ? *aBoundsOverride :
5019 nsDisplayTransform::GetFrameBoundsForTransform(aFrame));
5021 /* Allows us to access named variables by index. */
5022 float coords[3];
5023 const nscoord* dimensions[2] =
5024 {&boundingRect.width, &boundingRect.height};
5026 for (uint8_t index = 0; index < 2; ++index) {
5027 /* If the -moz-transform-origin specifies a percentage, take the percentage
5028 * of the size of the box.
5030 const nsStyleCoord &coord = display->mTransformOrigin[index];
5031 if (coord.GetUnit() == eStyleUnit_Calc) {
5032 const nsStyleCoord::Calc *calc = coord.GetCalcValue();
5033 coords[index] =
5034 NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
5035 calc->mPercent +
5036 NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
5037 } else if (coord.GetUnit() == eStyleUnit_Percent) {
5038 coords[index] =
5039 NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
5040 coord.GetPercentValue();
5041 } else {
5042 NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
5043 coords[index] =
5044 NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
5046 if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
5047 coord.GetUnit() != eStyleUnit_Percent) {
5048 // <length> values represent offsets from the origin of the SVG element's
5049 // user space, not the top left of its bounds, so we must adjust for that:
5050 nscoord offset =
5051 (index == 0) ? aFrame->GetPosition().x : aFrame->GetPosition().y;
5052 coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel);
5056 coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
5057 aAppUnitsPerPixel);
5058 /* Adjust based on the origin of the rectangle. */
5059 coords[0] += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel);
5060 coords[1] += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel);
5062 return Point3D(coords[0], coords[1], coords[2]);
5065 /* Returns the delta specified by the -moz-perspective-origin property.
5066 * This is a positive delta, meaning that it indicates the direction to move
5067 * to get from (0, 0) of the frame to the perspective origin. This function is
5068 * called off the main thread.
5070 /* static */ Point3D
5071 nsDisplayTransform::GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
5072 float aAppUnitsPerPixel)
5074 NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
5075 NS_PRECONDITION(aFrame->IsTransformed() || aFrame->StyleDisplay()->BackfaceIsHidden(),
5076 "Shouldn't get a delta for an untransformed frame!");
5078 if (!aFrame->IsTransformed()) {
5079 return Point3D();
5082 /* For both of the coordinates, if the value of -moz-perspective-origin is a
5083 * percentage, it's relative to the size of the frame. Otherwise, if it's
5084 * a distance, it's already computed for us!
5087 //TODO: Should this be using our bounds or the parent's bounds?
5088 // How do we handle aBoundsOverride in the latter case?
5089 nsIFrame* parent;
5090 nsStyleContext* psc = aFrame->GetParentStyleContext(&parent);
5091 if (!psc) {
5092 return Point3D();
5094 if (!parent) {
5095 parent = aFrame->GetParent();
5096 if (!parent) {
5097 return Point3D();
5100 const nsStyleDisplay* display = psc->StyleDisplay();
5101 nsRect boundingRect = nsDisplayTransform::GetFrameBoundsForTransform(parent);
5103 /* Allows us to access named variables by index. */
5104 Point3D result;
5105 result.z = 0.0f;
5106 gfx::Float* coords[2] = {&result.x, &result.y};
5107 const nscoord* dimensions[2] =
5108 {&boundingRect.width, &boundingRect.height};
5110 for (uint8_t index = 0; index < 2; ++index) {
5111 /* If the -moz-transform-origin specifies a percentage, take the percentage
5112 * of the size of the box.
5114 const nsStyleCoord &coord = display->mPerspectiveOrigin[index];
5115 if (coord.GetUnit() == eStyleUnit_Calc) {
5116 const nsStyleCoord::Calc *calc = coord.GetCalcValue();
5117 *coords[index] =
5118 NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
5119 calc->mPercent +
5120 NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
5121 } else if (coord.GetUnit() == eStyleUnit_Percent) {
5122 *coords[index] =
5123 NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
5124 coord.GetPercentValue();
5125 } else {
5126 NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
5127 *coords[index] =
5128 NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
5132 nsPoint parentOffset = aFrame->GetOffsetTo(parent);
5133 Point3D gfxOffset(
5134 NSAppUnitsToFloatPixels(parentOffset.x, aAppUnitsPerPixel),
5135 NSAppUnitsToFloatPixels(parentOffset.y, aAppUnitsPerPixel),
5136 0.0f);
5138 return result - gfxOffset;
5141 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsIFrame* aFrame,
5142 float aAppUnitsPerPixel,
5143 const nsRect* aBoundsOverride)
5144 : mFrame(aFrame)
5145 , mTransformList(aFrame->StyleDisplay()->mSpecifiedTransform)
5146 , mToTransformOrigin(GetDeltaToTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride))
5147 , mToPerspectiveOrigin(GetDeltaToPerspectiveOrigin(aFrame, aAppUnitsPerPixel))
5148 , mChildPerspective(0)
5150 const nsStyleDisplay* parentDisp = nullptr;
5151 nsStyleContext* parentStyleContext = aFrame->StyleContext()->GetParent();
5152 if (parentStyleContext) {
5153 parentDisp = parentStyleContext->StyleDisplay();
5155 if (parentDisp && parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord) {
5156 mChildPerspective = parentDisp->mChildPerspective.GetCoordValue();
5160 /* Wraps up the -moz-transform matrix in a change-of-basis matrix pair that
5161 * translates from local coordinate space to transform coordinate space, then
5162 * hands it back.
5164 gfx3DMatrix
5165 nsDisplayTransform::GetResultingTransformMatrix(const FrameTransformProperties& aProperties,
5166 const nsPoint& aOrigin,
5167 float aAppUnitsPerPixel,
5168 const nsRect* aBoundsOverride,
5169 nsIFrame** aOutAncestor)
5171 return GetResultingTransformMatrixInternal(aProperties, aOrigin, aAppUnitsPerPixel,
5172 aBoundsOverride, aOutAncestor, false);
5175 gfx3DMatrix
5176 nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame,
5177 const nsPoint& aOrigin,
5178 float aAppUnitsPerPixel,
5179 const nsRect* aBoundsOverride,
5180 nsIFrame** aOutAncestor,
5181 bool aOffsetByOrigin)
5183 FrameTransformProperties props(aFrame,
5184 aAppUnitsPerPixel,
5185 aBoundsOverride);
5187 return GetResultingTransformMatrixInternal(props, aOrigin, aAppUnitsPerPixel,
5188 aBoundsOverride, aOutAncestor,
5189 aOffsetByOrigin);
5192 gfx3DMatrix
5193 nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties,
5194 const nsPoint& aOrigin,
5195 float aAppUnitsPerPixel,
5196 const nsRect* aBoundsOverride,
5197 nsIFrame** aOutAncestor,
5198 bool aOffsetByOrigin)
5200 const nsIFrame *frame = aProperties.mFrame;
5202 if (aOutAncestor) {
5203 *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(frame);
5206 /* Get the underlying transform matrix. This requires us to get the
5207 * bounds of the frame.
5209 nsRect bounds = (aBoundsOverride ? *aBoundsOverride :
5210 nsDisplayTransform::GetFrameBoundsForTransform(frame));
5212 /* Get the matrix, then change its basis to factor in the origin. */
5213 bool dummy;
5214 gfx3DMatrix result;
5215 // Call IsSVGTransformed() regardless of the value of
5216 // disp->mSpecifiedTransform, since we still need any transformFromSVGParent.
5217 Matrix svgTransform, transformFromSVGParent;
5218 bool hasSVGTransforms =
5219 frame && frame->IsSVGTransformed(&svgTransform, &transformFromSVGParent);
5220 /* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */
5221 if (aProperties.mTransformList) {
5222 result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead,
5223 frame ? frame->StyleContext() : nullptr,
5224 frame ? frame->PresContext() : nullptr,
5225 dummy, bounds, aAppUnitsPerPixel);
5226 } else if (hasSVGTransforms) {
5227 // Correct the translation components for zoom:
5228 float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() /
5229 aAppUnitsPerPixel;
5230 svgTransform._31 *= pixelsPerCSSPx;
5231 svgTransform._32 *= pixelsPerCSSPx;
5232 result = gfx3DMatrix::From2D(ThebesMatrix(svgTransform));
5235 if (hasSVGTransforms && !transformFromSVGParent.IsIdentity()) {
5236 // Correct the translation components for zoom:
5237 float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() /
5238 aAppUnitsPerPixel;
5239 transformFromSVGParent._31 *= pixelsPerCSSPx;
5240 transformFromSVGParent._32 *= pixelsPerCSSPx;
5241 result = result * gfx3DMatrix::From2D(ThebesMatrix(transformFromSVGParent));
5244 if (aProperties.mChildPerspective > 0.0) {
5245 gfx3DMatrix perspective;
5246 perspective._34 =
5247 -1.0 / NSAppUnitsToFloatPixels(aProperties.mChildPerspective, aAppUnitsPerPixel);
5248 /* At the point when perspective is applied, we have been translated to the transform origin.
5249 * The translation to the perspective origin is the difference between these values.
5251 perspective.ChangeBasis(aProperties.mToPerspectiveOrigin - aProperties.mToTransformOrigin);
5252 result = result * perspective;
5255 /* Account for the -moz-transform-origin property by translating the
5256 * coordinate space to the new origin.
5258 Point3D newOrigin =
5259 Point3D(NSAppUnitsToFloatPixels(aOrigin.x, aAppUnitsPerPixel),
5260 NSAppUnitsToFloatPixels(aOrigin.y, aAppUnitsPerPixel),
5261 0.0f);
5262 Point3D roundedOrigin(hasSVGTransforms ? newOrigin.x : NS_round(newOrigin.x),
5263 hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y),
5265 Point3D offsetBetweenOrigins = roundedOrigin + aProperties.mToTransformOrigin;
5267 if (frame && frame->Preserves3D()) {
5268 // Include the transform set on our parent
5269 NS_ASSERTION(frame->GetParent() &&
5270 frame->GetParent()->IsTransformed() &&
5271 frame->GetParent()->Preserves3DChildren(),
5272 "Preserve3D mismatch!");
5273 FrameTransformProperties props(frame->GetParent(),
5274 aAppUnitsPerPixel,
5275 nullptr);
5277 // If this frame isn't transformed (but we exist for backface-visibility),
5278 // then we're not a reference frame so no offset to origin will be added. Our
5279 // parent transform however *is* the reference frame, so we pass true for
5280 // aOffsetByOrigin to convert into the correct coordinate space.
5281 gfx3DMatrix parent =
5282 GetResultingTransformMatrixInternal(props,
5283 aOrigin - frame->GetPosition(),
5284 aAppUnitsPerPixel, nullptr,
5285 aOutAncestor, !frame->IsTransformed());
5287 result.ChangeBasis(offsetBetweenOrigins);
5288 result = result * parent;
5289 if (aOffsetByOrigin) {
5290 result.Translate(roundedOrigin);
5292 return result;
5295 if (aOffsetByOrigin) {
5296 // We can fold the final translation by roundedOrigin into the first matrix
5297 // basis change translation. This is more stable against variation due to
5298 // insufficient floating point precision than reversing the translation
5299 // afterwards.
5300 result.Translate(-aProperties.mToTransformOrigin);
5301 result.TranslatePost(offsetBetweenOrigins);
5302 } else {
5303 result.ChangeBasis(offsetBetweenOrigins);
5305 return result;
5308 bool
5309 nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
5311 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, eCSSProperty_opacity)) {
5312 return true;
5315 if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
5316 nsCString message;
5317 message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for opacity animation");
5318 AnimationPlayerCollection::LogAsyncAnimationFailure(message,
5319 Frame()->GetContent());
5321 return false;
5324 bool
5325 nsDisplayTransform::ShouldPrerender(nsDisplayListBuilder* aBuilder) {
5326 if (!mMaybePrerender) {
5327 return false;
5330 if (ShouldPrerenderTransformedContent(aBuilder, mFrame)) {
5331 return true;
5334 const nsStyleDisplay* disp = mFrame->StyleDisplay();
5335 if ((disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM) &&
5336 aBuilder->IsInWillChangeBudget(mFrame)) {
5337 return true;
5340 return false;
5343 bool
5344 nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
5346 if (mMaybePrerender) {
5347 // TODO We need to make sure that if we use async animation we actually
5348 // pre-render even if we're out of will change budget.
5349 return true;
5351 DebugOnly<bool> prerender = ShouldPrerenderTransformedContent(aBuilder, mFrame, true);
5352 NS_ASSERTION(!prerender, "Something changed under us!");
5353 return false;
5356 /* static */ bool
5357 nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
5358 nsIFrame* aFrame,
5359 bool aLogAnimations)
5361 // Elements whose transform has been modified recently, or which
5362 // have a compositor-animated transform, can be prerendered. An element
5363 // might have only just had its transform animated in which case
5364 // the ActiveLayerManager may not have been notified yet.
5365 if (!ActiveLayerTracker::IsStyleMaybeAnimated(aFrame, eCSSProperty_transform) &&
5366 (!aFrame->GetContent() ||
5367 !nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(),
5368 eCSSProperty_transform))) {
5369 if (aLogAnimations) {
5370 nsCString message;
5371 message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for transform animation");
5372 AnimationPlayerCollection::LogAsyncAnimationFailure(message,
5373 aFrame->GetContent());
5375 return false;
5378 nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
5379 // Only prerender if the transformed frame's size is <= the
5380 // reference frame size (~viewport), allowing a 1/8th fuzz factor
5381 // for shadows, borders, etc.
5382 refSize += nsSize(refSize.width / 8, refSize.height / 8);
5383 nsSize frameSize = aFrame->GetVisualOverflowRectRelativeToSelf().Size();
5384 nscoord maxInAppUnits = nscoord_MAX;
5385 if (frameSize <= refSize) {
5386 maxInAppUnits = aFrame->PresContext()->DevPixelsToAppUnits(4096);
5387 nsRect visual = aFrame->GetVisualOverflowRect();
5388 if (visual.width <= maxInAppUnits && visual.height <= maxInAppUnits) {
5389 return true;
5393 if (aLogAnimations) {
5394 nsRect visual = aFrame->GetVisualOverflowRect();
5396 nsCString message;
5397 message.AppendLiteral("Performance warning: Async animation disabled because frame size (");
5398 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameSize.width));
5399 message.AppendLiteral(", ");
5400 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameSize.height));
5401 message.AppendLiteral(") is bigger than the viewport (");
5402 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.width));
5403 message.AppendLiteral(", ");
5404 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.height));
5405 message.AppendLiteral(") or the visual rectangle (");
5406 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(visual.width));
5407 message.AppendLiteral(", ");
5408 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(visual.height));
5409 message.AppendLiteral(") is larger than the max allowable value (");
5410 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(maxInAppUnits));
5411 message.Append(')');
5412 AnimationPlayerCollection::LogAsyncAnimationFailure(message,
5413 aFrame->GetContent());
5415 return false;
5418 /* If the matrix is singular, or a hidden backface is shown, the frame won't be visible or hit. */
5419 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix)
5421 if (aMatrix.IsSingular()) {
5422 return false;
5424 if (aFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
5425 aMatrix.IsBackfaceVisible()) {
5426 return false;
5428 return true;
5431 const Matrix4x4&
5432 nsDisplayTransform::GetTransform()
5434 if (mTransform.IsIdentity()) {
5435 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
5436 Point3D newOrigin =
5437 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
5438 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale),
5439 0.0f);
5440 if (mTransformGetter) {
5441 mTransform = mTransformGetter(mFrame, scale);
5442 mTransform.ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
5443 } else {
5445 * Passing true as the final argument means that we want to shift the
5446 * coordinates to be relative to our reference frame instead of relative
5447 * to this frame.
5448 * When we have preserve-3d, our reference frame is already guaranteed
5449 * to be an ancestor of the preserve-3d chain, so we only need to do
5450 * this once.
5452 mTransform = ToMatrix4x4(
5453 GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale,
5454 nullptr, nullptr, mFrame->IsTransformed()));
5457 return mTransform;
5460 bool
5461 nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
5463 return ShouldPrerender(aBuilder);
5466 already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder,
5467 LayerManager *aManager,
5468 const ContainerLayerParameters& aContainerParameters)
5470 const Matrix4x4& newTransformMatrix = GetTransform();
5472 if (mFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
5473 newTransformMatrix.IsBackfaceVisible()) {
5474 return nullptr;
5477 uint32_t flags = ShouldPrerender(aBuilder) ?
5478 FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0;
5479 nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
5480 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mStoredList.GetChildren(),
5481 aContainerParameters, &newTransformMatrix, flags);
5483 if (!container) {
5484 return nullptr;
5487 // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags,
5488 // so we never need to explicitely unset this flag.
5489 if (mFrame->Preserves3D() || mFrame->Preserves3DChildren()) {
5490 container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D);
5491 } else {
5492 container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_PRESERVE_3D);
5495 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
5496 this, mFrame,
5497 eCSSProperty_transform);
5498 if (ShouldPrerender(aBuilder)) {
5499 container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
5500 /*the value is irrelevant*/nullptr);
5501 container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_MAY_CHANGE_TRANSFORM);
5502 } else {
5503 container->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
5504 container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_MAY_CHANGE_TRANSFORM);
5506 return container.forget();
5509 nsDisplayItem::LayerState
5510 nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder,
5511 LayerManager* aManager,
5512 const ContainerLayerParameters& aParameters) {
5513 // If the transform is 3d, or the layer takes part in preserve-3d sorting
5514 // then we *always* want this to be an active layer.
5515 if (!GetTransform().Is2D() || mFrame->Preserves3D()) {
5516 return LAYER_ACTIVE_FORCE;
5518 // Here we check if the *post-transform* bounds of this item are big enough
5519 // to justify an active layer.
5520 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, eCSSProperty_transform) &&
5521 !IsItemTooSmallForActiveLayer(this))
5522 return LAYER_ACTIVE;
5523 if (mFrame->GetContent()) {
5524 if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(),
5525 eCSSProperty_transform)) {
5526 return LAYER_ACTIVE;
5530 const nsStyleDisplay* disp = mFrame->StyleDisplay();
5531 if ((disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM)) {
5532 return LAYER_ACTIVE;
5535 // Expect the child display items to have this frame as their animated
5536 // geometry root (since it will be their reference frame). If they have a
5537 // different animated geometry root, we'll make this an active layer so the
5538 // animation can be accelerated.
5539 return RequiredLayerStateForChildren(aBuilder, aManager, aParameters,
5540 *mStoredList.GetChildren(), Frame());
5543 bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder,
5544 nsRegion *aVisibleRegion)
5546 /* As we do this, we need to be sure to
5547 * untransform the visible rect, since we want everything that's painting to
5548 * think that it's painting in its original rectangular coordinate space.
5549 * If we can't untransform, take the entire overflow rect */
5550 nsRect untransformedVisibleRect;
5551 if (ShouldPrerender(aBuilder) ||
5552 !UntransformVisibleRect(aBuilder, &untransformedVisibleRect))
5554 untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf();
5556 nsRegion untransformedVisible = untransformedVisibleRect;
5557 // Call RecomputeVisiblity instead of ComputeVisibility since
5558 // nsDisplayItem::ComputeVisibility should only be called from
5559 // nsDisplayList::ComputeVisibility (which sets mVisibleRect on the item)
5560 mStoredList.RecomputeVisibility(aBuilder, &untransformedVisible);
5561 return true;
5564 #ifdef DEBUG_HIT
5565 #include <time.h>
5566 #endif
5568 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
5569 void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder,
5570 const nsRect& aRect,
5571 HitTestState *aState,
5572 nsTArray<nsIFrame*> *aOutFrames)
5574 /* Here's how this works:
5575 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
5576 * anything).
5577 * 2. Invert the matrix.
5578 * 3. Use it to transform the rect into the correct space.
5579 * 4. Pass that rect down through to the list's version of HitTest.
5581 // GetTransform always operates in dev pixels.
5582 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
5583 Matrix4x4 matrix = GetTransform();
5585 if (!IsFrameVisible(mFrame, matrix)) {
5586 return;
5589 /* We want to go from transformed-space to regular space.
5590 * Thus we have to invert the matrix, which normally does
5591 * the reverse operation (e.g. regular->transformed)
5594 /* Now, apply the transform and pass it down the channel. */
5595 matrix.Invert();
5596 nsRect resultingRect;
5597 if (aRect.width == 1 && aRect.height == 1) {
5598 // Magic width/height indicating we're hit testing a point, not a rect
5599 Point4D point = matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor),
5600 NSAppUnitsToFloatPixels(aRect.y, factor)));
5601 if (!point.HasPositiveWCoord()) {
5602 return;
5605 Point point2d = point.As2DPoint();
5607 resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor),
5608 NSFloatPixelsToAppUnits(float(point2d.y), factor),
5609 1, 1);
5611 } else {
5612 Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
5613 NSAppUnitsToFloatPixels(aRect.y, factor),
5614 NSAppUnitsToFloatPixels(aRect.width, factor),
5615 NSAppUnitsToFloatPixels(aRect.height, factor));
5617 Rect rect = matrix.ProjectRectBounds(originalRect);
5619 bool snap;
5620 nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
5621 Rect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
5622 NSAppUnitsToFloatPixels(childBounds.y, factor),
5623 NSAppUnitsToFloatPixels(childBounds.width, factor),
5624 NSAppUnitsToFloatPixels(childBounds.height, factor));
5625 rect = rect.Intersect(childGfxBounds);
5627 resultingRect = nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
5628 NSFloatPixelsToAppUnits(float(rect.Y()), factor),
5629 NSFloatPixelsToAppUnits(float(rect.Width()), factor),
5630 NSFloatPixelsToAppUnits(float(rect.Height()), factor));
5633 if (resultingRect.IsEmpty()) {
5634 return;
5638 #ifdef DEBUG_HIT
5639 printf("Frame: %p\n", dynamic_cast<void *>(mFrame));
5640 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(), resultingRect.Y());
5641 uint32_t originalFrameCount = aOutFrames.Length();
5642 #endif
5644 mStoredList.HitTest(aBuilder, resultingRect, aState, aOutFrames);
5646 #ifdef DEBUG_HIT
5647 if (originalFrameCount != aOutFrames.Length())
5648 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
5649 dynamic_cast<void *>(aOutFrames.ElementAt(0)));
5650 printf("=== end of hit test ===\n");
5651 #endif
5655 float
5656 nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint)
5658 // GetTransform always operates in dev pixels.
5659 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
5660 Matrix4x4 matrix = GetTransform();
5662 NS_ASSERTION(IsFrameVisible(mFrame, matrix), "We can't have hit a frame that isn't visible!");
5664 Matrix4x4 inverse = matrix;
5665 inverse.Invert();
5666 Point4D point = inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
5667 NSAppUnitsToFloatPixels(aPoint.y, factor)));
5668 NS_ASSERTION(point.HasPositiveWCoord(), "Why are we trying to get the depth for a point we didn't hit?");
5670 Point point2d = point.As2DPoint();
5672 Point3D transformed = matrix * Point3D(point2d.x, point2d.y, 0);
5673 return transformed.z;
5676 /* The bounding rectangle for the object is the overflow rectangle translated
5677 * by the reference point.
5679 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder, bool* aSnap)
5681 nsRect untransformedBounds = MaybePrerender() ?
5682 mFrame->GetVisualOverflowRectRelativeToSelf() :
5683 mStoredList.GetBounds(aBuilder, aSnap);
5684 *aSnap = false;
5685 // GetTransform always operates in dev pixels.
5686 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
5687 return nsLayoutUtils::MatrixTransformRect(untransformedBounds,
5688 To3DMatrix(GetTransform()),
5689 factor);
5692 /* The transform is opaque iff the transform consists solely of scales and
5693 * translations and if the underlying content is opaque. Thus if the transform
5694 * is of the form
5696 * |a c e|
5697 * |b d f|
5698 * |0 0 1|
5700 * We need b and c to be zero.
5702 * We also need to check whether the underlying opaque content completely fills
5703 * our visible rect. We use UntransformRect which expands to the axis-aligned
5704 * bounding rect, but that's OK since if
5705 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
5706 * certainly contains the actual (non-axis-aligned) untransformed rect.
5708 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder,
5709 bool* aSnap)
5711 *aSnap = false;
5712 nsRect untransformedVisible;
5713 // If we're going to prerender all our content, pretend like we
5714 // don't have opqaue content so that everything under us is rendered
5715 // as well. That will increase graphics memory usage if our frame
5716 // covers the entire window, but it allows our transform to be
5717 // updated extremely cheaply, without invalidating any other
5718 // content.
5719 if (MaybePrerender() ||
5720 !UntransformVisibleRect(aBuilder, &untransformedVisible)) {
5721 return nsRegion();
5724 const Matrix4x4& matrix = GetTransform();
5726 nsRegion result;
5727 Matrix matrix2d;
5728 bool tmpSnap;
5729 if (matrix.Is2D(&matrix2d) &&
5730 matrix2d.PreservesAxisAlignedRectangles() &&
5731 mStoredList.GetOpaqueRegion(aBuilder, &tmpSnap).Contains(untransformedVisible)) {
5732 result = mVisibleRect.Intersect(GetBounds(aBuilder, &tmpSnap));
5734 return result;
5737 /* The transform is uniform if it fills the entire bounding rect and the
5738 * wrapped list is uniform. See GetOpaqueRegion for discussion of why this
5739 * works.
5741 bool nsDisplayTransform::IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor)
5743 nsRect untransformedVisible;
5744 if (!UntransformVisibleRect(aBuilder, &untransformedVisible)) {
5745 return false;
5747 const Matrix4x4& matrix = GetTransform();
5749 Matrix matrix2d;
5750 return matrix.Is2D(&matrix2d) &&
5751 matrix2d.PreservesAxisAlignedRectangles() &&
5752 mStoredList.GetVisibleRect().Contains(untransformedVisible) &&
5753 mStoredList.IsUniform(aBuilder, aColor);
5756 /* If UNIFIED_CONTINUATIONS is defined, we can merge two display lists that
5757 * share the same underlying content. Otherwise, doing so results in graphical
5758 * glitches.
5760 #ifndef UNIFIED_CONTINUATIONS
5762 bool
5763 nsDisplayTransform::TryMerge(nsDisplayListBuilder *aBuilder,
5764 nsDisplayItem *aItem)
5766 return false;
5769 #else
5771 bool
5772 nsDisplayTransform::TryMerge(nsDisplayListBuilder *aBuilder,
5773 nsDisplayItem *aItem)
5775 NS_PRECONDITION(aItem, "Why did you try merging with a null item?");
5776 NS_PRECONDITION(aBuilder, "Why did you try merging with a null builder?");
5778 /* Make sure that we're dealing with two transforms. */
5779 if (aItem->GetType() != TYPE_TRANSFORM)
5780 return false;
5782 /* Check to see that both frames are part of the same content. */
5783 if (aItem->Frame()->GetContent() != mFrame->GetContent())
5784 return false;
5786 if (aItem->GetClip() != GetClip())
5787 return false;
5789 /* Now, move everything over to this frame and signal that
5790 * we merged things!
5792 mStoredList.MergeFromTrackingMergedFrames(&static_cast<nsDisplayTransform*>(aItem)->mStoredList);
5793 return true;
5796 #endif
5798 /* TransformRect takes in as parameters a rectangle (in app space) and returns
5799 * the smallest rectangle (in app space) containing the transformed image of
5800 * that rectangle. That is, it takes the four corners of the rectangle,
5801 * transforms them according to the matrix associated with the specified frame,
5802 * then returns the smallest rectangle containing the four transformed points.
5804 * @param aUntransformedBounds The rectangle (in app units) to transform.
5805 * @param aFrame The frame whose transformation should be applied.
5806 * @param aOrigin The delta from the frame origin to the coordinate space origin
5807 * @param aBoundsOverride (optional) Force the frame bounds to be the
5808 * specified bounds.
5809 * @return The smallest rectangle containing the image of the transformed
5810 * rectangle.
5812 nsRect nsDisplayTransform::TransformRect(const nsRect &aUntransformedBounds,
5813 const nsIFrame* aFrame,
5814 const nsPoint &aOrigin,
5815 const nsRect* aBoundsOverride)
5817 NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
5819 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
5820 return nsLayoutUtils::MatrixTransformRect
5821 (aUntransformedBounds,
5822 GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride),
5823 factor);
5826 nsRect nsDisplayTransform::TransformRectOut(const nsRect &aUntransformedBounds,
5827 const nsIFrame* aFrame,
5828 const nsPoint &aOrigin,
5829 const nsRect* aBoundsOverride)
5831 NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
5833 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
5834 return nsLayoutUtils::MatrixTransformRectOut
5835 (aUntransformedBounds,
5836 GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride),
5837 factor);
5840 bool nsDisplayTransform::UntransformRect(const nsRect &aTransformedBounds,
5841 const nsRect &aChildBounds,
5842 const nsIFrame* aFrame,
5843 const nsPoint &aOrigin,
5844 nsRect *aOutRect)
5846 NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
5848 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
5850 gfx3DMatrix transform = GetResultingTransformMatrix(aFrame, aOrigin, factor, nullptr);
5851 if (transform.IsSingular()) {
5852 return false;
5855 Rect result(NSAppUnitsToFloatPixels(aTransformedBounds.x, factor),
5856 NSAppUnitsToFloatPixels(aTransformedBounds.y, factor),
5857 NSAppUnitsToFloatPixels(aTransformedBounds.width, factor),
5858 NSAppUnitsToFloatPixels(aTransformedBounds.height, factor));
5860 Rect childGfxBounds(NSAppUnitsToFloatPixels(aChildBounds.x, factor),
5861 NSAppUnitsToFloatPixels(aChildBounds.y, factor),
5862 NSAppUnitsToFloatPixels(aChildBounds.width, factor),
5863 NSAppUnitsToFloatPixels(aChildBounds.height, factor));
5865 result = ToMatrix4x4(transform.Inverse()).ProjectRectBounds(result);
5866 result = result.Intersect(childGfxBounds);
5867 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
5868 return true;
5871 bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
5872 nsRect *aOutRect)
5874 const gfx3DMatrix& matrix = To3DMatrix(GetTransform());
5875 if (matrix.IsSingular())
5876 return false;
5878 // GetTransform always operates in dev pixels.
5879 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
5880 Rect result(NSAppUnitsToFloatPixels(mVisibleRect.x, factor),
5881 NSAppUnitsToFloatPixels(mVisibleRect.y, factor),
5882 NSAppUnitsToFloatPixels(mVisibleRect.width, factor),
5883 NSAppUnitsToFloatPixels(mVisibleRect.height, factor));
5885 bool snap;
5886 nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
5887 Rect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
5888 NSAppUnitsToFloatPixels(childBounds.y, factor),
5889 NSAppUnitsToFloatPixels(childBounds.width, factor),
5890 NSAppUnitsToFloatPixels(childBounds.height, factor));
5892 /* We want to untransform the matrix, so invert the transformation first! */
5893 result = ToMatrix4x4(matrix.Inverse()).ProjectRectBounds(result);
5894 result = result.Intersect(childGfxBounds);
5896 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(ThebesRect(result), factor);
5898 return true;
5901 void
5902 nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream)
5904 AppendToString(aStream, GetTransform());
5907 nsDisplayItemGeometry*
5908 nsCharClipDisplayItem::AllocateGeometry(nsDisplayListBuilder* aBuilder)
5910 return new nsCharClipGeometry(this, aBuilder);
5913 void
5914 nsCharClipDisplayItem::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
5915 const nsDisplayItemGeometry* aGeometry,
5916 nsRegion* aInvalidRegion)
5918 const nsCharClipGeometry* geometry = static_cast<const nsCharClipGeometry*>(aGeometry);
5920 bool snap;
5921 nsRect newRect = geometry->mBounds;
5922 nsRect oldRect = GetBounds(aBuilder, &snap);
5923 if (mLeftEdge != geometry->mLeftEdge ||
5924 mRightEdge != geometry->mRightEdge ||
5925 !oldRect.IsEqualInterior(newRect) ||
5926 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
5927 aInvalidRegion->Or(oldRect, newRect);
5931 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
5932 nsIFrame* aFrame, nsDisplayList* aList)
5933 : nsDisplayWrapList(aBuilder, aFrame, aList),
5934 mEffectsBounds(aFrame->GetVisualOverflowRectRelativeToSelf())
5936 MOZ_COUNT_CTOR(nsDisplaySVGEffects);
5939 #ifdef NS_BUILD_REFCNT_LOGGING
5940 nsDisplaySVGEffects::~nsDisplaySVGEffects()
5942 MOZ_COUNT_DTOR(nsDisplaySVGEffects);
5944 #endif
5946 nsDisplayVR::nsDisplayVR(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
5947 nsDisplayList* aList, mozilla::gfx::VRHMDInfo* aHMD)
5948 : nsDisplayOwnLayer(aBuilder, aFrame, aList)
5949 , mHMD(aHMD)
5953 already_AddRefed<Layer>
5954 nsDisplayVR::BuildLayer(nsDisplayListBuilder* aBuilder,
5955 LayerManager* aManager,
5956 const ContainerLayerParameters& aContainerParameters)
5958 ContainerLayerParameters newContainerParameters = aContainerParameters;
5959 uint32_t flags = FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS;
5960 nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
5961 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
5962 newContainerParameters, nullptr, flags);
5964 container->SetVRHMDInfo(mHMD);
5965 container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
5966 /*the value is irrelevant*/nullptr);
5968 return container.forget();
5970 nsRegion nsDisplaySVGEffects::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5971 bool* aSnap)
5973 *aSnap = false;
5974 return nsRegion();
5977 void
5978 nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5979 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
5981 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
5982 if (nsSVGIntegrationUtils::HitTestFrameForEffects(mFrame,
5983 rectCenter - ToReferenceFrame())) {
5984 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
5988 void
5989 nsDisplaySVGEffects::PaintAsLayer(nsDisplayListBuilder* aBuilder,
5990 nsRenderingContext* aCtx,
5991 LayerManager* aManager)
5993 nsSVGIntegrationUtils::PaintFramesWithEffects(*aCtx->ThebesContext(), mFrame,
5994 mVisibleRect,
5995 aBuilder, aManager);
5998 LayerState
5999 nsDisplaySVGEffects::GetLayerState(nsDisplayListBuilder* aBuilder,
6000 LayerManager* aManager,
6001 const ContainerLayerParameters& aParameters)
6003 return LAYER_SVG_EFFECTS;
6006 already_AddRefed<Layer>
6007 nsDisplaySVGEffects::BuildLayer(nsDisplayListBuilder* aBuilder,
6008 LayerManager* aManager,
6009 const ContainerLayerParameters& aContainerParameters)
6011 const nsIContent* content = mFrame->GetContent();
6012 bool hasSVGLayout = (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
6013 if (hasSVGLayout) {
6014 nsISVGChildFrame *svgChildFrame = do_QueryFrame(mFrame);
6015 if (!svgChildFrame || !mFrame->GetContent()->IsSVG()) {
6016 NS_ASSERTION(false, "why?");
6017 return nullptr;
6019 if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
6020 return nullptr; // The SVG spec says not to draw filters for this
6024 float opacity = mFrame->StyleDisplay()->mOpacity;
6025 if (opacity == 0.0f)
6026 return nullptr;
6028 nsIFrame* firstFrame =
6029 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
6030 nsSVGEffects::EffectProperties effectProperties =
6031 nsSVGEffects::GetEffectProperties(firstFrame);
6033 bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
6034 effectProperties.GetClipPathFrame(&isOK);
6035 effectProperties.GetMaskFrame(&isOK);
6037 if (!isOK) {
6038 return nullptr;
6041 ContainerLayerParameters newContainerParameters = aContainerParameters;
6042 if (effectProperties.HasValidFilter()) {
6043 newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
6046 nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
6047 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
6048 newContainerParameters, nullptr);
6050 return container.forget();
6053 bool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder,
6054 nsRegion* aVisibleRegion) {
6055 nsPoint offset = ToReferenceFrame();
6056 nsRect dirtyRect =
6057 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mFrame,
6058 mVisibleRect - offset) +
6059 offset;
6061 // Our children may be made translucent or arbitrarily deformed so we should
6062 // not allow them to subtract area from aVisibleRegion.
6063 nsRegion childrenVisible(dirtyRect);
6064 nsRect r = dirtyRect.Intersect(mList.GetBounds(aBuilder));
6065 mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r);
6066 return true;
6069 bool nsDisplaySVGEffects::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem)
6071 if (aItem->GetType() != TYPE_SVG_EFFECTS)
6072 return false;
6073 // items for the same content element should be merged into a single
6074 // compositing group
6075 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects
6076 if (aItem->Frame()->GetContent() != mFrame->GetContent())
6077 return false;
6078 if (aItem->GetClip() != GetClip())
6079 return false;
6080 nsDisplaySVGEffects* other = static_cast<nsDisplaySVGEffects*>(aItem);
6081 MergeFromTrackingMergedFrames(other);
6082 mEffectsBounds.UnionRect(mEffectsBounds,
6083 other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame));
6084 return true;
6087 gfxRect
6088 nsDisplaySVGEffects::BBoxInUserSpace() const
6090 return nsSVGUtils::GetBBox(mFrame);
6093 gfxPoint
6094 nsDisplaySVGEffects::UserSpaceOffset() const
6096 return nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
6099 void
6100 nsDisplaySVGEffects::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
6101 const nsDisplayItemGeometry* aGeometry,
6102 nsRegion* aInvalidRegion)
6104 const nsDisplaySVGEffectsGeometry* geometry =
6105 static_cast<const nsDisplaySVGEffectsGeometry*>(aGeometry);
6106 bool snap;
6107 nsRect bounds = GetBounds(aBuilder, &snap);
6108 if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
6109 geometry->mUserSpaceOffset != UserSpaceOffset() ||
6110 !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
6111 // Filter and mask output can depend on the location of the frame's user
6112 // space and on the frame's BBox. We need to invalidate if either of these
6113 // change relative to the reference frame.
6114 // Invalidations from our inactive layer manager are not enough to catch
6115 // some of these cases because filters can produce output even if there's
6116 // nothing in the filter input.
6117 aInvalidRegion->Or(bounds, geometry->mBounds);
6121 #ifdef MOZ_DUMP_PAINTING
6122 void
6123 nsDisplaySVGEffects::PrintEffects(nsACString& aTo)
6125 nsIFrame* firstFrame =
6126 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
6127 nsSVGEffects::EffectProperties effectProperties =
6128 nsSVGEffects::GetEffectProperties(firstFrame);
6129 bool isOK = true;
6130 nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
6131 bool first = true;
6132 aTo += " effects=(";
6133 if (mFrame->StyleDisplay()->mOpacity != 1.0f) {
6134 first = false;
6135 aTo += nsPrintfCString("opacity(%f)", mFrame->StyleDisplay()->mOpacity);
6137 if (clipPathFrame) {
6138 if (!first) {
6139 aTo += ", ";
6141 aTo += nsPrintfCString("clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
6142 first = false;
6144 if (effectProperties.HasValidFilter()) {
6145 if (!first) {
6146 aTo += ", ";
6148 aTo += "filter";
6149 first = false;
6151 if (effectProperties.GetMaskFrame(&isOK)) {
6152 if (!first) {
6153 aTo += ", ";
6155 aTo += "mask";
6157 aTo += ")";
6159 #endif