Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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
11 */
13 #include "mozilla/dom/TabChild.h"
14 #include "mozilla/layers/PLayerTransaction.h"
16 #include "nsDisplayList.h"
18 #include "nsCSSRendering.h"
19 #include "nsRenderingContext.h"
20 #include "nsISelectionController.h"
21 #include "nsIPresShell.h"
22 #include "nsRegion.h"
23 #include "nsStyleStructInlines.h"
24 #include "nsStyleTransformMatrix.h"
25 #include "gfxMatrix.h"
26 #include "nsSVGIntegrationUtils.h"
27 #include "nsLayoutUtils.h"
28 #include "nsIScrollableFrame.h"
29 #include "nsIFrameInlines.h"
30 #include "nsThemeConstants.h"
31 #include "LayerTreeInvalidation.h"
33 #include "imgIContainer.h"
34 #include "BasicLayers.h"
35 #include "nsBoxFrame.h"
36 #include "nsViewportFrame.h"
37 #include "nsSubDocumentFrame.h"
38 #include "nsSVGEffects.h"
39 #include "nsSVGElement.h"
40 #include "nsSVGClipPathFrame.h"
41 #include "GeckoProfiler.h"
42 #include "nsAnimationManager.h"
43 #include "nsTransitionManager.h"
44 #include "nsViewManager.h"
45 #include "ImageLayers.h"
46 #include "ImageContainer.h"
47 #include "nsCanvasFrame.h"
48 #include "StickyScrollContainer.h"
49 #include "mozilla/EventStates.h"
50 #include "mozilla/LookAndFeel.h"
51 #include "mozilla/Preferences.h"
52 #include "ActiveLayerTracker.h"
53 #include "nsContentUtils.h"
54 #include "nsPrintfCString.h"
55 #include "UnitTransforms.h"
57 #include <stdint.h>
58 #include <algorithm>
60 using namespace mozilla;
61 using namespace mozilla::css;
62 using namespace mozilla::layers;
63 using namespace mozilla::dom;
64 typedef FrameMetrics::ViewID ViewID;
66 static inline nsIFrame*
67 GetTransformRootFrame(nsIFrame* aFrame)
68 {
69 return nsLayoutUtils::GetTransformRootFrame(aFrame);
70 }
72 static void AddTransformFunctions(nsCSSValueList* aList,
73 nsStyleContext* aContext,
74 nsPresContext* aPresContext,
75 nsRect& aBounds,
76 float aAppUnitsPerPixel,
77 InfallibleTArray<TransformFunction>& aFunctions)
78 {
79 if (aList->mValue.GetUnit() == eCSSUnit_None) {
80 return;
81 }
83 for (const nsCSSValueList* curr = aList; curr; curr = curr->mNext) {
84 const nsCSSValue& currElem = curr->mValue;
85 NS_ASSERTION(currElem.GetUnit() == eCSSUnit_Function,
86 "Stream should consist solely of functions!");
87 nsCSSValue::Array* array = currElem.GetArrayValue();
88 bool canStoreInRuleTree = true;
89 switch (nsStyleTransformMatrix::TransformFunctionOf(array)) {
90 case eCSSKeyword_rotatex:
91 {
92 double theta = array->Item(1).GetAngleValueInRadians();
93 aFunctions.AppendElement(RotationX(theta));
94 break;
95 }
96 case eCSSKeyword_rotatey:
97 {
98 double theta = array->Item(1).GetAngleValueInRadians();
99 aFunctions.AppendElement(RotationY(theta));
100 break;
101 }
102 case eCSSKeyword_rotatez:
103 {
104 double theta = array->Item(1).GetAngleValueInRadians();
105 aFunctions.AppendElement(RotationZ(theta));
106 break;
107 }
108 case eCSSKeyword_rotate:
109 {
110 double theta = array->Item(1).GetAngleValueInRadians();
111 aFunctions.AppendElement(Rotation(theta));
112 break;
113 }
114 case eCSSKeyword_rotate3d:
115 {
116 double x = array->Item(1).GetFloatValue();
117 double y = array->Item(2).GetFloatValue();
118 double z = array->Item(3).GetFloatValue();
119 double theta = array->Item(4).GetAngleValueInRadians();
120 aFunctions.AppendElement(Rotation3D(x, y, z, theta));
121 break;
122 }
123 case eCSSKeyword_scalex:
124 {
125 double x = array->Item(1).GetFloatValue();
126 aFunctions.AppendElement(Scale(x, 1, 1));
127 break;
128 }
129 case eCSSKeyword_scaley:
130 {
131 double y = array->Item(1).GetFloatValue();
132 aFunctions.AppendElement(Scale(1, y, 1));
133 break;
134 }
135 case eCSSKeyword_scalez:
136 {
137 double z = array->Item(1).GetFloatValue();
138 aFunctions.AppendElement(Scale(1, 1, z));
139 break;
140 }
141 case eCSSKeyword_scale:
142 {
143 double x = array->Item(1).GetFloatValue();
144 // scale(x) is shorthand for scale(x, x);
145 double y = array->Count() == 2 ? x : array->Item(2).GetFloatValue();
146 aFunctions.AppendElement(Scale(x, y, 1));
147 break;
148 }
149 case eCSSKeyword_scale3d:
150 {
151 double x = array->Item(1).GetFloatValue();
152 double y = array->Item(2).GetFloatValue();
153 double z = array->Item(3).GetFloatValue();
154 aFunctions.AppendElement(Scale(x, y, z));
155 break;
156 }
157 case eCSSKeyword_translatex:
158 {
159 double x = nsStyleTransformMatrix::ProcessTranslatePart(
160 array->Item(1), aContext, aPresContext, canStoreInRuleTree,
161 aBounds.Width(), aAppUnitsPerPixel);
162 aFunctions.AppendElement(Translation(x, 0, 0));
163 break;
164 }
165 case eCSSKeyword_translatey:
166 {
167 double y = nsStyleTransformMatrix::ProcessTranslatePart(
168 array->Item(1), aContext, aPresContext, canStoreInRuleTree,
169 aBounds.Height(), aAppUnitsPerPixel);
170 aFunctions.AppendElement(Translation(0, y, 0));
171 break;
172 }
173 case eCSSKeyword_translatez:
174 {
175 double z = nsStyleTransformMatrix::ProcessTranslatePart(
176 array->Item(1), aContext, aPresContext, canStoreInRuleTree,
177 0, aAppUnitsPerPixel);
178 aFunctions.AppendElement(Translation(0, 0, z));
179 break;
180 }
181 case eCSSKeyword_translate:
182 {
183 double x = nsStyleTransformMatrix::ProcessTranslatePart(
184 array->Item(1), aContext, aPresContext, canStoreInRuleTree,
185 aBounds.Width(), aAppUnitsPerPixel);
186 // translate(x) is shorthand for translate(x, 0)
187 double y = 0;
188 if (array->Count() == 3) {
189 y = nsStyleTransformMatrix::ProcessTranslatePart(
190 array->Item(2), aContext, aPresContext, canStoreInRuleTree,
191 aBounds.Height(), aAppUnitsPerPixel);
192 }
193 aFunctions.AppendElement(Translation(x, y, 0));
194 break;
195 }
196 case eCSSKeyword_translate3d:
197 {
198 double x = nsStyleTransformMatrix::ProcessTranslatePart(
199 array->Item(1), aContext, aPresContext, canStoreInRuleTree,
200 aBounds.Width(), aAppUnitsPerPixel);
201 double y = nsStyleTransformMatrix::ProcessTranslatePart(
202 array->Item(2), aContext, aPresContext, canStoreInRuleTree,
203 aBounds.Height(), aAppUnitsPerPixel);
204 double z = nsStyleTransformMatrix::ProcessTranslatePart(
205 array->Item(3), aContext, aPresContext, canStoreInRuleTree,
206 0, aAppUnitsPerPixel);
208 aFunctions.AppendElement(Translation(x, y, z));
209 break;
210 }
211 case eCSSKeyword_skewx:
212 {
213 double x = array->Item(1).GetAngleValueInRadians();
214 aFunctions.AppendElement(SkewX(x));
215 break;
216 }
217 case eCSSKeyword_skewy:
218 {
219 double y = array->Item(1).GetAngleValueInRadians();
220 aFunctions.AppendElement(SkewY(y));
221 break;
222 }
223 case eCSSKeyword_skew:
224 {
225 double x = array->Item(1).GetAngleValueInRadians();
226 // skew(x) is shorthand for skew(x, 0)
227 double y = 0;
228 if (array->Count() == 3) {
229 y = array->Item(2).GetAngleValueInRadians();
230 }
231 aFunctions.AppendElement(Skew(x, y));
232 break;
233 }
234 case eCSSKeyword_matrix:
235 {
236 gfx::Matrix4x4 matrix;
237 matrix._11 = array->Item(1).GetFloatValue();
238 matrix._12 = array->Item(2).GetFloatValue();
239 matrix._13 = 0;
240 matrix._14 = 0;
241 matrix._21 = array->Item(3).GetFloatValue();
242 matrix._22 = array->Item(4).GetFloatValue();
243 matrix._23 = 0;
244 matrix._24 = 0;
245 matrix._31 = 0;
246 matrix._32 = 0;
247 matrix._33 = 1;
248 matrix._34 = 0;
249 matrix._41 = array->Item(5).GetFloatValue();
250 matrix._42 = array->Item(6).GetFloatValue();
251 matrix._43 = 0;
252 matrix._44 = 1;
253 aFunctions.AppendElement(TransformMatrix(matrix));
254 break;
255 }
256 case eCSSKeyword_matrix3d:
257 {
258 gfx::Matrix4x4 matrix;
259 matrix._11 = array->Item(1).GetFloatValue();
260 matrix._12 = array->Item(2).GetFloatValue();
261 matrix._13 = array->Item(3).GetFloatValue();
262 matrix._14 = array->Item(4).GetFloatValue();
263 matrix._21 = array->Item(5).GetFloatValue();
264 matrix._22 = array->Item(6).GetFloatValue();
265 matrix._23 = array->Item(7).GetFloatValue();
266 matrix._24 = array->Item(8).GetFloatValue();
267 matrix._31 = array->Item(9).GetFloatValue();
268 matrix._32 = array->Item(10).GetFloatValue();
269 matrix._33 = array->Item(11).GetFloatValue();
270 matrix._34 = array->Item(12).GetFloatValue();
271 matrix._41 = array->Item(13).GetFloatValue();
272 matrix._42 = array->Item(14).GetFloatValue();
273 matrix._43 = array->Item(15).GetFloatValue();
274 matrix._44 = array->Item(16).GetFloatValue();
275 aFunctions.AppendElement(TransformMatrix(matrix));
276 break;
277 }
278 case eCSSKeyword_interpolatematrix:
279 {
280 gfx3DMatrix matrix;
281 nsStyleTransformMatrix::ProcessInterpolateMatrix(matrix, array,
282 aContext,
283 aPresContext,
284 canStoreInRuleTree,
285 aBounds,
286 aAppUnitsPerPixel);
287 gfx::Matrix4x4 transform;
288 gfx::ToMatrix4x4(matrix, transform);
289 aFunctions.AppendElement(TransformMatrix(transform));
290 break;
291 }
292 case eCSSKeyword_perspective:
293 {
294 aFunctions.AppendElement(Perspective(array->Item(1).GetFloatValue()));
295 break;
296 }
297 default:
298 NS_ERROR("Function not handled yet!");
299 }
300 }
301 }
303 static TimingFunction
304 ToTimingFunction(css::ComputedTimingFunction& aCTF)
305 {
306 if (aCTF.GetType() == nsTimingFunction::Function) {
307 const nsSMILKeySpline* spline = aCTF.GetFunction();
308 return TimingFunction(CubicBezierFunction(spline->X1(), spline->Y1(),
309 spline->X2(), spline->Y2()));
310 }
312 uint32_t type = aCTF.GetType() == nsTimingFunction::StepStart ? 1 : 2;
313 return TimingFunction(StepFunction(aCTF.GetSteps(), type));
314 }
316 static void
317 AddAnimationForProperty(nsIFrame* aFrame, nsCSSProperty aProperty,
318 mozilla::StyleAnimation* ea, Layer* aLayer,
319 AnimationData& aData, bool aPending)
320 {
321 NS_ASSERTION(aLayer->AsContainerLayer(), "Should only animate ContainerLayer");
322 nsStyleContext* styleContext = aFrame->StyleContext();
323 nsPresContext* presContext = aFrame->PresContext();
324 nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame);
325 // all data passed directly to the compositor should be in css pixels
326 float scale = nsDeviceContext::AppUnitsPerCSSPixel();
328 mozilla::layers::Animation* animation =
329 aPending ?
330 aLayer->AddAnimationForNextTransaction() :
331 aLayer->AddAnimation();
333 animation->startTime() = ea->mStartTime + ea->mDelay;
334 animation->duration() = ea->mIterationDuration;
335 animation->numIterations() =
336 ea->mIterationCount != NS_IEEEPositiveInfinity() ? ea->mIterationCount : -1;
337 animation->direction() = ea->mDirection;
338 animation->property() = aProperty;
339 animation->data() = aData;
341 for (uint32_t propIdx = 0; propIdx < ea->mProperties.Length(); propIdx++) {
342 AnimationProperty* property = &ea->mProperties[propIdx];
344 if (aProperty != property->mProperty) {
345 continue;
346 }
348 for (uint32_t segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) {
349 AnimationPropertySegment* segment = &property->mSegments[segIdx];
351 AnimationSegment* animSegment = animation->segments().AppendElement();
352 if (aProperty == eCSSProperty_transform) {
353 animSegment->startState() = InfallibleTArray<TransformFunction>();
354 animSegment->endState() = InfallibleTArray<TransformFunction>();
356 nsCSSValueSharedList* list = segment->mFromValue.GetCSSValueSharedListValue();
357 AddTransformFunctions(list->mHead, styleContext, presContext, bounds, scale,
358 animSegment->startState().get_ArrayOfTransformFunction());
360 list = segment->mToValue.GetCSSValueSharedListValue();
361 AddTransformFunctions(list->mHead, styleContext, presContext, bounds, scale,
362 animSegment->endState().get_ArrayOfTransformFunction());
363 } else if (aProperty == eCSSProperty_opacity) {
364 animSegment->startState() = segment->mFromValue.GetFloatValue();
365 animSegment->endState() = segment->mToValue.GetFloatValue();
366 }
368 animSegment->startPortion() = segment->mFromKey;
369 animSegment->endPortion() = segment->mToKey;
370 animSegment->sampleFn() = ToTimingFunction(segment->mTimingFunction);
371 }
372 }
373 }
375 template<class T>
376 static void
377 AddAnimationsForProperty(nsIFrame* aFrame, nsCSSProperty aProperty,
378 nsTArray<T>& aAnimations,
379 Layer* aLayer, AnimationData& aData,
380 bool aPending) {
381 mozilla::TimeStamp currentTime =
382 aFrame->PresContext()->RefreshDriver()->MostRecentRefresh();
383 for (uint32_t animIdx = 0; animIdx < aAnimations.Length(); animIdx++) {
384 mozilla::StyleAnimation* anim = &aAnimations[animIdx];
385 if (!(anim->HasAnimationOfProperty(aProperty) &&
386 anim->IsRunningAt(currentTime))) {
387 continue;
388 }
389 AddAnimationForProperty(aFrame, aProperty, anim, aLayer, aData, aPending);
390 anim->mIsRunningOnCompositor = true;
391 }
392 }
394 /* static */ void
395 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
396 nsDisplayListBuilder* aBuilder,
397 nsDisplayItem* aItem,
398 nsIFrame* aFrame,
399 nsCSSProperty aProperty)
400 {
401 // This function can be called in two ways: from
402 // nsDisplay*::BuildLayer while constructing a layer (with all
403 // pointers non-null), or from RestyleManager's handling of
404 // UpdateOpacityLayer/UpdateTransformLayer hints.
405 MOZ_ASSERT(!aBuilder == !aItem,
406 "should only be called in two configurations, with both "
407 "aBuilder and aItem, or with neither");
408 MOZ_ASSERT(!aItem || aFrame == aItem->Frame(), "frame mismatch");
410 bool pending = !aBuilder;
412 if (pending) {
413 aLayer->ClearAnimationsForNextTransaction();
414 } else {
415 aLayer->ClearAnimations();
416 }
418 nsIContent* content = aFrame->GetContent();
419 if (!content) {
420 return;
421 }
422 ElementTransitions* et =
423 nsTransitionManager::GetTransitionsForCompositor(content, aProperty);
425 ElementAnimations* ea =
426 nsAnimationManager::GetAnimationsForCompositor(content, aProperty);
428 if (!ea && !et) {
429 return;
430 }
432 // If the frame is not prerendered, bail out.
433 // Do this check only during layer construction; during updating the
434 // caller is required to check it appropriately.
435 if (aItem && !aItem->CanUseAsyncAnimations(aBuilder)) {
436 // AnimationManager or TransitionManager need to know that we refused to
437 // run this animation asynchronously so that they will not throttle the
438 // main thread animation.
439 aFrame->Properties().Set(nsIFrame::RefusedAsyncAnimation(),
440 reinterpret_cast<void*>(intptr_t(true)));
442 // We need to schedule another refresh driver run so that AnimationManager
443 // or TransitionManager get a chance to unthrottle the animation.
444 aFrame->SchedulePaint();
445 return;
446 }
448 AnimationData data;
449 if (aProperty == eCSSProperty_transform) {
450 nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame);
451 // all data passed directly to the compositor should be in css pixels
452 float scale = nsDeviceContext::AppUnitsPerCSSPixel();
453 gfxPoint3D offsetToTransformOrigin =
454 nsDisplayTransform::GetDeltaToTransformOrigin(aFrame, scale, &bounds);
455 gfxPoint3D offsetToPerspectiveOrigin =
456 nsDisplayTransform::GetDeltaToPerspectiveOrigin(aFrame, scale);
457 nscoord perspective = 0.0;
458 nsStyleContext* parentStyleContext = aFrame->StyleContext()->GetParent();
459 if (parentStyleContext) {
460 const nsStyleDisplay* disp = parentStyleContext->StyleDisplay();
461 if (disp && disp->mChildPerspective.GetUnit() == eStyleUnit_Coord) {
462 perspective = disp->mChildPerspective.GetCoordValue();
463 }
464 }
465 nsPoint origin;
466 if (aItem) {
467 origin = aItem->ToReferenceFrame();
468 } else {
469 // transform display items used a reference frame computed from
470 // their GetTransformRootFrame().
471 nsIFrame* referenceFrame =
472 nsLayoutUtils::GetReferenceFrame(GetTransformRootFrame(aFrame));
473 origin = aFrame->GetOffsetToCrossDoc(referenceFrame);
474 }
476 data = TransformData(origin, offsetToTransformOrigin,
477 offsetToPerspectiveOrigin, bounds, perspective,
478 aFrame->PresContext()->AppUnitsPerDevPixel());
479 } else if (aProperty == eCSSProperty_opacity) {
480 data = null_t();
481 }
483 if (et) {
484 AddAnimationsForProperty(aFrame, aProperty, et->mPropertyTransitions,
485 aLayer, data, pending);
486 aLayer->SetAnimationGeneration(et->mAnimationGeneration);
487 }
489 if (ea) {
490 AddAnimationsForProperty(aFrame, aProperty, ea->mAnimations,
491 aLayer, data, pending);
492 aLayer->SetAnimationGeneration(ea->mAnimationGeneration);
493 }
494 }
496 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
497 Mode aMode, bool aBuildCaret)
498 : mReferenceFrame(aReferenceFrame),
499 mIgnoreScrollFrame(nullptr),
500 mLayerEventRegions(nullptr),
501 mCurrentTableItem(nullptr),
502 mFinalTransparentRegion(nullptr),
503 mCachedOffsetFrame(aReferenceFrame),
504 mCachedReferenceFrame(aReferenceFrame),
505 mCachedOffset(0, 0),
506 mGlassDisplayItem(nullptr),
507 mMode(aMode),
508 mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
509 mBuildCaret(aBuildCaret),
510 mIgnoreSuppression(false),
511 mHadToIgnoreSuppression(false),
512 mIsAtRootOfPseudoStackingContext(false),
513 mIncludeAllOutOfFlows(false),
514 mDescendIntoSubdocuments(true),
515 mSelectedFramesOnly(false),
516 mAccurateVisibleRegions(false),
517 mAllowMergingAndFlattening(true),
518 mWillComputePluginGeometry(false),
519 mInTransform(false),
520 mInFixedPos(false),
521 mSyncDecodeImages(false),
522 mIsPaintingToWindow(false),
523 mIsCompositingCheap(false),
524 mContainsPluginItem(false),
525 mContainsBlendMode(false),
526 mAncestorHasTouchEventHandler(false),
527 mHaveScrollableDisplayPort(false)
528 {
529 MOZ_COUNT_CTOR(nsDisplayListBuilder);
530 PL_InitArenaPool(&mPool, "displayListArena", 1024,
531 std::max(NS_ALIGNMENT_OF(void*),NS_ALIGNMENT_OF(double))-1);
533 nsPresContext* pc = aReferenceFrame->PresContext();
534 nsIPresShell *shell = pc->PresShell();
535 if (pc->IsRenderingOnlySelection()) {
536 nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(shell));
537 if (selcon) {
538 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
539 getter_AddRefs(mBoundingSelection));
540 }
541 }
543 nsCSSRendering::BeginFrameTreesLocked();
544 PR_STATIC_ASSERT(nsDisplayItem::TYPE_MAX < (1 << nsDisplayItem::TYPE_BITS));
545 }
547 static void MarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame) {
548 for (nsIFrame* f = aFrame; f;
549 f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) {
550 if (f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)
551 return;
552 f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
553 if (f == aStopAtFrame) {
554 // we've reached a frame that we know will be painted, so we can stop.
555 break;
556 }
557 }
558 }
560 void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame,
561 nsIFrame* aFrame,
562 const nsRect& aDirtyRect)
563 {
564 nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
565 if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame)) {
566 NS_ASSERTION(aDirtyFrame == aFrame->GetParent(), "Dirty frame should be viewport frame");
567 // position: fixed items are reflowed into and only drawn inside the
568 // viewport, or the scroll position clamping scrollport size, if one is
569 // set.
570 nsIPresShell* ps = aFrame->PresContext()->PresShell();
571 dirtyRectRelativeToDirtyFrame.MoveTo(0, 0);
572 if (ps->IsScrollPositionClampingScrollPortSizeSet()) {
573 dirtyRectRelativeToDirtyFrame.SizeTo(ps->GetScrollPositionClampingScrollPortSize());
574 } else {
575 dirtyRectRelativeToDirtyFrame.SizeTo(aDirtyFrame->GetSize());
576 }
577 }
579 nsRect dirty = dirtyRectRelativeToDirtyFrame - aFrame->GetOffsetTo(aDirtyFrame);
580 nsRect overflowRect = aFrame->GetVisualOverflowRect();
582 if (aFrame->IsTransformed() &&
583 nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(),
584 eCSSProperty_transform)) {
585 /**
586 * Add a fuzz factor to the overflow rectangle so that elements only just
587 * out of view are pulled into the display list, so they can be
588 * prerendered if necessary.
589 */
590 overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
591 }
593 if (!dirty.IntersectRect(dirty, overflowRect))
594 return;
595 const DisplayItemClip* clip = mClipState.GetClipForContainingBlockDescendants();
596 OutOfFlowDisplayData* data = clip ? new OutOfFlowDisplayData(*clip, dirty)
597 : new OutOfFlowDisplayData(dirty);
598 aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
600 MarkFrameForDisplay(aFrame, aDirtyFrame);
601 }
603 static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
604 nsPresContext* presContext = aFrame->PresContext();
605 presContext->PropertyTable()->
606 Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty());
608 for (nsIFrame* f = aFrame; f;
609 f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) {
610 if (!(f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO))
611 return;
612 f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
613 }
614 }
616 static bool gPrintApzcTree = false;
618 static bool GetApzcTreePrintPref() {
619 static bool initialized = false;
620 if (!initialized) {
621 Preferences::AddBoolVarCache(&gPrintApzcTree, "apz.printtree", gPrintApzcTree);
622 initialized = true;
623 }
624 return gPrintApzcTree;
625 }
627 static void RecordFrameMetrics(nsIFrame* aForFrame,
628 nsIFrame* aScrollFrame,
629 const nsIFrame* aReferenceFrame,
630 ContainerLayer* aRoot,
631 const nsRect& aVisibleRect,
632 const nsRect& aViewport,
633 nsRect* aDisplayPort,
634 nsRect* aCriticalDisplayPort,
635 ViewID aScrollId,
636 bool aIsRoot,
637 const ContainerLayerParameters& aContainerParameters) {
638 nsPresContext* presContext = aForFrame->PresContext();
639 int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
640 LayoutDeviceToLayerScale resolution(aContainerParameters.mXScale, aContainerParameters.mYScale);
642 nsIntRect visible = aVisibleRect.ScaleToNearestPixels(
643 resolution.scale, resolution.scale, auPerDevPixel);
644 aRoot->SetVisibleRegion(visible);
646 FrameMetrics metrics;
647 metrics.mViewport = CSSRect::FromAppUnits(aViewport);
648 if (aDisplayPort) {
649 metrics.mDisplayPort = CSSRect::FromAppUnits(*aDisplayPort);
650 if (aCriticalDisplayPort) {
651 metrics.mCriticalDisplayPort = CSSRect::FromAppUnits(*aCriticalDisplayPort);
652 }
653 }
655 nsIScrollableFrame* scrollableFrame = nullptr;
656 if (aScrollFrame)
657 scrollableFrame = aScrollFrame->GetScrollTargetFrame();
659 metrics.mScrollableRect = CSSRect::FromAppUnits(
660 nsLayoutUtils::CalculateScrollableRectForFrame(scrollableFrame, aForFrame));
662 if (scrollableFrame) {
663 nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
664 metrics.SetScrollOffset(CSSPoint::FromAppUnits(scrollPosition));
666 // If the frame was scrolled since the last layers update, and by
667 // something other than the APZ code, we want to tell the APZ to update
668 // its scroll offset.
669 nsIAtom* originOfLastScroll = scrollableFrame->OriginOfLastScroll();
670 if (originOfLastScroll && originOfLastScroll != nsGkAtoms::apz) {
671 metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
672 }
673 }
675 metrics.SetScrollId(aScrollId);
676 metrics.mIsRoot = aIsRoot;
678 // Only the root scrollable frame for a given presShell should pick up
679 // the presShell's resolution. All the other frames are 1.0.
680 nsIPresShell* presShell = presContext->GetPresShell();
681 if (aScrollFrame == presShell->GetRootScrollFrame()) {
682 metrics.mResolution = ParentLayerToLayerScale(presShell->GetXResolution(),
683 presShell->GetYResolution());
684 } else {
685 metrics.mResolution = ParentLayerToLayerScale(1.0f);
686 }
688 // For the cumulateive resolution, multiply the resolutions of all the
689 // presShells back up to the root
690 metrics.mCumulativeResolution = LayoutDeviceToLayerScale(1.0f);
691 nsIPresShell* curPresShell = presShell;
692 while (curPresShell != nullptr) {
693 ParentLayerToLayerScale presShellResolution(curPresShell->GetXResolution(),
694 curPresShell->GetYResolution());
695 metrics.mCumulativeResolution.scale *= presShellResolution.scale;
696 nsPresContext* parentContext = curPresShell->GetPresContext()->GetParentPresContext();
697 curPresShell = parentContext ? parentContext->GetPresShell() : nullptr;
698 }
700 metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(
701 (float)nsPresContext::AppUnitsPerCSSPixel() / auPerDevPixel);
703 // Initially, AsyncPanZoomController should render the content to the screen
704 // at the painted resolution.
705 const LayerToScreenScale layerToScreenScale(1.0f);
706 metrics.SetZoom(metrics.mCumulativeResolution * metrics.mDevPixelsPerCSSPixel
707 * layerToScreenScale);
709 if (presShell) {
710 nsIDocument* document = nullptr;
711 document = presShell->GetDocument();
712 if (document) {
713 nsCOMPtr<nsPIDOMWindow> innerWin(document->GetInnerWindow());
714 if (innerWin) {
715 metrics.mMayHaveTouchListeners = innerWin->HasTouchEventListeners();
716 }
717 }
718 }
720 LayoutDeviceToParentLayerScale layoutToParentLayerScale =
721 // The ScreenToParentLayerScale should be mTransformScale which is not calculated yet,
722 // but we don't yet handle CSS transforms, so we assume it's 1 here.
723 metrics.mCumulativeResolution * LayerToScreenScale(1.0) * ScreenToParentLayerScale(1.0);
725 // Calculate the composition bounds as the size of the scroll frame and
726 // its origin relative to the reference frame.
727 // If aScrollFrame is null, we are in a document without a root scroll frame,
728 // so it's a xul document. In this case, use the size of the viewport frame.
729 nsIFrame* frameForCompositionBoundsCalculation = aScrollFrame ? aScrollFrame : aForFrame;
730 nsRect compositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame),
731 frameForCompositionBoundsCalculation->GetSize());
732 metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel)
733 * layoutToParentLayerScale);
736 // For the root scroll frame of the root content document, the above calculation
737 // will yield the size of the viewport frame as the composition bounds, which
738 // doesn't actually correspond to what is visible when
739 // nsIDOMWindowUtils::setCSSViewport has been called to modify the visible area of
740 // the prescontext that the viewport frame is reflowed into. In that case if our
741 // document has a widget then the widget's bounds will correspond to what is
742 // visible. If we don't have a widget the root view's bounds correspond to what
743 // would be visible because they don't get modified by setCSSViewport.
744 bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument()
745 && aScrollFrame == presShell->GetRootScrollFrame();
746 if (isRootContentDocRootScrollFrame) {
747 if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
748 if (nsView* view = rootFrame->GetView()) {
749 nsRect viewBoundsAppUnits = view->GetBounds() + rootFrame->GetOffsetToCrossDoc(aReferenceFrame);
750 ParentLayerIntRect viewBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(viewBoundsAppUnits, auPerDevPixel)
751 * layoutToParentLayerScale);
753 // On Android, we need to do things a bit differently to get things
754 // right (see bug 983208, bug 988882). We use the bounds of the nearest
755 // widget, but clamp the height to the view bounds height. This clamping
756 // is done to get correct results for a page where the page is sized to
757 // the screen and thus the dynamic toolbar never disappears. In such a
758 // case, we want the composition bounds to exclude the toolbar height,
759 // but the widget bounds includes it. We don't currently have a good way
760 // of knowing about the toolbar height, but clamping to the view bounds
761 // height gives the correct answer in the cases we care about.
762 nsIWidget* widget =
763 #ifdef MOZ_WIDGET_ANDROID
764 rootFrame->GetNearestWidget();
765 #else
766 view->GetWidget();
767 #endif
768 if (widget) {
769 nsIntRect widgetBounds;
770 widget->GetBounds(widgetBounds);
771 metrics.mCompositionBounds = ViewAs<ParentLayerPixel>(widgetBounds);
772 #ifdef MOZ_WIDGET_ANDROID
773 if (viewBounds.height < metrics.mCompositionBounds.height) {
774 metrics.mCompositionBounds.height = viewBounds.height;
775 }
776 #endif
777 } else {
778 metrics.mCompositionBounds = viewBounds;
779 }
780 }
781 }
782 }
784 // Adjust composition bounds for the size of scroll bars.
785 if (scrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) {
786 nsMargin sizes = scrollableFrame->GetActualScrollbarSizes();
787 // Scrollbars are not subject to scaling, so CSS pixels = layer pixels for them.
788 ParentLayerIntMargin boundMargins = RoundedToInt(CSSMargin::FromAppUnits(sizes) * CSSToParentLayerScale(1.0f));
789 metrics.mCompositionBounds.Deflate(boundMargins);
790 }
792 metrics.SetRootCompositionSize(
793 nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame ? aScrollFrame : aForFrame,
794 isRootContentDocRootScrollFrame, metrics));
796 if (GetApzcTreePrintPref()) {
797 if (nsIContent* content = frameForCompositionBoundsCalculation->GetContent()) {
798 nsAutoString contentDescription;
799 content->Describe(contentDescription);
800 metrics.SetContentDescription(NS_LossyConvertUTF16toASCII(contentDescription).get());
801 }
802 }
804 metrics.mPresShellId = presShell->GetPresShellId();
806 // If the scroll frame's content is marked 'scrollgrab', record this
807 // in the FrameMetrics so APZ knows to provide the scroll grabbing
808 // behaviour.
809 if (aScrollFrame && nsContentUtils::HasScrollgrab(aScrollFrame->GetContent())) {
810 metrics.mHasScrollgrab = true;
811 }
813 aRoot->SetFrameMetrics(metrics);
814 }
816 nsDisplayListBuilder::~nsDisplayListBuilder() {
817 NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
818 "All frames should have been unmarked");
819 NS_ASSERTION(mPresShellStates.Length() == 0,
820 "All presshells should have been exited");
821 NS_ASSERTION(!mCurrentTableItem, "No table item should be active");
823 nsCSSRendering::EndFrameTreesLocked();
825 for (uint32_t i = 0; i < mDisplayItemClipsToDestroy.Length(); ++i) {
826 mDisplayItemClipsToDestroy[i]->DisplayItemClip::~DisplayItemClip();
827 }
829 PL_FinishArenaPool(&mPool);
830 MOZ_COUNT_DTOR(nsDisplayListBuilder);
831 }
833 uint32_t
834 nsDisplayListBuilder::GetBackgroundPaintFlags() {
835 uint32_t flags = 0;
836 if (mSyncDecodeImages) {
837 flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
838 }
839 if (mIsPaintingToWindow) {
840 flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
841 }
842 return flags;
843 }
845 void
846 nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
847 const nsRegion& aRegion)
848 {
849 if (aRegion.IsEmpty())
850 return;
852 nsRegion tmp;
853 tmp.Sub(*aVisibleRegion, aRegion);
854 // Don't let *aVisibleRegion get too complex, but don't let it fluff out
855 // to its bounds either, which can be very bad (see bug 516740).
856 // Do let aVisibleRegion get more complex if by doing so we reduce its
857 // area by at least half.
858 if (GetAccurateVisibleRegions() || tmp.GetNumRects() <= 15 ||
859 tmp.Area() <= aVisibleRegion->Area()/2) {
860 *aVisibleRegion = tmp;
861 }
862 }
864 nsCaret *
865 nsDisplayListBuilder::GetCaret() {
866 nsRefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
867 return caret;
868 }
870 void
871 nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
872 const nsRect& aDirtyRect) {
873 PresShellState* state = mPresShellStates.AppendElement();
874 if (!state)
875 return;
876 state->mPresShell = aReferenceFrame->PresContext()->PresShell();
877 state->mCaretFrame = nullptr;
878 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
880 state->mPresShell->UpdateCanvasBackground();
882 if (mIsPaintingToWindow) {
883 mReferenceFrame->AddPaintedPresShell(state->mPresShell);
885 state->mPresShell->IncrementPaintCount();
886 }
888 bool buildCaret = mBuildCaret;
889 if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
890 if (state->mPresShell->IsPaintingSuppressed()) {
891 mHadToIgnoreSuppression = true;
892 }
893 state->mIsBackgroundOnly = false;
894 } else {
895 state->mIsBackgroundOnly = true;
896 buildCaret = false;
897 }
899 if (!buildCaret)
900 return;
902 nsRefPtr<nsCaret> caret = state->mPresShell->GetCaret();
903 state->mCaretFrame = caret->GetCaretFrame();
904 NS_ASSERTION(state->mCaretFrame == caret->GetCaretFrame(),
905 "GetCaretFrame() is unstable");
907 if (state->mCaretFrame) {
908 // Check if the dirty rect intersects with the caret's dirty rect.
909 nsRect caretRect =
910 caret->GetCaretRect() + state->mCaretFrame->GetOffsetTo(aReferenceFrame);
911 if (caretRect.Intersects(aDirtyRect)) {
912 // Okay, our rects intersect, let's mark the frame and all of its ancestors.
913 mFramesMarkedForDisplay.AppendElement(state->mCaretFrame);
914 MarkFrameForDisplay(state->mCaretFrame, nullptr);
915 }
916 }
917 }
919 void
920 nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame,
921 const nsRect& aDirtyRect) {
922 if (CurrentPresShellState()->mPresShell != aReferenceFrame->PresContext()->PresShell()) {
923 // Must have not allocated a state for this presshell, presumably due
924 // to OOM.
925 return;
926 }
928 ResetMarkedFramesForDisplayList();
929 mPresShellStates.SetLength(mPresShellStates.Length() - 1);
930 }
932 void
933 nsDisplayListBuilder::ResetMarkedFramesForDisplayList()
934 {
935 // Unmark and pop off the frames marked for display in this pres shell.
936 uint32_t firstFrameForShell = CurrentPresShellState()->mFirstFrameMarkedForDisplay;
937 for (uint32_t i = firstFrameForShell;
938 i < mFramesMarkedForDisplay.Length(); ++i) {
939 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]);
940 }
941 mFramesMarkedForDisplay.SetLength(firstFrameForShell);
942 }
944 void
945 nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame,
946 const nsFrameList& aFrames,
947 const nsRect& aDirtyRect) {
948 mFramesMarkedForDisplay.SetCapacity(mFramesMarkedForDisplay.Length() + aFrames.GetLength());
949 for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) {
950 mFramesMarkedForDisplay.AppendElement(e.get());
951 MarkOutOfFlowFrameForDisplay(aDirtyFrame, e.get(), aDirtyRect);
952 }
953 }
955 void
956 nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect)
957 {
958 nsAutoTArray<nsIFrame::ChildList,4> childListArray;
959 aDirtyFrame->GetChildLists(&childListArray);
960 nsIFrame::ChildListArrayIterator lists(childListArray);
961 for (; !lists.IsDone(); lists.Next()) {
962 nsFrameList::Enumerator childFrames(lists.CurrentList());
963 for (; !childFrames.AtEnd(); childFrames.Next()) {
964 nsIFrame *child = childFrames.get();
965 if (child->Preserves3D()) {
966 mFramesMarkedForDisplay.AppendElement(child);
967 nsRect dirty = aDirtyRect - child->GetOffsetTo(aDirtyFrame);
969 child->Properties().Set(nsDisplayListBuilder::Preserve3DDirtyRectProperty(),
970 new nsRect(dirty));
972 MarkFrameForDisplay(child, aDirtyFrame);
973 }
974 }
975 }
976 }
978 void*
979 nsDisplayListBuilder::Allocate(size_t aSize) {
980 void *tmp;
981 PL_ARENA_ALLOCATE(tmp, &mPool, aSize);
982 if (!tmp) {
983 NS_RUNTIMEABORT("out of memory");
984 }
985 return tmp;
986 }
988 const DisplayItemClip*
989 nsDisplayListBuilder::AllocateDisplayItemClip(const DisplayItemClip& aOriginal)
990 {
991 void* p = Allocate(sizeof(DisplayItemClip));
992 if (!aOriginal.GetRoundedRectCount()) {
993 memcpy(p, &aOriginal, sizeof(DisplayItemClip));
994 return static_cast<DisplayItemClip*>(p);
995 }
997 DisplayItemClip* c = new (p) DisplayItemClip(aOriginal);
998 mDisplayItemClipsToDestroy.AppendElement(c);
999 return c;
1000 }
1002 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const
1003 {
1004 aDestination.BorderBackground()->AppendToTop(BorderBackground());
1005 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
1006 aDestination.Floats()->AppendToTop(Floats());
1007 aDestination.Content()->AppendToTop(Content());
1008 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
1009 aDestination.Outlines()->AppendToTop(Outlines());
1010 }
1012 void
1013 nsDisplayList::FlattenTo(nsTArray<nsDisplayItem*>* aElements) {
1014 nsDisplayItem* item;
1015 while ((item = RemoveBottom()) != nullptr) {
1016 if (item->GetType() == nsDisplayItem::TYPE_WRAP_LIST) {
1017 item->GetSameCoordinateSystemChildren()->FlattenTo(aElements);
1018 item->~nsDisplayItem();
1019 } else {
1020 aElements->AppendElement(item);
1021 }
1022 }
1023 }
1025 nsRect
1026 nsDisplayList::GetBounds(nsDisplayListBuilder* aBuilder) const {
1027 nsRect bounds;
1028 for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) {
1029 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder));
1030 }
1031 return bounds;
1032 }
1034 bool
1035 nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder,
1036 nsRegion* aVisibleRegion,
1037 nsIFrame* aDisplayPortFrame) {
1038 PROFILER_LABEL("nsDisplayList", "ComputeVisibilityForRoot");
1039 nsRegion r;
1040 r.And(*aVisibleRegion, GetBounds(aBuilder));
1041 return ComputeVisibilityForSublist(aBuilder, aVisibleRegion,
1042 r.GetBounds(), r.GetBounds(),
1043 aDisplayPortFrame);
1044 }
1046 static nsRegion
1047 TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
1048 {
1049 bool snap;
1050 nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap);
1051 if (aBuilder->IsForPluginGeometry()) {
1052 // Treat all leaf chrome items as opaque, unless their frames are opacity:0.
1053 // Since opacity:0 frames generate an nsDisplayOpacity, that item will
1054 // not be treated as opaque here, so opacity:0 chrome content will be
1055 // effectively ignored, as it should be.
1056 // We treat leaf chrome items as opaque to ensure that they cover
1057 // content plugins, for security reasons.
1058 // Non-leaf chrome items don't render contents of their own so shouldn't
1059 // be treated as opaque (and their bounds is just the union of their
1060 // children, which might be a large area their contents don't really cover).
1061 nsIFrame* f = aItem->Frame();
1062 if (f->PresContext()->IsChrome() && !aItem->GetChildren() &&
1063 f->StyleDisplay()->mOpacity != 0.0) {
1064 opaque = aItem->GetBounds(aBuilder, &snap);
1065 }
1066 }
1067 if (opaque.IsEmpty()) {
1068 return opaque;
1069 }
1070 nsRegion opaqueClipped;
1071 nsRegionRectIterator iter(opaque);
1072 for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
1073 opaqueClipped.Or(opaqueClipped, aItem->GetClip().ApproximateIntersectInward(*r));
1074 }
1075 return opaqueClipped;
1076 }
1078 /* Checks if aPotentialScrollItem is a scroll layer item and aPotentialScrollbarItem
1079 * is an overlay scrollbar item for the same scroll frame.
1080 */
1081 static bool
1082 IsScrollLayerItemAndOverlayScrollbarForScrollFrame(
1083 nsDisplayItem* aPotentialScrollItem, nsDisplayItem* aPotentialScrollbarItem)
1084 {
1085 if (aPotentialScrollItem->GetType() == nsDisplayItem::TYPE_SCROLL_LAYER &&
1086 aPotentialScrollbarItem &&
1087 aPotentialScrollbarItem->GetType() == nsDisplayItem::TYPE_OWN_LAYER &&
1088 LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) {
1089 nsDisplayScrollLayer* scrollItem =
1090 static_cast<nsDisplayScrollLayer*>(aPotentialScrollItem);
1091 nsDisplayOwnLayer* layerItem =
1092 static_cast<nsDisplayOwnLayer*>(aPotentialScrollbarItem);
1093 if ((layerItem->GetFlags() &
1094 (nsDisplayOwnLayer::VERTICAL_SCROLLBAR |
1095 nsDisplayOwnLayer::HORIZONTAL_SCROLLBAR)) &&
1096 layerItem->Frame()->GetParent() == scrollItem->GetScrollFrame()) {
1097 return true;
1098 }
1099 }
1100 return false;
1101 }
1103 bool
1104 nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
1105 nsRegion* aVisibleRegion,
1106 const nsRect& aListVisibleBounds,
1107 const nsRect& aAllowVisibleRegionExpansion,
1108 nsIFrame* aDisplayPortFrame) {
1109 #ifdef DEBUG
1110 nsRegion r;
1111 r.And(*aVisibleRegion, GetBounds(aBuilder));
1112 NS_ASSERTION(r.GetBounds().IsEqualInterior(aListVisibleBounds),
1113 "bad aListVisibleBounds");
1114 #endif
1116 mVisibleRect = aListVisibleBounds;
1117 bool anyVisible = false;
1119 nsAutoTArray<nsDisplayItem*, 512> elements;
1120 FlattenTo(&elements);
1122 bool forceTransparentSurface = false;
1124 for (int32_t i = elements.Length() - 1; i >= 0; --i) {
1125 nsDisplayItem* item = elements[i];
1126 nsDisplayItem* belowItem = i < 1 ? nullptr : elements[i - 1];
1128 nsDisplayList* list = item->GetSameCoordinateSystemChildren();
1129 if (aBuilder->AllowMergingAndFlattening()) {
1130 if (belowItem && item->TryMerge(aBuilder, belowItem)) {
1131 belowItem->~nsDisplayItem();
1132 elements.ReplaceElementsAt(i - 1, 1, item);
1133 continue;
1134 }
1136 // If an overlay scrollbar item is between a scroll layer item and the
1137 // other scroll layer items that we need to merge with just move the
1138 // scrollbar item up, that way it will be on top of the scrolled content
1139 // and we can try to merge all the scroll layer items.
1140 if (IsScrollLayerItemAndOverlayScrollbarForScrollFrame(item, belowItem)) {
1141 elements[i] = belowItem;
1142 elements[i-1] = item;
1143 i++;
1144 continue;
1145 }
1147 if (list && item->ShouldFlattenAway(aBuilder)) {
1148 // The elements on the list >= i no longer serve any use.
1149 elements.SetLength(i);
1150 list->FlattenTo(&elements);
1151 i = elements.Length();
1152 item->~nsDisplayItem();
1153 continue;
1154 }
1155 }
1157 nsRect bounds = item->GetClippedBounds(aBuilder);
1159 nsRegion itemVisible;
1160 itemVisible.And(*aVisibleRegion, bounds);
1161 item->mVisibleRect = itemVisible.GetBounds();
1163 if (item->ComputeVisibility(aBuilder, aVisibleRegion,
1164 aAllowVisibleRegionExpansion.Intersect(bounds))) {
1165 anyVisible = true;
1167 // If we're in a displayport, we need to make sure that fixed position
1168 // items do not subtract from the visible region, as async scrolling
1169 // may expose these occluded areas.
1170 // If the item is fixed pos in the same document as the displayport
1171 // then don't let it occlude this list. The only other case possible
1172 // is that the fixed pos content is in a child document, in which it
1173 // would scroll with the rest of the content.
1174 bool occlude = true;
1175 if (aDisplayPortFrame && item->IsInFixedPos()) {
1176 if (item->Frame()->PresContext() == aDisplayPortFrame->PresContext()) {
1177 occlude = false;
1178 }
1179 }
1181 if (occlude) {
1182 nsRegion opaque = TreatAsOpaque(item, aBuilder);
1183 // Subtract opaque item from the visible region
1184 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque);
1185 }
1187 if (aBuilder->NeedToForceTransparentSurfaceForItem(item) ||
1188 (list && list->NeedsTransparentSurface())) {
1189 forceTransparentSurface = true;
1190 }
1191 }
1192 AppendToBottom(item);
1193 }
1195 mIsOpaque = !aVisibleRegion->Intersects(mVisibleRect);
1196 mForceTransparentSurface = forceTransparentSurface;
1197 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
1198 mDidComputeVisibility = true;
1199 #endif
1200 return anyVisible;
1201 }
1203 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder,
1204 nsRenderingContext* aCtx,
1205 uint32_t aFlags) const {
1206 PROFILER_LABEL("nsDisplayList", "PaintRoot");
1207 PaintForFrame(aBuilder, aCtx, aBuilder->RootReferenceFrame(), aFlags);
1208 }
1210 /**
1211 * We paint by executing a layer manager transaction, constructing a
1212 * single layer representing the display list, and then making it the
1213 * root of the layer manager, drawing into the ThebesLayers.
1214 */
1215 void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
1216 nsRenderingContext* aCtx,
1217 nsIFrame* aForFrame,
1218 uint32_t aFlags) const {
1219 NS_ASSERTION(mDidComputeVisibility,
1220 "Must call ComputeVisibility before calling Paint");
1222 nsRefPtr<LayerManager> layerManager;
1223 bool widgetTransaction = false;
1224 bool allowRetaining = false;
1225 bool doBeginTransaction = true;
1226 nsView *view = nullptr;
1227 if (aFlags & PAINT_USE_WIDGET_LAYERS) {
1228 nsIFrame* rootReferenceFrame = aBuilder->RootReferenceFrame();
1229 view = rootReferenceFrame->GetView();
1230 NS_ASSERTION(rootReferenceFrame == nsLayoutUtils::GetDisplayRootFrame(rootReferenceFrame),
1231 "Reference frame must be a display root for us to use the layer manager");
1232 nsIWidget* window = rootReferenceFrame->GetNearestWidget();
1233 if (window) {
1234 layerManager = window->GetLayerManager(&allowRetaining);
1235 if (layerManager) {
1236 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION);
1237 widgetTransaction = true;
1238 }
1239 }
1240 }
1241 if (!layerManager) {
1242 if (!aCtx) {
1243 NS_WARNING("Nowhere to paint into");
1244 return;
1245 }
1246 layerManager = new BasicLayerManager();
1247 }
1249 // Store the existing layer builder to reinstate it on return.
1250 FrameLayerBuilder *oldBuilder = layerManager->GetLayerBuilder();
1252 FrameLayerBuilder *layerBuilder = new FrameLayerBuilder();
1253 layerBuilder->Init(aBuilder, layerManager);
1255 if (aFlags & PAINT_COMPRESSED) {
1256 layerBuilder->SetLayerTreeCompressionMode();
1257 }
1259 if (aFlags & PAINT_FLUSH_LAYERS) {
1260 FrameLayerBuilder::InvalidateAllLayers(layerManager);
1261 }
1263 if (doBeginTransaction) {
1264 if (aCtx) {
1265 layerManager->BeginTransactionWithTarget(aCtx->ThebesContext());
1266 } else {
1267 layerManager->BeginTransaction();
1268 }
1269 }
1270 if (widgetTransaction) {
1271 layerBuilder->DidBeginRetainedLayerTransaction(layerManager);
1272 }
1274 nsPresContext* presContext = aForFrame->PresContext();
1275 nsIPresShell* presShell = presContext->GetPresShell();
1277 NotifySubDocInvalidationFunc computeInvalidFunc =
1278 presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0;
1279 bool computeInvalidRect = (computeInvalidFunc ||
1280 (!layerManager->IsCompositingCheap() && layerManager->NeedsWidgetInvalidation())) &&
1281 widgetTransaction;
1283 nsAutoPtr<LayerProperties> props(computeInvalidRect ?
1284 LayerProperties::CloneFrom(layerManager->GetRoot()) :
1285 nullptr);
1287 ContainerLayerParameters containerParameters
1288 (presShell->GetXResolution(), presShell->GetYResolution());
1289 nsRefPtr<ContainerLayer> root = layerBuilder->
1290 BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nullptr, *this,
1291 containerParameters, nullptr);
1293 nsIDocument* document = nullptr;
1294 if (presShell) {
1295 document = presShell->GetDocument();
1296 }
1298 if (widgetTransaction ||
1299 // SVG-as-an-image docs don't paint as part of the retained layer tree,
1300 // but they still need the invalidation state bits cleared in order for
1301 // invalidation for CSS/SMIL animation to work properly.
1302 (document && document->IsBeingUsedAsImage())) {
1303 aForFrame->ClearInvalidationStateBits();
1304 }
1306 if (!root) {
1307 layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
1308 return;
1309 }
1310 // Root is being scaled up by the X/Y resolution. Scale it back down.
1311 root->SetPostScale(1.0f/containerParameters.mXScale,
1312 1.0f/containerParameters.mYScale);
1314 ViewID id = FrameMetrics::NULL_SCROLL_ID;
1315 bool isRoot = presContext->IsRootContentDocument();
1317 nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
1318 nsRect displayport, criticalDisplayport;
1319 bool usingDisplayport = false;
1320 bool usingCriticalDisplayport = false;
1321 if (rootScrollFrame) {
1322 nsIContent* content = rootScrollFrame->GetContent();
1323 if (content) {
1324 usingDisplayport = nsLayoutUtils::GetDisplayPort(content, &displayport);
1325 usingCriticalDisplayport =
1326 nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport);
1328 // If this is the root content document, we want it to have a scroll id.
1329 if (isRoot) {
1330 id = nsLayoutUtils::FindOrCreateIDFor(content);
1331 }
1332 }
1333 }
1335 nsRect viewport(aBuilder->ToReferenceFrame(aForFrame), aForFrame->GetSize());
1337 RecordFrameMetrics(aForFrame, rootScrollFrame,
1338 aBuilder->FindReferenceFrameFor(aForFrame),
1339 root, mVisibleRect, viewport,
1340 (usingDisplayport ? &displayport : nullptr),
1341 (usingCriticalDisplayport ? &criticalDisplayport : nullptr),
1342 id, isRoot, containerParameters);
1343 if (usingDisplayport &&
1344 !(root->GetContentFlags() & Layer::CONTENT_OPAQUE)) {
1345 // See bug 693938, attachment 567017
1346 NS_WARNING("Transparent content with displayports can be expensive.");
1347 }
1349 layerManager->SetRoot(root);
1350 layerBuilder->WillEndTransaction();
1351 bool temp = aBuilder->SetIsCompositingCheap(layerManager->IsCompositingCheap());
1352 LayerManager::EndTransactionFlags flags = LayerManager::END_DEFAULT;
1353 if (layerManager->NeedsWidgetInvalidation()) {
1354 if (aFlags & PAINT_NO_COMPOSITE) {
1355 flags = LayerManager::END_NO_COMPOSITE;
1356 }
1357 } else {
1358 // Client layer managers never composite directly, so
1359 // we don't need to worry about END_NO_COMPOSITE.
1360 if (aBuilder->WillComputePluginGeometry()) {
1361 flags = LayerManager::END_NO_REMOTE_COMPOSITE;
1362 }
1363 }
1365 layerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer,
1366 aBuilder, flags);
1367 aBuilder->SetIsCompositingCheap(temp);
1368 layerBuilder->DidEndTransaction();
1370 nsIntRegion invalid;
1371 if (props) {
1372 invalid = props->ComputeDifferences(root, computeInvalidFunc);
1373 } else if (widgetTransaction) {
1374 LayerProperties::ClearInvalidations(root);
1375 }
1377 bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
1378 if (view) {
1379 if (props) {
1380 if (!invalid.IsEmpty()) {
1381 nsIntRect bounds = invalid.GetBounds();
1382 nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
1383 presContext->DevPixelsToAppUnits(bounds.y),
1384 presContext->DevPixelsToAppUnits(bounds.width),
1385 presContext->DevPixelsToAppUnits(bounds.height));
1386 if (shouldInvalidate) {
1387 view->GetViewManager()->InvalidateViewNoSuppression(view, rect);
1388 }
1389 presContext->NotifyInvalidation(bounds, 0);
1390 }
1391 } else if (shouldInvalidate) {
1392 view->GetViewManager()->InvalidateView(view);
1393 }
1394 }
1396 if (aFlags & PAINT_FLUSH_LAYERS) {
1397 FrameLayerBuilder::InvalidateAllLayers(layerManager);
1398 }
1400 layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
1401 }
1403 uint32_t nsDisplayList::Count() const {
1404 uint32_t count = 0;
1405 for (nsDisplayItem* i = GetBottom(); i; i = i->GetAbove()) {
1406 ++count;
1407 }
1408 return count;
1409 }
1411 nsDisplayItem* nsDisplayList::RemoveBottom() {
1412 nsDisplayItem* item = mSentinel.mAbove;
1413 if (!item)
1414 return nullptr;
1415 mSentinel.mAbove = item->mAbove;
1416 if (item == mTop) {
1417 // must have been the only item
1418 mTop = &mSentinel;
1419 }
1420 item->mAbove = nullptr;
1421 return item;
1422 }
1424 void nsDisplayList::DeleteAll() {
1425 nsDisplayItem* item;
1426 while ((item = RemoveBottom()) != nullptr) {
1427 item->~nsDisplayItem();
1428 }
1429 }
1431 static bool
1432 GetMouseThrough(const nsIFrame* aFrame)
1433 {
1434 if (!aFrame->IsBoxFrame())
1435 return false;
1437 const nsIFrame* frame = aFrame;
1438 while (frame) {
1439 if (frame->GetStateBits() & NS_FRAME_MOUSE_THROUGH_ALWAYS) {
1440 return true;
1441 } else if (frame->GetStateBits() & NS_FRAME_MOUSE_THROUGH_NEVER) {
1442 return false;
1443 }
1444 frame = frame->GetParentBox();
1445 }
1446 return false;
1447 }
1449 static bool
1450 IsFrameReceivingPointerEvents(nsIFrame* aFrame)
1451 {
1452 nsSubDocumentFrame* frame = do_QueryFrame(aFrame);
1453 if (frame && frame->PassPointerEventsToChildren()) {
1454 return true;
1455 }
1456 return NS_STYLE_POINTER_EVENTS_NONE !=
1457 aFrame->StyleVisibility()->GetEffectivePointerEvents(aFrame);
1458 }
1460 // A list of frames, and their z depth. Used for sorting
1461 // the results of hit testing.
1462 struct FramesWithDepth
1463 {
1464 FramesWithDepth(float aDepth) :
1465 mDepth(aDepth)
1466 {}
1468 bool operator<(const FramesWithDepth& aOther) const {
1469 if (mDepth != aOther.mDepth) {
1470 // We want to sort so that the shallowest item (highest depth value) is first
1471 return mDepth > aOther.mDepth;
1472 }
1473 return this < &aOther;
1474 }
1475 bool operator==(const FramesWithDepth& aOther) const {
1476 return this == &aOther;
1477 }
1479 float mDepth;
1480 nsTArray<nsIFrame*> mFrames;
1481 };
1483 // Sort the frames by depth and then moves all the contained frames to the destination
1484 void FlushFramesArray(nsTArray<FramesWithDepth>& aSource, nsTArray<nsIFrame*>* aDest)
1485 {
1486 if (aSource.IsEmpty()) {
1487 return;
1488 }
1489 aSource.Sort();
1490 uint32_t length = aSource.Length();
1491 for (uint32_t i = 0; i < length; i++) {
1492 aDest->MoveElementsFrom(aSource[i].mFrames);
1493 }
1494 aSource.Clear();
1495 }
1497 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
1498 nsDisplayItem::HitTestState* aState,
1499 nsTArray<nsIFrame*> *aOutFrames) const {
1500 int32_t itemBufferStart = aState->mItemBuffer.Length();
1501 nsDisplayItem* item;
1502 for (item = GetBottom(); item; item = item->GetAbove()) {
1503 aState->mItemBuffer.AppendElement(item);
1504 }
1505 nsAutoTArray<FramesWithDepth, 16> temp;
1506 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart; --i) {
1507 // Pop element off the end of the buffer. We want to shorten the buffer
1508 // so that recursive calls to HitTest have more buffer space.
1509 item = aState->mItemBuffer[i];
1510 aState->mItemBuffer.SetLength(i);
1512 bool snap;
1513 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
1514 if (item->GetClip().MayIntersect(r)) {
1515 nsAutoTArray<nsIFrame*, 16> outFrames;
1516 item->HitTest(aBuilder, aRect, aState, &outFrames);
1518 // For 3d transforms with preserve-3d we add hit frames into the temp list
1519 // so we can sort them later, otherwise we add them directly to the output list.
1520 nsTArray<nsIFrame*> *writeFrames = aOutFrames;
1521 if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
1522 item->Frame()->Preserves3D()) {
1523 if (outFrames.Length()) {
1524 nsDisplayTransform *transform = static_cast<nsDisplayTransform*>(item);
1525 nsPoint point = aRect.TopLeft();
1526 // A 1x1 rect means a point, otherwise use the center of the rect
1527 if (aRect.width != 1 || aRect.height != 1) {
1528 point = aRect.Center();
1529 }
1530 temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
1531 writeFrames = &temp[temp.Length() - 1].mFrames;
1532 }
1533 } else {
1534 // We may have just finished a run of consecutive preserve-3d transforms,
1535 // so flush these into the destination array before processing our frame list.
1536 FlushFramesArray(temp, aOutFrames);
1537 }
1539 for (uint32_t j = 0; j < outFrames.Length(); j++) {
1540 nsIFrame *f = outFrames.ElementAt(j);
1541 // Handle the XUL 'mousethrough' feature and 'pointer-events'.
1542 if (!GetMouseThrough(f) && IsFrameReceivingPointerEvents(f)) {
1543 writeFrames->AppendElement(f);
1544 }
1545 }
1546 }
1547 }
1548 // Clear any remaining preserve-3d transforms.
1549 FlushFramesArray(temp, aOutFrames);
1550 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart),
1551 "How did we forget to pop some elements?");
1552 }
1554 static void Sort(nsDisplayList* aList, int32_t aCount, nsDisplayList::SortLEQ aCmp,
1555 void* aClosure) {
1556 if (aCount < 2)
1557 return;
1559 nsDisplayList list1;
1560 nsDisplayList list2;
1561 int i;
1562 int32_t half = aCount/2;
1563 bool sorted = true;
1564 nsDisplayItem* prev = nullptr;
1565 for (i = 0; i < aCount; ++i) {
1566 nsDisplayItem* item = aList->RemoveBottom();
1567 (i < half ? &list1 : &list2)->AppendToTop(item);
1568 if (sorted && prev && !aCmp(prev, item, aClosure)) {
1569 sorted = false;
1570 }
1571 prev = item;
1572 }
1573 if (sorted) {
1574 aList->AppendToTop(&list1);
1575 aList->AppendToTop(&list2);
1576 return;
1577 }
1579 Sort(&list1, half, aCmp, aClosure);
1580 Sort(&list2, aCount - half, aCmp, aClosure);
1582 for (i = 0; i < aCount; ++i) {
1583 if (list1.GetBottom() &&
1584 (!list2.GetBottom() ||
1585 aCmp(list1.GetBottom(), list2.GetBottom(), aClosure))) {
1586 aList->AppendToTop(list1.RemoveBottom());
1587 } else {
1588 aList->AppendToTop(list2.RemoveBottom());
1589 }
1590 }
1591 }
1593 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, nsIDocument* aDoc) {
1594 nsIFrame* f = aItem->Frame();
1595 while (f) {
1596 nsPresContext* pc = f->PresContext();
1597 if (pc->Document() == aDoc) {
1598 return f->GetContent();
1599 }
1600 f = nsLayoutUtils::GetCrossDocParentFrame(pc->PresShell()->GetRootFrame());
1601 }
1602 return nullptr;
1603 }
1605 static bool IsContentLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
1606 void* aClosure) {
1607 nsIContent* commonAncestor = static_cast<nsIContent*>(aClosure);
1608 // It's possible that the nsIContent for aItem1 or aItem2 is in a subdocument
1609 // of commonAncestor, because display items for subdocuments have been
1610 // mixed into the same list. Ensure that we're looking at content
1611 // in commonAncestor's document.
1612 nsIDocument* commonAncestorDoc = commonAncestor->OwnerDoc();
1613 nsIContent* content1 = FindContentInDocument(aItem1, commonAncestorDoc);
1614 nsIContent* content2 = FindContentInDocument(aItem2, commonAncestorDoc);
1615 if (!content1 || !content2) {
1616 NS_ERROR("Document trees are mixed up!");
1617 // Something weird going on
1618 return true;
1619 }
1620 return nsLayoutUtils::CompareTreePosition(content1, content2, commonAncestor) <= 0;
1621 }
1623 static bool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
1624 void* aClosure) {
1625 // Note that we can't just take the difference of the two
1626 // z-indices here, because that might overflow a 32-bit int.
1627 return aItem1->ZIndex() <= aItem2->ZIndex();
1628 }
1630 void nsDisplayList::SortByZOrder(nsDisplayListBuilder* aBuilder,
1631 nsIContent* aCommonAncestor) {
1632 Sort(aBuilder, IsZOrderLEQ, aCommonAncestor);
1633 }
1635 void nsDisplayList::SortByContentOrder(nsDisplayListBuilder* aBuilder,
1636 nsIContent* aCommonAncestor) {
1637 Sort(aBuilder, IsContentLEQ, aCommonAncestor);
1638 }
1640 void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder,
1641 SortLEQ aCmp, void* aClosure) {
1642 ::Sort(this, Count(), aCmp, aClosure);
1643 }
1645 void
1646 nsDisplayItem::AddInvalidRegionForSyncDecodeBackgroundImages(
1647 nsDisplayListBuilder* aBuilder,
1648 const nsDisplayItemGeometry* aGeometry,
1649 nsRegion* aInvalidRegion)
1650 {
1651 if (aBuilder->ShouldSyncDecodeImages()) {
1652 if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(mFrame)) {
1653 bool snap;
1654 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
1655 }
1656 }
1657 }
1659 /* static */ bool
1660 nsDisplayItem::ForceActiveLayers()
1661 {
1662 static bool sForce = false;
1663 static bool sForceCached = false;
1665 if (!sForceCached) {
1666 Preferences::AddBoolVarCache(&sForce, "layers.force-active", false);
1667 sForceCached = true;
1668 }
1670 return sForce;
1671 }
1673 /* static */ int32_t
1674 nsDisplayItem::MaxActiveLayers()
1675 {
1676 static int32_t sMaxLayers = false;
1677 static bool sMaxLayersCached = false;
1679 if (!sMaxLayersCached) {
1680 Preferences::AddIntVarCache(&sMaxLayers, "layers.max-active", -1);
1681 sMaxLayersCached = true;
1682 }
1684 return sMaxLayers;
1685 }
1687 int32_t
1688 nsDisplayItem::ZIndex() const
1689 {
1690 if (!mFrame->IsPositioned() && !mFrame->IsFlexItem())
1691 return 0;
1693 const nsStylePosition* position = mFrame->StylePosition();
1694 if (position->mZIndex.GetUnit() == eStyleUnit_Integer)
1695 return position->mZIndex.GetIntValue();
1697 // sort the auto and 0 elements together
1698 return 0;
1699 }
1701 bool
1702 nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
1703 nsRegion* aVisibleRegion) {
1704 nsRect bounds = GetClippedBounds(aBuilder);
1706 nsRegion itemVisible;
1707 itemVisible.And(*aVisibleRegion, bounds);
1708 mVisibleRect = itemVisible.GetBounds();
1710 // When we recompute visibility within layers we don't need to
1711 // expand the visible region for content behind plugins (the plugin
1712 // is not in the layer).
1713 if (!ComputeVisibility(aBuilder, aVisibleRegion, nsRect()))
1714 return false;
1716 nsRegion opaque = TreatAsOpaque(this, aBuilder);
1717 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque);
1718 return true;
1719 }
1721 nsRect
1722 nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder)
1723 {
1724 bool snap;
1725 nsRect r = GetBounds(aBuilder, &snap);
1726 return GetClip().ApplyNonRoundedIntersection(r);
1727 }
1729 nsRect
1730 nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
1731 {
1732 *aSnap = true;
1733 return mBounds;
1734 }
1736 void
1737 nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
1738 nsRenderingContext* aCtx)
1739 {
1740 aCtx->SetColor(mColor);
1741 aCtx->FillRect(mVisibleRect);
1742 }
1744 #ifdef MOZ_DUMP_PAINTING
1745 void
1746 nsDisplaySolidColor::WriteDebugInfo(nsACString& aTo)
1747 {
1748 aTo += nsPrintfCString(" (rgba %d,%d,%d,%d)",
1749 NS_GET_R(mColor), NS_GET_G(mColor),
1750 NS_GET_B(mColor), NS_GET_A(mColor));
1751 }
1752 #endif
1754 static void
1755 RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1756 {
1757 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
1759 for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
1760 // Bail out if we're in a transformed subtree
1761 if (f->IsTransformed())
1762 return;
1763 // Bail out if we're not in the displayRoot's document
1764 if (!f->GetParent() && f != displayRoot)
1765 return;
1766 }
1768 nsRect borderBox(aFrame->GetOffsetTo(displayRoot), aFrame->GetSize());
1769 aBuilder->RegisterThemeGeometry(aFrame->StyleDisplay()->mAppearance,
1770 borderBox.ToNearestPixels(aFrame->PresContext()->AppUnitsPerDevPixel()));
1771 }
1773 nsDisplayBackgroundImage::nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilder,
1774 nsIFrame* aFrame,
1775 uint32_t aLayer,
1776 const nsStyleBackground* aBackgroundStyle)
1777 : nsDisplayImageContainer(aBuilder, aFrame)
1778 , mBackgroundStyle(aBackgroundStyle)
1779 , mLayer(aLayer)
1780 {
1781 MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
1783 mBounds = GetBoundsInternal(aBuilder);
1784 }
1786 nsDisplayBackgroundImage::~nsDisplayBackgroundImage()
1787 {
1788 #ifdef NS_BUILD_REFCNT_LOGGING
1789 MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
1790 #endif
1791 }
1793 static nsStyleContext* GetBackgroundStyleContext(nsIFrame* aFrame)
1794 {
1795 nsStyleContext *sc;
1796 if (!nsCSSRendering::FindBackground(aFrame, &sc)) {
1797 // We don't want to bail out if moz-appearance is set on a root
1798 // node. If it has a parent content node, bail because it's not
1799 // a root, other wise keep going in order to let the theme stuff
1800 // draw the background. The canvas really should be drawing the
1801 // bg, but there's no way to hook that up via css.
1802 if (!aFrame->StyleDisplay()->mAppearance) {
1803 return nullptr;
1804 }
1806 nsIContent* content = aFrame->GetContent();
1807 if (!content || content->GetParent()) {
1808 return nullptr;
1809 }
1811 sc = aFrame->StyleContext();
1812 }
1813 return sc;
1814 }
1816 /*static*/ bool
1817 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuilder,
1818 nsIFrame* aFrame,
1819 nsDisplayList* aList)
1820 {
1821 nsStyleContext* bgSC = nullptr;
1822 const nsStyleBackground* bg = nullptr;
1823 nsPresContext* presContext = aFrame->PresContext();
1824 bool isThemed = aFrame->IsThemed();
1825 if (!isThemed) {
1826 bgSC = GetBackgroundStyleContext(aFrame);
1827 if (bgSC) {
1828 bg = bgSC->StyleBackground();
1829 }
1830 }
1832 bool drawBackgroundColor = false;
1833 nscolor color;
1834 if (!nsCSSRendering::IsCanvasFrame(aFrame) && bg) {
1835 bool drawBackgroundImage;
1836 color =
1837 nsCSSRendering::DetermineBackgroundColor(presContext, bgSC, aFrame,
1838 drawBackgroundImage, drawBackgroundColor);
1839 }
1841 // An auxiliary list is necessary in case we have background blending; if that
1842 // is the case, background items need to be wrapped by a blend container to
1843 // isolate blending to the background
1844 nsDisplayList bgItemList;
1845 // Even if we don't actually have a background color to paint, we may still need
1846 // to create an item for hit testing.
1847 if ((drawBackgroundColor && color != NS_RGBA(0,0,0,0)) ||
1848 aBuilder->IsForEventDelivery()) {
1849 bgItemList.AppendNewToTop(
1850 new (aBuilder) nsDisplayBackgroundColor(aBuilder, aFrame, bg,
1851 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0)));
1852 }
1854 if (isThemed) {
1855 nsDisplayThemedBackground* bgItem =
1856 new (aBuilder) nsDisplayThemedBackground(aBuilder, aFrame);
1857 bgItemList.AppendNewToTop(bgItem);
1858 aList->AppendToTop(&bgItemList);
1859 return true;
1860 }
1862 if (!bg) {
1863 aList->AppendToTop(&bgItemList);
1864 return false;
1865 }
1867 bool needBlendContainer = false;
1869 // Passing bg == nullptr in this macro will result in one iteration with
1870 // i = 0.
1871 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
1872 if (bg->mLayers[i].mImage.IsEmpty()) {
1873 continue;
1874 }
1876 if (bg->mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
1877 needBlendContainer = true;
1878 }
1880 nsDisplayBackgroundImage* bgItem =
1881 new (aBuilder) nsDisplayBackgroundImage(aBuilder, aFrame, i, bg);
1882 bgItemList.AppendNewToTop(bgItem);
1883 }
1885 if (needBlendContainer) {
1886 bgItemList.AppendNewToTop(
1887 new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, &bgItemList));
1888 }
1890 aList->AppendToTop(&bgItemList);
1891 return false;
1892 }
1894 // Check that the rounded border of aFrame, added to aToReferenceFrame,
1895 // intersects aRect. Assumes that the unrounded border has already
1896 // been checked for intersection.
1897 static bool
1898 RoundedBorderIntersectsRect(nsIFrame* aFrame,
1899 const nsPoint& aFrameToReferenceFrame,
1900 const nsRect& aTestRect)
1901 {
1902 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize()).Intersects(aTestRect))
1903 return false;
1905 nscoord radii[8];
1906 return !aFrame->GetBorderRadii(radii) ||
1907 nsLayoutUtils::RoundedRectIntersectsRect(nsRect(aFrameToReferenceFrame,
1908 aFrame->GetSize()),
1909 radii, aTestRect);
1910 }
1912 // Returns TRUE if aContainedRect is guaranteed to be contained in
1913 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
1914 // handled conservatively by returning FALSE in some situations where
1915 // a more thorough analysis could return TRUE.
1916 //
1917 // See also RoundedRectIntersectsRect.
1918 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
1919 const nscoord aRadii[8],
1920 const nsRect& aContainedRect) {
1921 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii, aContainedRect);
1922 return rgn.Contains(aContainedRect);
1923 }
1925 bool
1926 nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
1927 const nsRect& aClipRect,
1928 gfxRect* aDestRect)
1929 {
1930 if (!mBackgroundStyle)
1931 return false;
1933 if (mBackgroundStyle->mLayers.Length() != 1)
1934 return false;
1936 nsPresContext* presContext = mFrame->PresContext();
1937 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
1938 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
1939 const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
1941 if (layer.mAttachment != NS_STYLE_BG_ATTACHMENT_FIXED)
1942 return false;
1944 nsBackgroundLayerState state =
1945 nsCSSRendering::PrepareBackgroundLayer(presContext,
1946 mFrame,
1947 flags,
1948 borderArea,
1949 aClipRect,
1950 *mBackgroundStyle,
1951 layer);
1953 nsImageRenderer* imageRenderer = &state.mImageRenderer;
1954 // We only care about images here, not gradients.
1955 if (!imageRenderer->IsRasterImage())
1956 return false;
1958 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
1959 *aDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
1961 return true;
1962 }
1964 bool
1965 nsDisplayBackgroundImage::TryOptimizeToImageLayer(LayerManager* aManager,
1966 nsDisplayListBuilder* aBuilder)
1967 {
1968 if (!mBackgroundStyle)
1969 return false;
1971 nsPresContext* presContext = mFrame->PresContext();
1972 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
1973 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
1974 const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
1976 if (layer.mClip != NS_STYLE_BG_CLIP_BORDER) {
1977 return false;
1978 }
1979 nscoord radii[8];
1980 if (mFrame->GetBorderRadii(radii)) {
1981 return false;
1982 }
1984 nsBackgroundLayerState state =
1985 nsCSSRendering::PrepareBackgroundLayer(presContext,
1986 mFrame,
1987 flags,
1988 borderArea,
1989 borderArea,
1990 *mBackgroundStyle,
1991 layer);
1993 nsImageRenderer* imageRenderer = &state.mImageRenderer;
1994 // We only care about images here, not gradients.
1995 if (!imageRenderer->IsRasterImage())
1996 return false;
1998 nsRefPtr<ImageContainer> imageContainer = imageRenderer->GetContainer(aManager);
1999 // Image is not ready to be made into a layer yet
2000 if (!imageContainer)
2001 return false;
2003 // We currently can't handle tiled or partial backgrounds.
2004 if (!state.mDestArea.IsEqualEdges(state.mFillArea)) {
2005 return false;
2006 }
2008 // XXX Ignoring state.mAnchor. ImageLayer drawing snaps mDestArea edges to
2009 // layer pixel boundaries. This should be OK for now.
2011 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
2012 mDestRect = nsLayoutUtils::RectToGfxRect(state.mDestArea, appUnitsPerDevPixel);
2013 mImageContainer = imageContainer;
2015 // Ok, we can turn this into a layer if needed.
2016 return true;
2017 }
2019 already_AddRefed<ImageContainer>
2020 nsDisplayBackgroundImage::GetContainer(LayerManager* aManager,
2021 nsDisplayListBuilder *aBuilder)
2022 {
2023 if (!TryOptimizeToImageLayer(aManager, aBuilder)) {
2024 return nullptr;
2025 }
2027 nsRefPtr<ImageContainer> container = mImageContainer;
2029 return container.forget();
2030 }
2032 LayerState
2033 nsDisplayBackgroundImage::GetLayerState(nsDisplayListBuilder* aBuilder,
2034 LayerManager* aManager,
2035 const ContainerLayerParameters& aParameters)
2036 {
2037 bool animated = false;
2038 if (mBackgroundStyle) {
2039 const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
2040 const nsStyleImage* image = &layer.mImage;
2041 if (image->GetType() == eStyleImageType_Image) {
2042 imgIRequest* imgreq = image->GetImageData();
2043 nsCOMPtr<imgIContainer> image;
2044 if (NS_SUCCEEDED(imgreq->GetImage(getter_AddRefs(image))) && image) {
2045 if (NS_FAILED(image->GetAnimated(&animated))) {
2046 animated = false;
2047 }
2048 }
2049 }
2050 }
2052 if (!animated ||
2053 !nsLayoutUtils::AnimatedImageLayersEnabled()) {
2054 if (!aManager->IsCompositingCheap() ||
2055 !nsLayoutUtils::GPUImageScalingEnabled()) {
2056 return LAYER_NONE;
2057 }
2058 }
2060 if (!TryOptimizeToImageLayer(aManager, aBuilder)) {
2061 return LAYER_NONE;
2062 }
2064 if (!animated) {
2065 mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize();
2066 NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!");
2068 gfxRect destRect = mDestRect;
2070 destRect.width *= aParameters.mXScale;
2071 destRect.height *= aParameters.mYScale;
2073 // Calculate the scaling factor for the frame.
2074 gfxSize scale = gfxSize(destRect.width / imageSize.width, destRect.height / imageSize.height);
2076 // If we are not scaling at all, no point in separating this into a layer.
2077 if (scale.width == 1.0f && scale.height == 1.0f) {
2078 return LAYER_NONE;
2079 }
2081 // If the target size is pretty small, no point in using a layer.
2082 if (destRect.width * destRect.height < 64 * 64) {
2083 return LAYER_NONE;
2084 }
2085 }
2087 return LAYER_ACTIVE;
2088 }
2090 already_AddRefed<Layer>
2091 nsDisplayBackgroundImage::BuildLayer(nsDisplayListBuilder* aBuilder,
2092 LayerManager* aManager,
2093 const ContainerLayerParameters& aParameters)
2094 {
2095 nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
2096 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
2097 if (!layer) {
2098 layer = aManager->CreateImageLayer();
2099 if (!layer)
2100 return nullptr;
2101 }
2102 layer->SetContainer(mImageContainer);
2103 ConfigureLayer(layer, aParameters.mOffset);
2104 return layer.forget();
2105 }
2107 void
2108 nsDisplayBackgroundImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset)
2109 {
2110 aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
2112 mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize();
2113 NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!");
2115 gfxPoint p = mDestRect.TopLeft() + aOffset;
2116 gfx::Matrix transform;
2117 transform.Translate(p.x, p.y);
2118 transform.Scale(mDestRect.width/imageSize.width,
2119 mDestRect.height/imageSize.height);
2120 aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
2121 aLayer->SetVisibleRegion(nsIntRect(0, 0, imageSize.width, imageSize.height));
2122 }
2124 void
2125 nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
2126 const nsRect& aRect,
2127 HitTestState* aState,
2128 nsTArray<nsIFrame*> *aOutFrames)
2129 {
2130 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
2131 aOutFrames->AppendElement(mFrame);
2132 }
2133 }
2135 bool
2136 nsDisplayBackgroundImage::ComputeVisibility(nsDisplayListBuilder* aBuilder,
2137 nsRegion* aVisibleRegion,
2138 const nsRect& aAllowVisibleRegionExpansion)
2139 {
2140 if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
2141 aAllowVisibleRegionExpansion)) {
2142 return false;
2143 }
2145 // Return false if the background was propagated away from this
2146 // frame. We don't want this display item to show up and confuse
2147 // anything.
2148 return mBackgroundStyle;
2149 }
2151 /* static */ nsRegion
2152 nsDisplayBackgroundImage::GetInsideClipRegion(nsDisplayItem* aItem,
2153 nsPresContext* aPresContext,
2154 uint8_t aClip, const nsRect& aRect,
2155 bool* aSnap)
2156 {
2157 nsRegion result;
2158 if (aRect.IsEmpty())
2159 return result;
2161 nsIFrame *frame = aItem->Frame();
2163 nscoord radii[8];
2164 nsRect clipRect;
2165 bool haveRadii;
2166 switch (aClip) {
2167 case NS_STYLE_BG_CLIP_BORDER:
2168 haveRadii = frame->GetBorderRadii(radii);
2169 clipRect = nsRect(aItem->ToReferenceFrame(), frame->GetSize());
2170 break;
2171 case NS_STYLE_BG_CLIP_PADDING:
2172 haveRadii = frame->GetPaddingBoxBorderRadii(radii);
2173 clipRect = frame->GetPaddingRect() - frame->GetPosition() + aItem->ToReferenceFrame();
2174 break;
2175 case NS_STYLE_BG_CLIP_CONTENT:
2176 haveRadii = frame->GetContentBoxBorderRadii(radii);
2177 clipRect = frame->GetContentRect() - frame->GetPosition() + aItem->ToReferenceFrame();
2178 break;
2179 default:
2180 NS_NOTREACHED("Unknown clip type");
2181 return result;
2182 }
2184 if (haveRadii) {
2185 *aSnap = false;
2186 result = nsLayoutUtils::RoundedRectIntersectRect(clipRect, radii, aRect);
2187 } else {
2188 result = clipRect.Intersect(aRect);
2189 }
2190 return result;
2191 }
2193 nsRegion
2194 nsDisplayBackgroundImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2195 bool* aSnap) {
2196 nsRegion result;
2197 *aSnap = false;
2199 if (!mBackgroundStyle)
2200 return result;
2203 *aSnap = true;
2205 // For policies other than EACH_BOX, don't try to optimize here, since
2206 // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
2207 // which expects frames to be sent to it in content order, not reverse
2208 // content order which we'll produce here.
2209 // Of course, if there's only one frame in the flow, it doesn't matter.
2210 if (mBackgroundStyle->mBackgroundInlinePolicy == NS_STYLE_BG_INLINE_POLICY_EACH_BOX ||
2211 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
2212 const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer];
2213 if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL) {
2214 nsPresContext* presContext = mFrame->PresContext();
2215 result = GetInsideClipRegion(this, presContext, layer.mClip, mBounds, aSnap);
2216 }
2217 }
2219 return result;
2220 }
2222 bool
2223 nsDisplayBackgroundImage::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) {
2224 if (!mBackgroundStyle) {
2225 *aColor = NS_RGBA(0,0,0,0);
2226 return true;
2227 }
2228 return false;
2229 }
2231 bool
2232 nsDisplayBackgroundImage::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder,
2233 nsIFrame* aFrame)
2234 {
2235 if (!mBackgroundStyle)
2236 return false;
2237 if (!mBackgroundStyle->HasFixedBackground())
2238 return false;
2240 // If aFrame is mFrame or an ancestor in this document, and aFrame is
2241 // not the viewport frame, then moving aFrame will move mFrame
2242 // relative to the viewport, so our fixed-pos background will change.
2243 return aFrame->GetParent() &&
2244 (aFrame == mFrame ||
2245 nsLayoutUtils::IsProperAncestorFrame(aFrame, mFrame));
2246 }
2248 nsRect
2249 nsDisplayBackgroundImage::GetPositioningArea()
2250 {
2251 if (!mBackgroundStyle) {
2252 return nsRect();
2253 }
2254 nsIFrame* attachedToFrame;
2255 return nsCSSRendering::ComputeBackgroundPositioningArea(
2256 mFrame->PresContext(), mFrame,
2257 nsRect(ToReferenceFrame(), mFrame->GetSize()),
2258 *mBackgroundStyle, mBackgroundStyle->mLayers[mLayer],
2259 &attachedToFrame) + ToReferenceFrame();
2260 }
2262 bool
2263 nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange()
2264 {
2265 if (!mBackgroundStyle)
2266 return false;
2268 nscoord radii[8];
2269 if (mFrame->GetBorderRadii(radii)) {
2270 // A change in the size of the positioning area might change the position
2271 // of the rounded corners.
2272 return true;
2273 }
2275 const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
2276 if (layer.RenderingMightDependOnPositioningAreaSizeChange()) {
2277 return true;
2278 }
2279 return false;
2280 }
2282 static void CheckForBorderItem(nsDisplayItem *aItem, uint32_t& aFlags)
2283 {
2284 nsDisplayItem* nextItem = aItem->GetAbove();
2285 while (nextItem && nextItem->GetType() == nsDisplayItem::TYPE_BACKGROUND) {
2286 nextItem = nextItem->GetAbove();
2287 }
2288 if (nextItem &&
2289 nextItem->Frame() == aItem->Frame() &&
2290 nextItem->GetType() == nsDisplayItem::TYPE_BORDER) {
2291 aFlags |= nsCSSRendering::PAINTBG_WILL_PAINT_BORDER;
2292 }
2293 }
2295 void
2296 nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
2297 nsRenderingContext* aCtx) {
2298 PaintInternal(aBuilder, aCtx, mVisibleRect, nullptr);
2299 }
2301 void
2302 nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
2303 nsRenderingContext* aCtx, const nsRect& aBounds,
2304 nsRect* aClipRect) {
2305 nsPoint offset = ToReferenceFrame();
2306 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2307 CheckForBorderItem(this, flags);
2309 nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame,
2310 aBounds,
2311 nsRect(offset, mFrame->GetSize()),
2312 flags, aClipRect, mLayer);
2314 }
2316 void nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
2317 const nsDisplayItemGeometry* aGeometry,
2318 nsRegion* aInvalidRegion)
2319 {
2320 if (!mBackgroundStyle) {
2321 return;
2322 }
2324 const nsDisplayBackgroundGeometry* geometry = static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
2326 bool snap;
2327 nsRect bounds = GetBounds(aBuilder, &snap);
2328 nsRect positioningArea = GetPositioningArea();
2329 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
2330 (positioningArea.Size() != geometry->mPositioningArea.Size() &&
2331 RenderingMightDependOnPositioningAreaSizeChange())) {
2332 // Positioning area changed in a way that could cause everything to change,
2333 // so invalidate everything (both old and new painting areas).
2334 aInvalidRegion->Or(bounds, geometry->mBounds);
2336 if (positioningArea.Size() != geometry->mPositioningArea.Size()) {
2337 NotifyRenderingChanged();
2338 }
2339 return;
2340 }
2341 if (aBuilder->ShouldSyncDecodeImages()) {
2342 if (mBackgroundStyle &&
2343 !nsCSSRendering::IsBackgroundImageDecodedForStyleContextAndLayer(mBackgroundStyle, mLayer)) {
2344 aInvalidRegion->Or(*aInvalidRegion, bounds);
2346 NotifyRenderingChanged();
2347 }
2348 }
2349 if (!bounds.IsEqualInterior(geometry->mBounds)) {
2350 // Positioning area is unchanged, so invalidate just the change in the
2351 // painting area.
2352 aInvalidRegion->Xor(bounds, geometry->mBounds);
2354 NotifyRenderingChanged();
2355 }
2356 }
2358 nsRect
2359 nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
2360 *aSnap = true;
2361 return mBounds;
2362 }
2364 nsRect
2365 nsDisplayBackgroundImage::GetBoundsInternal(nsDisplayListBuilder* aBuilder) {
2366 nsPresContext* presContext = mFrame->PresContext();
2368 if (!mBackgroundStyle) {
2369 return nsRect();
2370 }
2372 nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize());
2373 nsRect clipRect = borderBox;
2374 if (mFrame->GetType() == nsGkAtoms::canvasFrame) {
2375 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
2376 clipRect = frame->CanvasArea() + ToReferenceFrame();
2377 }
2378 const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer];
2379 return nsCSSRendering::GetBackgroundLayerRect(presContext, mFrame,
2380 borderBox, clipRect,
2381 *mBackgroundStyle, layer,
2382 aBuilder->GetBackgroundPaintFlags());
2383 }
2385 uint32_t
2386 nsDisplayBackgroundImage::GetPerFrameKey()
2387 {
2388 return (mLayer << nsDisplayItem::TYPE_BITS) |
2389 nsDisplayItem::GetPerFrameKey();
2390 }
2392 nsDisplayThemedBackground::nsDisplayThemedBackground(nsDisplayListBuilder* aBuilder,
2393 nsIFrame* aFrame)
2394 : nsDisplayItem(aBuilder, aFrame)
2395 {
2396 MOZ_COUNT_CTOR(nsDisplayThemedBackground);
2398 const nsStyleDisplay* disp = mFrame->StyleDisplay();
2399 mAppearance = disp->mAppearance;
2400 mFrame->IsThemed(disp, &mThemeTransparency);
2402 // Perform necessary RegisterThemeGeometry
2403 switch (disp->mAppearance) {
2404 case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
2405 case NS_THEME_TOOLBAR:
2406 case NS_THEME_WINDOW_TITLEBAR:
2407 case NS_THEME_WINDOW_BUTTON_BOX:
2408 case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON:
2409 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
2410 RegisterThemeGeometry(aBuilder, aFrame);
2411 break;
2412 case NS_THEME_WIN_BORDERLESS_GLASS:
2413 case NS_THEME_WIN_GLASS:
2414 aBuilder->SetGlassDisplayItem(this);
2415 break;
2416 }
2418 mBounds = GetBoundsInternal();
2419 }
2421 nsDisplayThemedBackground::~nsDisplayThemedBackground()
2422 {
2423 #ifdef NS_BUILD_REFCNT_LOGGING
2424 MOZ_COUNT_DTOR(nsDisplayThemedBackground);
2425 #endif
2426 }
2428 #ifdef MOZ_DUMP_PAINTING
2429 void
2430 nsDisplayThemedBackground::WriteDebugInfo(nsACString& aTo)
2431 {
2432 aTo += nsPrintfCString(" (themed, appearance:%d)", mAppearance);
2433 }
2434 #endif
2436 void
2437 nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder,
2438 const nsRect& aRect,
2439 HitTestState* aState,
2440 nsTArray<nsIFrame*> *aOutFrames)
2441 {
2442 // Assume that any point in our border rect is a hit.
2443 if (nsRect(ToReferenceFrame(), mFrame->GetSize()).Intersects(aRect)) {
2444 aOutFrames->AppendElement(mFrame);
2445 }
2446 }
2448 nsRegion
2449 nsDisplayThemedBackground::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2450 bool* aSnap) {
2451 nsRegion result;
2452 *aSnap = false;
2454 if (mThemeTransparency == nsITheme::eOpaque) {
2455 result = nsRect(ToReferenceFrame(), mFrame->GetSize());
2456 }
2457 return result;
2458 }
2460 bool
2461 nsDisplayThemedBackground::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) {
2462 if (mAppearance == NS_THEME_WIN_BORDERLESS_GLASS ||
2463 mAppearance == NS_THEME_WIN_GLASS) {
2464 *aColor = NS_RGBA(0,0,0,0);
2465 return true;
2466 }
2467 return false;
2468 }
2470 nsRect
2471 nsDisplayThemedBackground::GetPositioningArea()
2472 {
2473 return nsRect(ToReferenceFrame(), mFrame->GetSize());
2474 }
2476 void
2477 nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
2478 nsRenderingContext* aCtx)
2479 {
2480 PaintInternal(aBuilder, aCtx, mVisibleRect, nullptr);
2481 }
2484 void
2485 nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder,
2486 nsRenderingContext* aCtx, const nsRect& aBounds,
2487 nsRect* aClipRect)
2488 {
2489 // XXXzw this ignores aClipRect.
2490 nsPresContext* presContext = mFrame->PresContext();
2491 nsITheme *theme = presContext->GetTheme();
2492 nsRect borderArea(ToReferenceFrame(), mFrame->GetSize());
2493 nsRect drawing(borderArea);
2494 theme->GetWidgetOverflow(presContext->DeviceContext(), mFrame, mAppearance,
2495 &drawing);
2496 drawing.IntersectRect(drawing, aBounds);
2497 theme->DrawWidgetBackground(aCtx, mFrame, mAppearance, borderArea, drawing);
2498 }
2500 bool nsDisplayThemedBackground::IsWindowActive()
2501 {
2502 EventStates docState = mFrame->GetContent()->OwnerDoc()->GetDocumentState();
2503 return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
2504 }
2506 void nsDisplayThemedBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
2507 const nsDisplayItemGeometry* aGeometry,
2508 nsRegion* aInvalidRegion)
2509 {
2510 const nsDisplayThemedBackgroundGeometry* geometry = static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
2512 bool snap;
2513 nsRect bounds = GetBounds(aBuilder, &snap);
2514 nsRect positioningArea = GetPositioningArea();
2515 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
2516 // Invalidate everything (both old and new painting areas).
2517 aInvalidRegion->Or(bounds, geometry->mBounds);
2518 return;
2519 }
2520 if (!bounds.IsEqualInterior(geometry->mBounds)) {
2521 // Positioning area is unchanged, so invalidate just the change in the
2522 // painting area.
2523 aInvalidRegion->Xor(bounds, geometry->mBounds);
2524 }
2525 nsITheme* theme = mFrame->PresContext()->GetTheme();
2526 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) &&
2527 IsWindowActive() != geometry->mWindowIsActive) {
2528 aInvalidRegion->Or(*aInvalidRegion, bounds);
2529 }
2530 }
2532 nsRect
2533 nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
2534 *aSnap = true;
2535 return mBounds;
2536 }
2538 nsRect
2539 nsDisplayThemedBackground::GetBoundsInternal() {
2540 nsPresContext* presContext = mFrame->PresContext();
2542 nsRect r(nsPoint(0,0), mFrame->GetSize());
2543 presContext->GetTheme()->
2544 GetWidgetOverflow(presContext->DeviceContext(), mFrame,
2545 mFrame->StyleDisplay()->mAppearance, &r);
2546 #ifdef XP_MACOSX
2547 // Bug 748219
2548 r.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
2549 #endif
2551 return r + ToReferenceFrame();
2552 }
2554 void
2555 nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
2556 nsRenderingContext* aCtx)
2557 {
2558 if (mColor == NS_RGBA(0, 0, 0, 0)) {
2559 return;
2560 }
2562 nsPoint offset = ToReferenceFrame();
2563 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
2564 CheckForBorderItem(this, flags);
2565 nsCSSRendering::PaintBackgroundColor(mFrame->PresContext(), *aCtx, mFrame,
2566 mVisibleRect,
2567 nsRect(offset, mFrame->GetSize()),
2568 flags);
2569 }
2571 nsRegion
2572 nsDisplayBackgroundColor::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
2573 bool* aSnap)
2574 {
2575 if (NS_GET_A(mColor) != 255) {
2576 return nsRegion();
2577 }
2579 if (!mBackgroundStyle)
2580 return nsRegion();
2582 *aSnap = true;
2584 const nsStyleBackground::Layer& bottomLayer = mBackgroundStyle->BottomLayer();
2585 nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize());
2586 nsPresContext* presContext = mFrame->PresContext();
2587 return nsDisplayBackgroundImage::GetInsideClipRegion(this, presContext, bottomLayer.mClip, borderBox, aSnap);
2588 }
2590 bool
2591 nsDisplayBackgroundColor::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor)
2592 {
2593 *aColor = mColor;
2595 if (!mBackgroundStyle)
2596 return true;
2598 return (!nsLayoutUtils::HasNonZeroCorner(mFrame->StyleBorder()->mBorderRadius) &&
2599 mBackgroundStyle->BottomLayer().mClip == NS_STYLE_BG_CLIP_BORDER);
2600 }
2602 void
2603 nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
2604 const nsRect& aRect,
2605 HitTestState* aState,
2606 nsTArray<nsIFrame*> *aOutFrames)
2607 {
2608 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
2609 // aRect doesn't intersect our border-radius curve.
2610 return;
2611 }
2613 aOutFrames->AppendElement(mFrame);
2614 }
2616 #ifdef MOZ_DUMP_PAINTING
2617 void
2618 nsDisplayBackgroundColor::WriteDebugInfo(nsACString& aTo)
2619 {
2620 aTo += nsPrintfCString(" (rgba %d,%d,%d,%d)",
2621 NS_GET_R(mColor), NS_GET_G(mColor),
2622 NS_GET_B(mColor), NS_GET_A(mColor));
2623 }
2624 #endif
2626 nsRect
2627 nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
2628 *aSnap = false;
2629 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
2630 }
2632 void
2633 nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder,
2634 nsRenderingContext* aCtx) {
2635 // TODO join outlines together
2636 nsPoint offset = ToReferenceFrame();
2637 nsCSSRendering::PaintOutline(mFrame->PresContext(), *aCtx, mFrame,
2638 mVisibleRect,
2639 nsRect(offset, mFrame->GetSize()),
2640 mFrame->StyleContext());
2641 }
2643 bool
2644 nsDisplayOutline::ComputeVisibility(nsDisplayListBuilder* aBuilder,
2645 nsRegion* aVisibleRegion,
2646 const nsRect& aAllowVisibleRegionExpansion) {
2647 if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
2648 aAllowVisibleRegionExpansion)) {
2649 return false;
2650 }
2652 const nsStyleOutline* outline = mFrame->StyleOutline();
2653 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
2654 if (borderBox.Contains(aVisibleRegion->GetBounds()) &&
2655 !nsLayoutUtils::HasNonZeroCorner(outline->mOutlineRadius)) {
2656 if (outline->mOutlineOffset >= 0) {
2657 // the visible region is entirely inside the border-rect, and the outline
2658 // isn't rendered inside the border-rect, so the outline is not visible
2659 return false;
2660 }
2661 }
2663 return true;
2664 }
2666 void
2667 nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
2668 const nsRect& aRect,
2669 HitTestState* aState,
2670 nsTArray<nsIFrame*> *aOutFrames)
2671 {
2672 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) {
2673 // aRect doesn't intersect our border-radius curve.
2674 return;
2675 }
2677 aOutFrames->AppendElement(mFrame);
2678 }
2680 void
2681 nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
2682 nsIFrame* aFrame)
2683 {
2684 NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) == aBuilder->FindReferenceFrameFor(mFrame),
2685 "Reference frame mismatch");
2686 uint8_t pointerEvents = aFrame->StyleVisibility()->mPointerEvents;
2687 if (pointerEvents == NS_STYLE_POINTER_EVENTS_NONE) {
2688 return;
2689 }
2690 // XXX handle other pointerEvents values for SVG
2691 // XXX Do something clever here for the common case where the border box
2692 // is obviously entirely inside mHitRegion.
2693 nsRect borderBox(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize());
2694 const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder);
2695 bool borderBoxHasRoundedCorners =
2696 nsLayoutUtils::HasNonZeroCorner(aFrame->StyleBorder()->mBorderRadius);
2697 if (clip) {
2698 borderBox = clip->ApplyNonRoundedIntersection(borderBox);
2699 if (clip->GetRoundedRectCount() > 0) {
2700 borderBoxHasRoundedCorners = true;
2701 }
2702 }
2703 if (borderBoxHasRoundedCorners ||
2704 (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
2705 mMaybeHitRegion.Or(mMaybeHitRegion, borderBox);
2706 } else {
2707 mHitRegion.Or(mHitRegion, borderBox);
2708 }
2709 if (aBuilder->GetAncestorHasTouchEventHandler()) {
2710 mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox);
2711 }
2712 }
2714 void
2715 nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder,
2716 nsRenderingContext* aCtx) {
2717 // Note: Because we exist, we know that the caret is visible, so we don't
2718 // need to check for the caret's visibility.
2719 mCaret->PaintCaret(aBuilder, aCtx, mFrame, ToReferenceFrame());
2720 }
2722 bool
2723 nsDisplayBorder::ComputeVisibility(nsDisplayListBuilder* aBuilder,
2724 nsRegion* aVisibleRegion,
2725 const nsRect& aAllowVisibleRegionExpansion) {
2726 if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
2727 aAllowVisibleRegionExpansion)) {
2728 return false;
2729 }
2731 nsRect paddingRect = mFrame->GetPaddingRect() - mFrame->GetPosition() +
2732 ToReferenceFrame();
2733 const nsStyleBorder *styleBorder;
2734 if (paddingRect.Contains(aVisibleRegion->GetBounds()) &&
2735 !(styleBorder = mFrame->StyleBorder())->IsBorderImageLoaded() &&
2736 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
2737 // the visible region is entirely inside the content rect, and no part
2738 // of the border is rendered inside the content rect, so we are not
2739 // visible
2740 // Skip this if there's a border-image (which draws a background
2741 // too) or if there is a border-radius (which makes the border draw
2742 // further in).
2743 return false;
2744 }
2746 return true;
2747 }
2749 nsDisplayItemGeometry*
2750 nsDisplayBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder)
2751 {
2752 return new nsDisplayBorderGeometry(this, aBuilder);
2753 }
2755 void
2756 nsDisplayBorder::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
2757 const nsDisplayItemGeometry* aGeometry,
2758 nsRegion* aInvalidRegion)
2759 {
2760 const nsDisplayBorderGeometry* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
2761 bool snap;
2762 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
2763 !geometry->mContentRect.IsEqualInterior(GetContentRect())) {
2764 // We can probably get away with only invalidating the difference
2765 // between the border and padding rects, but the XUL ui at least
2766 // is apparently painting a background with this?
2767 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
2768 }
2769 }
2771 void
2772 nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder,
2773 nsRenderingContext* aCtx) {
2774 nsPoint offset = ToReferenceFrame();
2775 nsCSSRendering::PaintBorder(mFrame->PresContext(), *aCtx, mFrame,
2776 mVisibleRect,
2777 nsRect(offset, mFrame->GetSize()),
2778 mFrame->StyleContext(),
2779 mFrame->GetSkipSides());
2780 }
2782 nsRect
2783 nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
2784 {
2785 *aSnap = true;
2786 return CalculateBounds(*mFrame->StyleBorder());
2787 }
2789 nsRect
2790 nsDisplayBorder::CalculateBounds(const nsStyleBorder& aStyleBorder)
2791 {
2792 nsRect borderBounds(ToReferenceFrame(), mFrame->GetSize());
2793 if (aStyleBorder.IsBorderImageLoaded()) {
2794 borderBounds.Inflate(aStyleBorder.GetImageOutset());
2795 return borderBounds;
2796 } else {
2797 nsMargin border = aStyleBorder.GetComputedBorder();
2798 nsRect result;
2799 if (border.top > 0) {
2800 result = nsRect(borderBounds.X(), borderBounds.Y(), borderBounds.Width(), border.top);
2801 }
2802 if (border.right > 0) {
2803 result.UnionRect(result, nsRect(borderBounds.XMost() - border.right, borderBounds.Y(), border.right, borderBounds.Height()));
2804 }
2805 if (border.bottom > 0) {
2806 result.UnionRect(result, nsRect(borderBounds.X(), borderBounds.YMost() - border.bottom, borderBounds.Width(), border.bottom));
2807 }
2808 if (border.left > 0) {
2809 result.UnionRect(result, nsRect(borderBounds.X(), borderBounds.Y(), border.left, borderBounds.Height()));
2810 }
2812 return result;
2813 }
2814 }
2816 // Given a region, compute a conservative approximation to it as a list
2817 // of rectangles that aren't vertically adjacent (i.e., vertically
2818 // adjacent or overlapping rectangles are combined).
2819 // Right now this is only approximate, some vertically overlapping rectangles
2820 // aren't guaranteed to be combined.
2821 static void
2822 ComputeDisjointRectangles(const nsRegion& aRegion,
2823 nsTArray<nsRect>* aRects) {
2824 nscoord accumulationMargin = nsPresContext::CSSPixelsToAppUnits(25);
2825 nsRect accumulated;
2826 nsRegionRectIterator iter(aRegion);
2827 while (true) {
2828 const nsRect* r = iter.Next();
2829 if (r && !accumulated.IsEmpty() &&
2830 accumulated.YMost() >= r->y - accumulationMargin) {
2831 accumulated.UnionRect(accumulated, *r);
2832 continue;
2833 }
2835 if (!accumulated.IsEmpty()) {
2836 aRects->AppendElement(accumulated);
2837 accumulated.SetEmpty();
2838 }
2840 if (!r)
2841 break;
2843 accumulated = *r;
2844 }
2845 }
2847 void
2848 nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
2849 nsRenderingContext* aCtx) {
2850 nsPoint offset = ToReferenceFrame();
2851 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
2852 nsPresContext* presContext = mFrame->PresContext();
2853 nsAutoTArray<nsRect,10> rects;
2854 ComputeDisjointRectangles(mVisibleRegion, &rects);
2856 PROFILER_LABEL("nsDisplayBoxShadowOuter", "Paint");
2857 for (uint32_t i = 0; i < rects.Length(); ++i) {
2858 aCtx->PushState();
2859 aCtx->IntersectClip(rects[i]);
2860 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame,
2861 borderRect, rects[i], mOpacity);
2862 aCtx->PopState();
2863 }
2864 }
2866 nsRect
2867 nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
2868 *aSnap = false;
2869 return mBounds;
2870 }
2872 nsRect
2873 nsDisplayBoxShadowOuter::GetBoundsInternal() {
2874 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
2875 ToReferenceFrame();
2876 }
2878 bool
2879 nsDisplayBoxShadowOuter::ComputeVisibility(nsDisplayListBuilder* aBuilder,
2880 nsRegion* aVisibleRegion,
2881 const nsRect& aAllowVisibleRegionExpansion) {
2882 if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
2883 aAllowVisibleRegionExpansion)) {
2884 return false;
2885 }
2887 // Store the actual visible region
2888 mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
2890 nsPoint origin = ToReferenceFrame();
2891 nsRect visibleBounds = aVisibleRegion->GetBounds();
2892 nsRect frameRect(origin, mFrame->GetSize());
2893 if (!frameRect.Contains(visibleBounds))
2894 return true;
2896 // the visible region is entirely inside the border-rect, and box shadows
2897 // never render within the border-rect (unless there's a border radius).
2898 nscoord twipsRadii[8];
2899 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
2900 if (!hasBorderRadii)
2901 return false;
2903 return !RoundedRectContainsRect(frameRect, twipsRadii, visibleBounds);
2904 }
2906 void
2907 nsDisplayBoxShadowOuter::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
2908 const nsDisplayItemGeometry* aGeometry,
2909 nsRegion* aInvalidRegion)
2910 {
2911 const nsDisplayItemGenericGeometry* geometry =
2912 static_cast<const nsDisplayItemGenericGeometry*>(aGeometry);
2913 bool snap;
2914 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
2915 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
2916 nsRegion oldShadow, newShadow;
2917 nscoord dontCare[8];
2918 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
2919 if (hasBorderRadius) {
2920 // If we have rounded corners then we need to invalidate the frame area
2921 // too since we paint into it.
2922 oldShadow = geometry->mBounds;
2923 newShadow = GetBounds(aBuilder, &snap);
2924 } else {
2925 oldShadow = oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
2926 newShadow = newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
2927 }
2928 aInvalidRegion->Or(oldShadow, newShadow);
2929 }
2930 }
2933 void
2934 nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
2935 nsRenderingContext* aCtx) {
2936 nsPoint offset = ToReferenceFrame();
2937 nsRect borderRect = nsRect(offset, mFrame->GetSize());
2938 nsPresContext* presContext = mFrame->PresContext();
2939 nsAutoTArray<nsRect,10> rects;
2940 ComputeDisjointRectangles(mVisibleRegion, &rects);
2942 PROFILER_LABEL("nsDisplayBoxShadowInner", "Paint");
2943 for (uint32_t i = 0; i < rects.Length(); ++i) {
2944 aCtx->PushState();
2945 aCtx->IntersectClip(rects[i]);
2946 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame,
2947 borderRect, rects[i]);
2948 aCtx->PopState();
2949 }
2950 }
2952 bool
2953 nsDisplayBoxShadowInner::ComputeVisibility(nsDisplayListBuilder* aBuilder,
2954 nsRegion* aVisibleRegion,
2955 const nsRect& aAllowVisibleRegionExpansion) {
2956 if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
2957 aAllowVisibleRegionExpansion)) {
2958 return false;
2959 }
2961 // Store the actual visible region
2962 mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
2963 return true;
2964 }
2966 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
2967 nsIFrame* aFrame, nsDisplayList* aList)
2968 : nsDisplayItem(aBuilder, aFrame)
2969 , mOverrideZIndex(0)
2970 {
2971 mList.AppendToTop(aList);
2972 UpdateBounds(aBuilder);
2974 if (!aFrame || !aFrame->IsTransformed()) {
2975 return;
2976 }
2978 // If the frame is a preserve-3d parent, then we will create transforms
2979 // inside this list afterwards (see WrapPreserve3DList in nsFrame.cpp).
2980 // In this case we will always be outside of the transform, so share
2981 // our parents reference frame.
2982 if (aFrame->Preserves3DChildren()) {
2983 mReferenceFrame =
2984 aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame));
2985 mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
2986 return;
2987 }
2989 // If we're a transformed frame, then we need to find out if we're inside
2990 // the nsDisplayTransform or outside of it. Frames inside the transform
2991 // need mReferenceFrame == mFrame, outside needs the next ancestor
2992 // reference frame.
2993 // If we're inside the transform, then the nsDisplayItem constructor
2994 // will have done the right thing.
2995 // If we're outside the transform, then we should have only one child
2996 // (since nsDisplayTransform wraps all actual content), and that child
2997 // will have the correct reference frame set (since nsDisplayTransform
2998 // handles this explictly).
2999 //
3000 // Preserve-3d can cause us to have multiple nsDisplayTransform
3001 // children.
3002 nsDisplayItem *i = mList.GetBottom();
3003 if (i && (!i->GetAbove() || i->GetType() == TYPE_TRANSFORM) &&
3004 i->Frame() == mFrame) {
3005 mReferenceFrame = i->ReferenceFrame();
3006 mToReferenceFrame = i->ToReferenceFrame();
3007 }
3008 }
3010 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
3011 nsIFrame* aFrame, nsDisplayItem* aItem)
3012 : nsDisplayItem(aBuilder, aFrame)
3013 , mOverrideZIndex(0)
3014 {
3015 mList.AppendToTop(aItem);
3016 UpdateBounds(aBuilder);
3018 if (!aFrame || !aFrame->IsTransformed()) {
3019 return;
3020 }
3022 if (aFrame->Preserves3DChildren()) {
3023 mReferenceFrame =
3024 aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame));
3025 mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
3026 return;
3027 }
3029 // See the previous nsDisplayWrapList constructor
3030 if (aItem->Frame() == aFrame) {
3031 mReferenceFrame = aItem->ReferenceFrame();
3032 mToReferenceFrame = aItem->ToReferenceFrame();
3033 }
3034 }
3036 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
3037 nsIFrame* aFrame, nsDisplayItem* aItem,
3038 const nsIFrame* aReferenceFrame,
3039 const nsPoint& aToReferenceFrame)
3040 : nsDisplayItem(aBuilder, aFrame, aReferenceFrame, aToReferenceFrame)
3041 , mOverrideZIndex(0)
3042 {
3043 mList.AppendToTop(aItem);
3044 mBounds = mList.GetBounds(aBuilder);
3045 }
3047 nsDisplayWrapList::~nsDisplayWrapList() {
3048 mList.DeleteAll();
3049 }
3051 void
3052 nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
3053 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
3054 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
3055 }
3057 nsRect
3058 nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
3059 *aSnap = false;
3060 return mBounds;
3061 }
3063 bool
3064 nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3065 nsRegion* aVisibleRegion,
3066 const nsRect& aAllowVisibleRegionExpansion) {
3067 // Convert the passed in visible region to our appunits.
3068 nsRegion visibleRegion;
3069 // mVisibleRect has been clipped to GetClippedBounds
3070 visibleRegion.And(*aVisibleRegion, mVisibleRect);
3071 nsRegion originalVisibleRegion = visibleRegion;
3073 bool retval =
3074 mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion,
3075 mVisibleRect,
3076 aAllowVisibleRegionExpansion);
3078 nsRegion removed;
3079 // removed = originalVisibleRegion - visibleRegion
3080 removed.Sub(originalVisibleRegion, visibleRegion);
3081 // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications
3082 // SubtractFromVisibleRegion does)
3083 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed);
3085 return retval;
3086 }
3088 nsRegion
3089 nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
3090 bool* aSnap) {
3091 *aSnap = false;
3092 nsRegion result;
3093 if (mList.IsOpaque()) {
3094 // Everything within GetBounds that's visible is opaque.
3095 result = GetBounds(aBuilder, aSnap);
3096 }
3097 return result;
3098 }
3100 bool nsDisplayWrapList::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) {
3101 // We could try to do something but let's conservatively just return false.
3102 return false;
3103 }
3105 bool nsDisplayWrapList::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder,
3106 nsIFrame* aFrame) {
3107 NS_WARNING("nsDisplayWrapList::IsVaryingRelativeToMovingFrame called unexpectedly");
3108 // We could try to do something but let's conservatively just return true.
3109 return true;
3110 }
3112 void nsDisplayWrapList::Paint(nsDisplayListBuilder* aBuilder,
3113 nsRenderingContext* aCtx) {
3114 NS_ERROR("nsDisplayWrapList should have been flattened away for painting");
3115 }
3117 /**
3118 * Returns true if all descendant display items can be placed in the same
3119 * ThebesLayer --- GetLayerState returns LAYER_INACTIVE or LAYER_NONE,
3120 * and they all have the expected animated geometry root.
3121 */
3122 static LayerState
3123 RequiredLayerStateForChildren(nsDisplayListBuilder* aBuilder,
3124 LayerManager* aManager,
3125 const ContainerLayerParameters& aParameters,
3126 const nsDisplayList& aList,
3127 nsIFrame* aExpectedAnimatedGeometryRootForChildren)
3128 {
3129 LayerState result = LAYER_INACTIVE;
3130 for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
3131 if (result == LAYER_INACTIVE &&
3132 nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder) !=
3133 aExpectedAnimatedGeometryRootForChildren) {
3134 result = LAYER_ACTIVE;
3135 }
3137 LayerState state = i->GetLayerState(aBuilder, aManager, aParameters);
3138 if ((state == LAYER_ACTIVE || state == LAYER_ACTIVE_FORCE) &&
3139 state > result) {
3140 result = state;
3141 }
3142 if (state == LAYER_ACTIVE_EMPTY && state > result) {
3143 result = LAYER_ACTIVE_FORCE;
3144 }
3145 if (state == LAYER_NONE) {
3146 nsDisplayList* list = i->GetSameCoordinateSystemChildren();
3147 if (list) {
3148 LayerState childState =
3149 RequiredLayerStateForChildren(aBuilder, aManager, aParameters, *list,
3150 aExpectedAnimatedGeometryRootForChildren);
3151 if (childState > result) {
3152 result = childState;
3153 }
3154 }
3155 }
3156 }
3157 return result;
3158 }
3160 nsRect nsDisplayWrapList::GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder)
3161 {
3162 nsRect bounds;
3163 for (nsDisplayItem* i = mList.GetBottom(); i; i = i->GetAbove()) {
3164 bounds.UnionRect(bounds, i->GetComponentAlphaBounds(aBuilder));
3165 }
3166 return bounds;
3167 }
3169 static nsresult
3170 WrapDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3171 nsDisplayList* aList, nsDisplayWrapper* aWrapper) {
3172 if (!aList->GetTop())
3173 return NS_OK;
3174 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
3175 if (!item)
3176 return NS_ERROR_OUT_OF_MEMORY;
3177 // aList was emptied
3178 aList->AppendToTop(item);
3179 return NS_OK;
3180 }
3182 static nsresult
3183 WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
3184 nsDisplayList* aList, nsDisplayWrapper* aWrapper) {
3185 nsDisplayList newList;
3186 nsDisplayItem* item;
3187 while ((item = aList->RemoveBottom())) {
3188 item = aWrapper->WrapItem(aBuilder, item);
3189 if (!item)
3190 return NS_ERROR_OUT_OF_MEMORY;
3191 newList.AppendToTop(item);
3192 }
3193 // aList was emptied
3194 aList->AppendToTop(&newList);
3195 return NS_OK;
3196 }
3198 nsresult nsDisplayWrapper::WrapLists(nsDisplayListBuilder* aBuilder,
3199 nsIFrame* aFrame, const nsDisplayListSet& aIn, const nsDisplayListSet& aOut)
3200 {
3201 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn);
3202 NS_ENSURE_SUCCESS(rv, rv);
3204 if (&aOut == &aIn)
3205 return NS_OK;
3206 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground());
3207 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds());
3208 aOut.Floats()->AppendToTop(aIn.Floats());
3209 aOut.Content()->AppendToTop(aIn.Content());
3210 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants());
3211 aOut.Outlines()->AppendToTop(aIn.Outlines());
3212 return NS_OK;
3213 }
3215 nsresult nsDisplayWrapper::WrapListsInPlace(nsDisplayListBuilder* aBuilder,
3216 nsIFrame* aFrame, const nsDisplayListSet& aLists)
3217 {
3218 nsresult rv;
3219 if (WrapBorderBackground()) {
3220 // Our border-backgrounds are in-flow
3221 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this);
3222 NS_ENSURE_SUCCESS(rv, rv);
3223 }
3224 // Our block border-backgrounds are in-flow
3225 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this);
3226 NS_ENSURE_SUCCESS(rv, rv);
3227 // The floats are not in flow
3228 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this);
3229 NS_ENSURE_SUCCESS(rv, rv);
3230 // Our child content is in flow
3231 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this);
3232 NS_ENSURE_SUCCESS(rv, rv);
3233 // The positioned descendants may not be in-flow
3234 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
3235 NS_ENSURE_SUCCESS(rv, rv);
3236 // The outlines may not be in-flow
3237 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
3238 }
3240 nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
3241 nsIFrame* aFrame, nsDisplayList* aList)
3242 : nsDisplayWrapList(aBuilder, aFrame, aList) {
3243 MOZ_COUNT_CTOR(nsDisplayOpacity);
3244 }
3246 #ifdef NS_BUILD_REFCNT_LOGGING
3247 nsDisplayOpacity::~nsDisplayOpacity() {
3248 MOZ_COUNT_DTOR(nsDisplayOpacity);
3249 }
3250 #endif
3252 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
3253 bool* aSnap) {
3254 *aSnap = false;
3255 // We are never opaque, if our opacity was < 1 then we wouldn't have
3256 // been created.
3257 return nsRegion();
3258 }
3260 // nsDisplayOpacity uses layers for rendering
3261 already_AddRefed<Layer>
3262 nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
3263 LayerManager* aManager,
3264 const ContainerLayerParameters& aContainerParameters) {
3265 if (mFrame->StyleDisplay()->mOpacity == 0 && mFrame->GetContent() &&
3266 !nsLayoutUtils::HasAnimations(mFrame->GetContent(), eCSSProperty_opacity)) {
3267 return nullptr;
3268 }
3269 nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
3270 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
3271 aContainerParameters, nullptr);
3272 if (!container)
3273 return nullptr;
3275 container->SetOpacity(mFrame->StyleDisplay()->mOpacity);
3276 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
3277 this, mFrame,
3278 eCSSProperty_opacity);
3279 return container.forget();
3280 }
3282 /**
3283 * This doesn't take into account layer scaling --- the layer may be
3284 * rendered at a higher (or lower) resolution, affecting the retained layer
3285 * size --- but this should be good enough.
3286 */
3287 static bool
3288 IsItemTooSmallForActiveLayer(nsDisplayItem* aItem)
3289 {
3290 nsIntRect visibleDevPixels = aItem->GetVisibleRect().ToOutsidePixels(
3291 aItem->Frame()->PresContext()->AppUnitsPerDevPixel());
3292 static const int MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS = 16;
3293 return visibleDevPixels.Size() <
3294 nsIntSize(MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS, MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS);
3295 }
3297 bool
3298 nsDisplayOpacity::NeedsActiveLayer()
3299 {
3300 if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_opacity) &&
3301 !IsItemTooSmallForActiveLayer(this))
3302 return true;
3303 if (mFrame->GetContent()) {
3304 if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(),
3305 eCSSProperty_opacity)) {
3306 return true;
3307 }
3308 }
3309 return false;
3310 }
3312 bool
3313 nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder* aBuilder)
3314 {
3315 if (NeedsActiveLayer())
3316 return false;
3318 nsDisplayItem* child = mList.GetBottom();
3319 // Only try folding our opacity down if we have a single
3320 // child. We could potentially do this also if we had multiple
3321 // children as long as they don't overlap.
3322 if (!child || child->GetAbove()) {
3323 return false;
3324 }
3326 return child->ApplyOpacity(aBuilder, mFrame->StyleDisplay()->mOpacity, mClip);
3327 }
3329 nsDisplayItem::LayerState
3330 nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder,
3331 LayerManager* aManager,
3332 const ContainerLayerParameters& aParameters) {
3333 if (NeedsActiveLayer())
3334 return LAYER_ACTIVE;
3336 return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList,
3337 nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder));
3338 }
3340 bool
3341 nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3342 nsRegion* aVisibleRegion,
3343 const nsRect& aAllowVisibleRegionExpansion) {
3344 // Our children are translucent so we should not allow them to subtract
3345 // area from aVisibleRegion. We do need to find out what is visible under
3346 // our children in the temporary compositing buffer, because if our children
3347 // paint our entire bounds opaquely then we don't need an alpha channel in
3348 // the temporary compositing buffer.
3349 nsRect bounds = GetClippedBounds(aBuilder);
3350 nsRegion visibleUnderChildren;
3351 visibleUnderChildren.And(*aVisibleRegion, bounds);
3352 nsRect allowExpansion = bounds.Intersect(aAllowVisibleRegionExpansion);
3353 return
3354 nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren,
3355 allowExpansion);
3356 }
3358 bool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
3359 if (aItem->GetType() != TYPE_OPACITY)
3360 return false;
3361 // items for the same content element should be merged into a single
3362 // compositing group
3363 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
3364 if (aItem->Frame()->GetContent() != mFrame->GetContent())
3365 return false;
3366 if (aItem->GetClip() != GetClip())
3367 return false;
3368 MergeFromTrackingMergedFrames(static_cast<nsDisplayOpacity*>(aItem));
3369 return true;
3370 }
3372 #ifdef MOZ_DUMP_PAINTING
3373 void
3374 nsDisplayOpacity::WriteDebugInfo(nsACString& aTo)
3375 {
3376 aTo += nsPrintfCString(" (opacity %f)", mFrame->StyleDisplay()->mOpacity);
3377 }
3378 #endif
3380 nsDisplayMixBlendMode::nsDisplayMixBlendMode(nsDisplayListBuilder* aBuilder,
3381 nsIFrame* aFrame, nsDisplayList* aList,
3382 uint32_t aFlags)
3383 : nsDisplayWrapList(aBuilder, aFrame, aList) {
3384 MOZ_COUNT_CTOR(nsDisplayMixBlendMode);
3385 }
3387 #ifdef NS_BUILD_REFCNT_LOGGING
3388 nsDisplayMixBlendMode::~nsDisplayMixBlendMode() {
3389 MOZ_COUNT_DTOR(nsDisplayMixBlendMode);
3390 }
3391 #endif
3393 nsRegion nsDisplayMixBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
3394 bool* aSnap) {
3395 *aSnap = false;
3396 // We are never considered opaque
3397 return nsRegion();
3398 }
3400 // nsDisplayMixBlendMode uses layers for rendering
3401 already_AddRefed<Layer>
3402 nsDisplayMixBlendMode::BuildLayer(nsDisplayListBuilder* aBuilder,
3403 LayerManager* aManager,
3404 const ContainerLayerParameters& aContainerParameters) {
3405 ContainerLayerParameters newContainerParameters = aContainerParameters;
3406 newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
3408 nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
3409 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
3410 newContainerParameters, nullptr);
3411 if (!container) {
3412 return nullptr;
3413 }
3415 container->DeprecatedSetMixBlendMode(nsCSSRendering::GetGFXBlendMode(mFrame->StyleDisplay()->mMixBlendMode));
3417 return container.forget();
3418 }
3420 bool nsDisplayMixBlendMode::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3421 nsRegion* aVisibleRegion,
3422 const nsRect& aAllowVisibleRegionExpansion) {
3423 // Our children are need their backdrop so we should not allow them to subtract
3424 // area from aVisibleRegion. We do need to find out what is visible under
3425 // our children in the temporary compositing buffer, because if our children
3426 // paint our entire bounds opaquely then we don't need an alpha channel in
3427 // the temporary compositing buffer.
3428 nsRect bounds = GetClippedBounds(aBuilder);
3429 nsRegion visibleUnderChildren;
3430 visibleUnderChildren.And(*aVisibleRegion, bounds);
3431 nsRect allowExpansion = bounds.Intersect(aAllowVisibleRegionExpansion);
3432 return
3433 nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren,
3434 allowExpansion);
3435 }
3437 bool nsDisplayMixBlendMode::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
3438 if (aItem->GetType() != TYPE_MIX_BLEND_MODE)
3439 return false;
3440 // items for the same content element should be merged into a single
3441 // compositing group
3442 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
3443 if (aItem->Frame()->GetContent() != mFrame->GetContent())
3444 return false;
3445 if (aItem->GetClip() != GetClip())
3446 return false;
3447 MergeFromTrackingMergedFrames(static_cast<nsDisplayMixBlendMode*>(aItem));
3448 return true;
3449 }
3451 nsDisplayBlendContainer::nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder,
3452 nsIFrame* aFrame, nsDisplayList* aList,
3453 uint32_t aFlags)
3454 : nsDisplayWrapList(aBuilder, aFrame, aList) {
3455 MOZ_COUNT_CTOR(nsDisplayBlendContainer);
3456 }
3458 #ifdef NS_BUILD_REFCNT_LOGGING
3459 nsDisplayBlendContainer::~nsDisplayBlendContainer() {
3460 MOZ_COUNT_DTOR(nsDisplayBlendContainer);
3461 }
3462 #endif
3464 // nsDisplayBlendContainer uses layers for rendering
3465 already_AddRefed<Layer>
3466 nsDisplayBlendContainer::BuildLayer(nsDisplayListBuilder* aBuilder,
3467 LayerManager* aManager,
3468 const ContainerLayerParameters& aContainerParameters) {
3469 // turn off anti-aliasing in the parent stacking context because it changes
3470 // how the group is initialized.
3471 ContainerLayerParameters newContainerParameters = aContainerParameters;
3472 newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
3474 nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
3475 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
3476 newContainerParameters, nullptr);
3477 if (!container) {
3478 return nullptr;
3479 }
3481 container->SetForceIsolatedGroup(true);
3482 return container.forget();
3483 }
3485 bool nsDisplayBlendContainer::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
3486 if (aItem->GetType() != TYPE_BLEND_CONTAINER)
3487 return false;
3488 // items for the same content element should be merged into a single
3489 // compositing group
3490 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
3491 if (aItem->Frame()->GetContent() != mFrame->GetContent())
3492 return false;
3493 if (aItem->GetClip() != GetClip())
3494 return false;
3495 MergeFromTrackingMergedFrames(static_cast<nsDisplayBlendContainer*>(aItem));
3496 return true;
3497 }
3499 nsDisplayOwnLayer::nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder,
3500 nsIFrame* aFrame, nsDisplayList* aList,
3501 uint32_t aFlags, ViewID aScrollTarget)
3502 : nsDisplayWrapList(aBuilder, aFrame, aList)
3503 , mFlags(aFlags)
3504 , mScrollTarget(aScrollTarget) {
3505 MOZ_COUNT_CTOR(nsDisplayOwnLayer);
3506 }
3508 #ifdef NS_BUILD_REFCNT_LOGGING
3509 nsDisplayOwnLayer::~nsDisplayOwnLayer() {
3510 MOZ_COUNT_DTOR(nsDisplayOwnLayer);
3511 }
3512 #endif
3514 // nsDisplayOpacity uses layers for rendering
3515 already_AddRefed<Layer>
3516 nsDisplayOwnLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
3517 LayerManager* aManager,
3518 const ContainerLayerParameters& aContainerParameters) {
3519 nsRefPtr<ContainerLayer> layer = aManager->GetLayerBuilder()->
3520 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
3521 aContainerParameters, nullptr);
3522 if (mFlags & VERTICAL_SCROLLBAR) {
3523 layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::VERTICAL);
3524 }
3525 if (mFlags & HORIZONTAL_SCROLLBAR) {
3526 layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::HORIZONTAL);
3527 }
3529 if (mFlags & GENERATE_SUBDOC_INVALIDATIONS) {
3530 mFrame->PresContext()->SetNotifySubDocInvalidationData(layer);
3531 }
3532 return layer.forget();
3533 }
3535 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
3536 nsIFrame* aFrame, nsDisplayList* aList,
3537 uint32_t aFlags)
3538 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aFlags)
3539 , mScrollParentId(aBuilder->GetCurrentScrollParentId())
3540 {
3541 MOZ_COUNT_CTOR(nsDisplaySubDocument);
3542 }
3544 #ifdef NS_BUILD_REFCNT_LOGGING
3545 nsDisplaySubDocument::~nsDisplaySubDocument() {
3546 MOZ_COUNT_DTOR(nsDisplaySubDocument);
3547 }
3548 #endif
3550 already_AddRefed<Layer>
3551 nsDisplaySubDocument::BuildLayer(nsDisplayListBuilder* aBuilder,
3552 LayerManager* aManager,
3553 const ContainerLayerParameters& aContainerParameters) {
3554 nsRefPtr<Layer> layer = nsDisplayOwnLayer::BuildLayer(
3555 aBuilder, aManager, aContainerParameters);
3557 if (!(mFlags & GENERATE_SCROLLABLE_LAYER)) {
3558 return layer.forget();
3559 }
3561 NS_ASSERTION(layer->AsContainerLayer(), "nsDisplayOwnLayer should have made a ContainerLayer");
3562 if (ContainerLayer* container = layer->AsContainerLayer()) {
3563 nsPresContext* presContext = mFrame->PresContext();
3564 nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame();
3565 bool isRootContentDocument = presContext->IsRootContentDocument();
3567 bool usingDisplayport = false;
3568 bool usingCriticalDisplayport = false;
3569 nsRect displayport, criticalDisplayport;
3570 ViewID scrollId = FrameMetrics::NULL_SCROLL_ID;
3571 if (rootScrollFrame) {
3572 nsIContent* content = rootScrollFrame->GetContent();
3573 if (content) {
3574 usingDisplayport = nsLayoutUtils::GetDisplayPort(content, &displayport);
3575 usingCriticalDisplayport =
3576 nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport);
3578 if (isRootContentDocument) {
3579 scrollId = nsLayoutUtils::FindOrCreateIDFor(content);
3580 } else {
3581 nsLayoutUtils::FindIDFor(content, &scrollId);
3582 }
3583 }
3584 }
3586 nsRect viewport = mFrame->GetRect() -
3587 mFrame->GetPosition() +
3588 mFrame->GetOffsetToCrossDoc(ReferenceFrame());
3590 container->SetScrollHandoffParentId(mScrollParentId);
3591 RecordFrameMetrics(mFrame, rootScrollFrame, ReferenceFrame(),
3592 container, mList.GetVisibleRect(), viewport,
3593 (usingDisplayport ? &displayport : nullptr),
3594 (usingCriticalDisplayport ? &criticalDisplayport : nullptr),
3595 scrollId, isRootContentDocument, aContainerParameters);
3596 }
3598 return layer.forget();
3599 }
3601 nsRect
3602 nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
3603 {
3604 bool usingDisplayPort =
3605 nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext());
3607 if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) {
3608 *aSnap = false;
3609 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame);
3610 }
3612 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap);
3613 }
3615 bool
3616 nsDisplaySubDocument::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3617 nsRegion* aVisibleRegion,
3618 const nsRect& aAllowVisibleRegionExpansion)
3619 {
3620 nsRect displayport;
3621 bool usingDisplayPort =
3622 nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext(), &displayport);
3624 if (!(mFlags & GENERATE_SCROLLABLE_LAYER) || !usingDisplayPort) {
3625 return nsDisplayWrapList::ComputeVisibility(aBuilder, aVisibleRegion,
3626 aAllowVisibleRegionExpansion);
3627 }
3629 nsRegion childVisibleRegion;
3630 // The visible region for the children may be much bigger than the hole we
3631 // are viewing the children from, so that the compositor process has enough
3632 // content to asynchronously pan while content is being refreshed.
3633 childVisibleRegion = displayport + mFrame->GetOffsetToCrossDoc(ReferenceFrame());
3635 nsRect boundedRect =
3636 childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder));
3637 nsRect allowExpansion = boundedRect.Intersect(aAllowVisibleRegionExpansion);
3638 bool visible = mList.ComputeVisibilityForSublist(
3639 aBuilder, &childVisibleRegion, boundedRect, allowExpansion,
3640 usingDisplayPort ? mFrame : nullptr);
3641 // We don't allow this computation to influence aVisibleRegion, on the
3642 // assumption that the layer can be asynchronously scrolled so we'll
3643 // definitely need all the content under it.
3645 return visible;
3646 }
3648 bool
3649 nsDisplaySubDocument::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
3650 {
3651 bool usingDisplayPort =
3652 nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext());
3654 if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) {
3655 return true;
3656 }
3658 return nsDisplayOwnLayer::ShouldBuildLayerEvenIfInvisible(aBuilder);
3659 }
3661 nsRegion
3662 nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap)
3663 {
3664 bool usingDisplayPort =
3665 nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext());
3667 if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) {
3668 *aSnap = false;
3669 return nsRegion();
3670 }
3672 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap);
3673 }
3675 nsDisplayResolution::nsDisplayResolution(nsDisplayListBuilder* aBuilder,
3676 nsIFrame* aFrame, nsDisplayList* aList,
3677 uint32_t aFlags)
3678 : nsDisplaySubDocument(aBuilder, aFrame, aList, aFlags) {
3679 MOZ_COUNT_CTOR(nsDisplayResolution);
3680 }
3682 #ifdef NS_BUILD_REFCNT_LOGGING
3683 nsDisplayResolution::~nsDisplayResolution() {
3684 MOZ_COUNT_DTOR(nsDisplayResolution);
3685 }
3686 #endif
3688 already_AddRefed<Layer>
3689 nsDisplayResolution::BuildLayer(nsDisplayListBuilder* aBuilder,
3690 LayerManager* aManager,
3691 const ContainerLayerParameters& aContainerParameters) {
3692 nsIPresShell* presShell = mFrame->PresContext()->PresShell();
3693 ContainerLayerParameters containerParameters(
3694 presShell->GetXResolution(), presShell->GetYResolution(), nsIntPoint(),
3695 aContainerParameters);
3697 nsRefPtr<Layer> layer = nsDisplaySubDocument::BuildLayer(
3698 aBuilder, aManager, containerParameters);
3699 layer->SetPostScale(1.0f / presShell->GetXResolution(),
3700 1.0f / presShell->GetYResolution());
3701 return layer.forget();
3702 }
3704 nsDisplayStickyPosition::nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder,
3705 nsIFrame* aFrame,
3706 nsDisplayList* aList)
3707 : nsDisplayOwnLayer(aBuilder, aFrame, aList)
3708 {
3709 MOZ_COUNT_CTOR(nsDisplayStickyPosition);
3710 }
3712 #ifdef NS_BUILD_REFCNT_LOGGING
3713 nsDisplayStickyPosition::~nsDisplayStickyPosition() {
3714 MOZ_COUNT_DTOR(nsDisplayStickyPosition);
3715 }
3716 #endif
3718 already_AddRefed<Layer>
3719 nsDisplayStickyPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
3720 LayerManager* aManager,
3721 const ContainerLayerParameters& aContainerParameters) {
3722 nsRefPtr<Layer> layer =
3723 nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
3725 StickyScrollContainer* stickyScrollContainer = StickyScrollContainer::
3726 GetStickyScrollContainerForFrame(mFrame);
3727 if (!stickyScrollContainer) {
3728 return layer.forget();
3729 }
3731 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
3732 nsPresContext* presContext = scrollFrame->PresContext();
3734 // Sticky position frames whose scroll frame is the root scroll frame are
3735 // reflowed into the scroll-port size if one has been set.
3736 nsSize scrollFrameSize = scrollFrame->GetSize();
3737 if (scrollFrame == presContext->PresShell()->GetRootScrollFrame() &&
3738 presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
3739 scrollFrameSize = presContext->PresShell()->
3740 GetScrollPositionClampingScrollPortSize();
3741 }
3743 nsLayoutUtils::SetFixedPositionLayerData(layer, scrollFrame,
3744 nsRect(scrollFrame->GetOffsetToCrossDoc(ReferenceFrame()), scrollFrameSize),
3745 mFrame, presContext, aContainerParameters);
3747 ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(
3748 stickyScrollContainer->ScrollFrame()->GetScrolledFrame()->GetContent());
3750 float factor = presContext->AppUnitsPerDevPixel();
3751 nsRect outer;
3752 nsRect inner;
3753 stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
3754 LayerRect stickyOuter(NSAppUnitsToFloatPixels(outer.x, factor) *
3755 aContainerParameters.mXScale,
3756 NSAppUnitsToFloatPixels(outer.y, factor) *
3757 aContainerParameters.mYScale,
3758 NSAppUnitsToFloatPixels(outer.width, factor) *
3759 aContainerParameters.mXScale,
3760 NSAppUnitsToFloatPixels(outer.height, factor) *
3761 aContainerParameters.mYScale);
3762 LayerRect stickyInner(NSAppUnitsToFloatPixels(inner.x, factor) *
3763 aContainerParameters.mXScale,
3764 NSAppUnitsToFloatPixels(inner.y, factor) *
3765 aContainerParameters.mYScale,
3766 NSAppUnitsToFloatPixels(inner.width, factor) *
3767 aContainerParameters.mXScale,
3768 NSAppUnitsToFloatPixels(inner.height, factor) *
3769 aContainerParameters.mYScale);
3770 layer->SetStickyPositionData(scrollId, stickyOuter, stickyInner);
3772 return layer.forget();
3773 }
3775 bool nsDisplayStickyPosition::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
3776 if (aItem->GetType() != TYPE_STICKY_POSITION)
3777 return false;
3778 // Items with the same fixed position frame can be merged.
3779 nsDisplayStickyPosition* other = static_cast<nsDisplayStickyPosition*>(aItem);
3780 if (other->mFrame != mFrame)
3781 return false;
3782 if (aItem->GetClip() != GetClip())
3783 return false;
3784 MergeFromTrackingMergedFrames(other);
3785 return true;
3786 }
3788 nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder,
3789 nsDisplayList* aList,
3790 nsIFrame* aForFrame,
3791 nsIFrame* aScrolledFrame,
3792 nsIFrame* aScrollFrame)
3793 : nsDisplayWrapList(aBuilder, aForFrame, aList)
3794 , mScrollFrame(aScrollFrame)
3795 , mScrolledFrame(aScrolledFrame)
3796 , mScrollParentId(aBuilder->GetCurrentScrollParentId())
3797 {
3798 #ifdef NS_BUILD_REFCNT_LOGGING
3799 MOZ_COUNT_CTOR(nsDisplayScrollLayer);
3800 #endif
3802 NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(),
3803 "Need a child frame with content");
3804 }
3806 nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder,
3807 nsDisplayItem* aItem,
3808 nsIFrame* aForFrame,
3809 nsIFrame* aScrolledFrame,
3810 nsIFrame* aScrollFrame)
3811 : nsDisplayWrapList(aBuilder, aForFrame, aItem)
3812 , mScrollFrame(aScrollFrame)
3813 , mScrolledFrame(aScrolledFrame)
3814 , mScrollParentId(aBuilder->GetCurrentScrollParentId())
3815 {
3816 #ifdef NS_BUILD_REFCNT_LOGGING
3817 MOZ_COUNT_CTOR(nsDisplayScrollLayer);
3818 #endif
3820 NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(),
3821 "Need a child frame with content");
3822 }
3824 nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder,
3825 nsIFrame* aForFrame,
3826 nsIFrame* aScrolledFrame,
3827 nsIFrame* aScrollFrame)
3828 : nsDisplayWrapList(aBuilder, aForFrame)
3829 , mScrollFrame(aScrollFrame)
3830 , mScrolledFrame(aScrolledFrame)
3831 , mScrollParentId(aBuilder->GetCurrentScrollParentId())
3832 {
3833 #ifdef NS_BUILD_REFCNT_LOGGING
3834 MOZ_COUNT_CTOR(nsDisplayScrollLayer);
3835 #endif
3837 NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(),
3838 "Need a child frame with content");
3839 }
3841 #ifdef NS_BUILD_REFCNT_LOGGING
3842 nsDisplayScrollLayer::~nsDisplayScrollLayer()
3843 {
3844 MOZ_COUNT_DTOR(nsDisplayScrollLayer);
3845 }
3846 #endif
3848 nsRect
3849 nsDisplayScrollLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
3850 {
3851 nsIScrollableFrame* sf = do_QueryFrame(mScrollFrame);
3852 if (sf) {
3853 *aSnap = false;
3854 return sf->GetScrollPortRect() + aBuilder->ToReferenceFrame(mScrollFrame);
3855 }
3856 return nsDisplayWrapList::GetBounds(aBuilder, aSnap);
3857 }
3859 already_AddRefed<Layer>
3860 nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
3861 LayerManager* aManager,
3862 const ContainerLayerParameters& aContainerParameters) {
3863 nsRefPtr<ContainerLayer> layer = aManager->GetLayerBuilder()->
3864 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
3865 aContainerParameters, nullptr);
3867 // Get the already set unique ID for scrolling this content remotely.
3868 // Or, if not set, generate a new ID.
3869 nsIContent* content = mScrolledFrame->GetContent();
3870 ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(content);
3872 nsRect viewport = mScrollFrame->GetRect() -
3873 mScrollFrame->GetPosition() +
3874 mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
3876 bool usingDisplayport = false;
3877 bool usingCriticalDisplayport = false;
3878 nsRect displayport, criticalDisplayport;
3879 if (content) {
3880 usingDisplayport = nsLayoutUtils::GetDisplayPort(content, &displayport);
3881 usingCriticalDisplayport =
3882 nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport);
3883 }
3884 layer->SetScrollHandoffParentId(mScrollParentId);
3885 RecordFrameMetrics(mScrolledFrame, mScrollFrame, ReferenceFrame(), layer,
3886 mList.GetVisibleRect(), viewport,
3887 (usingDisplayport ? &displayport : nullptr),
3888 (usingCriticalDisplayport ? &criticalDisplayport : nullptr),
3889 scrollId, false, aContainerParameters);
3891 return layer.forget();
3892 }
3894 bool
3895 nsDisplayScrollLayer::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
3896 {
3897 if (nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), nullptr)) {
3898 return true;
3899 }
3901 return nsDisplayWrapList::ShouldBuildLayerEvenIfInvisible(aBuilder);
3902 }
3904 bool
3905 nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder,
3906 nsRegion* aVisibleRegion,
3907 const nsRect& aAllowVisibleRegionExpansion)
3908 {
3909 nsRect displayport;
3910 bool usingDisplayPort =
3911 nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), &displayport);
3912 nsRegion childVisibleRegion;
3913 if (usingDisplayPort) {
3914 // The visible region for the children may be much bigger than the hole we
3915 // are viewing the children from, so that the compositor process has enough
3916 // content to asynchronously pan while content is being refreshed.
3917 childVisibleRegion = displayport + mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
3918 } else {
3919 bool snap;
3920 childVisibleRegion = GetBounds(aBuilder, &snap);
3921 }
3923 nsRect boundedRect =
3924 childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder));
3925 nsRect allowExpansion = boundedRect.Intersect(aAllowVisibleRegionExpansion);
3926 bool visible = mList.ComputeVisibilityForSublist(
3927 aBuilder, &childVisibleRegion, boundedRect, allowExpansion,
3928 usingDisplayPort ? mScrollFrame : nullptr);
3929 // We don't allow this computation to influence aVisibleRegion, on the
3930 // assumption that the layer can be asynchronously scrolled so we'll
3931 // definitely need all the content under it.
3933 return visible;
3934 }
3936 LayerState
3937 nsDisplayScrollLayer::GetLayerState(nsDisplayListBuilder* aBuilder,
3938 LayerManager* aManager,
3939 const ContainerLayerParameters& aParameters)
3940 {
3941 // Force this as a layer so we can scroll asynchronously.
3942 // This causes incorrect rendering for rounded clips!
3943 return LAYER_ACTIVE_FORCE;
3944 }
3946 // Check if we are going to clip an abs pos item that we don't contain.
3947 // Root scroll frames clip all their descendants, so we don't need to worry
3948 // about them.
3949 bool
3950 WouldCauseIncorrectClippingOnAbsPosItem(nsDisplayListBuilder* aBuilder,
3951 nsDisplayScrollLayer* aItem)
3952 {
3953 nsIFrame* scrollFrame = aItem->GetScrollFrame();
3954 nsIPresShell* presShell = scrollFrame->PresContext()->PresShell();
3955 if (scrollFrame == presShell->GetRootScrollFrame()) {
3956 return false;
3957 }
3958 nsIFrame* scrolledFrame = aItem->GetScrolledFrame();
3959 nsIFrame* frame = aItem->Frame();
3960 if (frame == scrolledFrame || !frame->IsAbsolutelyPositioned() ||
3961 nsLayoutUtils::IsAncestorFrameCrossDoc(scrollFrame, frame, presShell->GetRootFrame())) {
3962 return false;
3963 }
3964 if (!aItem->GetClip().IsRectAffectedByClip(aItem->GetChildren()->GetBounds(aBuilder))) {
3965 return false;
3966 }
3967 return true;
3968 }
3970 bool
3971 nsDisplayScrollLayer::TryMerge(nsDisplayListBuilder* aBuilder,
3972 nsDisplayItem* aItem)
3973 {
3974 if (aItem->GetType() != TYPE_SCROLL_LAYER) {
3975 return false;
3976 }
3977 nsDisplayScrollLayer* other = static_cast<nsDisplayScrollLayer*>(aItem);
3978 if (other->mScrolledFrame != this->mScrolledFrame) {
3979 return false;
3980 }
3981 if (aItem->GetClip() != GetClip()) {
3982 return false;
3983 }
3985 if (WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, this) ||
3986 WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, other)) {
3987 return false;
3988 }
3990 NS_ASSERTION(other->mReferenceFrame == mReferenceFrame,
3991 "Must have the same reference frame!");
3993 FrameProperties props = mScrolledFrame->Properties();
3994 props.Set(nsIFrame::ScrollLayerCount(),
3995 reinterpret_cast<void*>(GetScrollLayerCount() - 1));
3997 // Swap frames with the other item before doing MergeFrom.
3998 // XXX - This ensures that the frame associated with a scroll layer after
3999 // merging is the first, rather than the last. This tends to change less,
4000 // ensuring we're more likely to retain the associated gfx layer.
4001 // See Bug 729534 and Bug 731641.
4002 nsIFrame* tmp = mFrame;
4003 mFrame = other->mFrame;
4004 other->mFrame = tmp;
4005 MergeFromTrackingMergedFrames(other);
4006 return true;
4007 }
4009 void
4010 PropagateClip(nsDisplayListBuilder* aBuilder, const DisplayItemClip& aClip,
4011 nsDisplayList* aList)
4012 {
4013 for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) {
4014 DisplayItemClip clip(i->GetClip());
4015 clip.IntersectWith(aClip);
4016 i->SetClip(aBuilder, clip);
4017 nsDisplayList* list = i->GetSameCoordinateSystemChildren();
4018 if (list) {
4019 PropagateClip(aBuilder, aClip, list);
4020 }
4021 }
4022 }
4024 bool
4025 nsDisplayScrollLayer::ShouldFlattenAway(nsDisplayListBuilder* aBuilder)
4026 {
4027 bool badAbsPosClip = WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, this);
4028 if (GetScrollLayerCount() > 1 || badAbsPosClip) {
4029 // Propagate our clip to our children. The clip for the scroll frame is
4030 // on this item, but not our child items so that they can draw non-visible
4031 // parts of the display port. But if we are flattening we failed and can't
4032 // draw the extra content, so it needs to be clipped.
4033 // But don't induce our clip on abs pos frames that we shouldn't be clipping.
4034 if (!badAbsPosClip) {
4035 PropagateClip(aBuilder, GetClip(), &mList);
4036 }
4037 return true;
4038 }
4039 if (mFrame != mScrolledFrame) {
4040 mMergedFrames.AppendElement(mFrame);
4041 mFrame = mScrolledFrame;
4042 }
4043 return false;
4044 }
4046 intptr_t
4047 nsDisplayScrollLayer::GetScrollLayerCount()
4048 {
4049 FrameProperties props = mScrolledFrame->Properties();
4050 #ifdef DEBUG
4051 bool hasCount = false;
4052 intptr_t result = reinterpret_cast<intptr_t>(
4053 props.Get(nsIFrame::ScrollLayerCount(), &hasCount));
4054 // If this aborts, then the property was either not added before scroll
4055 // layers were created or the property was deleted to early. If the latter,
4056 // make sure that nsDisplayScrollInfoLayer is on the bottom of the list so
4057 // that it is processed last.
4058 NS_ABORT_IF_FALSE(hasCount, "nsDisplayScrollLayer should always be defined");
4059 return result;
4060 #else
4061 return reinterpret_cast<intptr_t>(props.Get(nsIFrame::ScrollLayerCount()));
4062 #endif
4063 }
4065 #ifdef MOZ_DUMP_PAINTING
4066 void
4067 nsDisplayScrollLayer::WriteDebugInfo(nsACString& aTo)
4068 {
4069 aTo += nsPrintfCString(" (scrollframe %p scrolledframe %p)",
4070 mScrollFrame, mScrolledFrame);
4071 }
4072 #endif
4074 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
4075 nsDisplayListBuilder* aBuilder,
4076 nsIFrame* aScrolledFrame,
4077 nsIFrame* aScrollFrame)
4078 : nsDisplayScrollLayer(aBuilder, aScrollFrame, aScrolledFrame, aScrollFrame)
4079 {
4080 #ifdef NS_BUILD_REFCNT_LOGGING
4081 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
4082 #endif
4083 }
4085 nsDisplayScrollInfoLayer::~nsDisplayScrollInfoLayer()
4086 {
4087 FrameProperties props = mScrolledFrame->Properties();
4088 props.Remove(nsIFrame::ScrollLayerCount());
4089 MOZ_COUNT_DTOR(nsDisplayScrollInfoLayer);
4090 }
4092 nsRect
4093 nsDisplayScrollInfoLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
4094 {
4095 return nsDisplayWrapList::GetBounds(aBuilder, aSnap);
4096 }
4098 LayerState
4099 nsDisplayScrollInfoLayer::GetLayerState(nsDisplayListBuilder* aBuilder,
4100 LayerManager* aManager,
4101 const ContainerLayerParameters& aParameters)
4102 {
4103 return LAYER_ACTIVE_EMPTY;
4104 }
4106 bool
4107 nsDisplayScrollInfoLayer::TryMerge(nsDisplayListBuilder* aBuilder,
4108 nsDisplayItem* aItem)
4109 {
4110 return false;
4111 }
4113 bool
4114 nsDisplayScrollInfoLayer::ShouldFlattenAway(nsDisplayListBuilder* aBuilder)
4115 {
4116 // Layer metadata for a particular scroll frame needs to be unique. Only
4117 // one nsDisplayScrollLayer (with rendered content) or one
4118 // nsDisplayScrollInfoLayer (with only the metadata) should survive the
4119 // visibility computation.
4120 return GetScrollLayerCount() == 1;
4121 }
4123 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder,
4124 nsIFrame* aFrame, nsDisplayList* aList,
4125 int32_t aAPD, int32_t aParentAPD,
4126 uint32_t aFlags)
4127 : nsDisplaySubDocument(aBuilder, aFrame, aList, aFlags)
4128 , mAPD(aAPD), mParentAPD(aParentAPD) {
4129 MOZ_COUNT_CTOR(nsDisplayZoom);
4130 }
4132 #ifdef NS_BUILD_REFCNT_LOGGING
4133 nsDisplayZoom::~nsDisplayZoom() {
4134 MOZ_COUNT_DTOR(nsDisplayZoom);
4135 }
4136 #endif
4138 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
4139 {
4140 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap);
4141 *aSnap = false;
4142 return bounds.ConvertAppUnitsRoundOut(mAPD, mParentAPD);
4143 }
4145 void nsDisplayZoom::HitTest(nsDisplayListBuilder *aBuilder,
4146 const nsRect& aRect,
4147 HitTestState *aState,
4148 nsTArray<nsIFrame*> *aOutFrames)
4149 {
4150 nsRect rect;
4151 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
4152 // rect as well instead of possibly rounding the width or height to zero.
4153 if (aRect.width == 1 && aRect.height == 1) {
4154 rect.MoveTo(aRect.TopLeft().ConvertAppUnits(mParentAPD, mAPD));
4155 rect.width = rect.height = 1;
4156 } else {
4157 rect = aRect.ConvertAppUnitsRoundOut(mParentAPD, mAPD);
4158 }
4159 mList.HitTest(aBuilder, rect, aState, aOutFrames);
4160 }
4162 void nsDisplayZoom::Paint(nsDisplayListBuilder* aBuilder,
4163 nsRenderingContext* aCtx)
4164 {
4165 mList.PaintForFrame(aBuilder, aCtx, mFrame, nsDisplayList::PAINT_DEFAULT);
4166 }
4168 bool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder *aBuilder,
4169 nsRegion *aVisibleRegion,
4170 const nsRect& aAllowVisibleRegionExpansion)
4171 {
4172 // Convert the passed in visible region to our appunits.
4173 nsRegion visibleRegion;
4174 // mVisibleRect has been clipped to GetClippedBounds
4175 visibleRegion.And(*aVisibleRegion, mVisibleRect);
4176 visibleRegion = visibleRegion.ConvertAppUnitsRoundOut(mParentAPD, mAPD);
4177 nsRegion originalVisibleRegion = visibleRegion;
4179 nsRect transformedVisibleRect =
4180 mVisibleRect.ConvertAppUnitsRoundOut(mParentAPD, mAPD);
4181 nsRect allowExpansion =
4182 aAllowVisibleRegionExpansion.ConvertAppUnitsRoundIn(mParentAPD, mAPD);
4183 bool retval;
4184 // If we are to generate a scrollable layer we call
4185 // nsDisplaySubDocument::ComputeVisibility to make the necessary adjustments
4186 // for ComputeVisibility, it does all it's calculations in the child APD.
4187 bool usingDisplayPort =
4188 nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext());
4189 if (!(mFlags & GENERATE_SCROLLABLE_LAYER) || !usingDisplayPort) {
4190 retval =
4191 mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion,
4192 transformedVisibleRect,
4193 allowExpansion);
4194 } else {
4195 retval =
4196 nsDisplaySubDocument::ComputeVisibility(aBuilder, &visibleRegion,
4197 allowExpansion);
4198 }
4200 nsRegion removed;
4201 // removed = originalVisibleRegion - visibleRegion
4202 removed.Sub(originalVisibleRegion, visibleRegion);
4203 // Convert removed region to parent appunits.
4204 removed = removed.ConvertAppUnitsRoundIn(mAPD, mParentAPD);
4205 // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications
4206 // SubtractFromVisibleRegion does)
4207 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed);
4209 return retval;
4210 }
4212 ///////////////////////////////////////////////////
4213 // nsDisplayTransform Implementation
4214 //
4216 // Write #define UNIFIED_CONTINUATIONS here to have the transform property try
4217 // to transform content with continuations as one unified block instead of
4218 // several smaller ones. This is currently disabled because it doesn't work
4219 // correctly, since when the frames are initially being reflowed, their
4220 // continuations all compute their bounding rects independently of each other
4221 // and consequently get the wrong value. Write #define DEBUG_HIT here to have
4222 // the nsDisplayTransform class dump out a bunch of information about hit
4223 // detection.
4224 #undef UNIFIED_CONTINUATIONS
4225 #undef DEBUG_HIT
4227 /* Returns the bounds of a frame as defined for transforms. If
4228 * UNIFIED_CONTINUATIONS is not defined, this is simply the frame's bounding
4229 * rectangle, translated to the origin. Otherwise, returns the smallest
4230 * rectangle containing a frame and all of its continuations. For example, if
4231 * there is a <span> element with several continuations split over several
4232 * lines, this function will return the rectangle containing all of those
4233 * continuations. This rectangle is relative to the origin of the frame's local
4234 * coordinate space.
4235 */
4236 #ifndef UNIFIED_CONTINUATIONS
4238 nsRect
4239 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
4240 {
4241 NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
4243 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
4244 // TODO: SVG needs to define what percentage translations resolve against.
4245 return nsRect();
4246 }
4248 return nsRect(nsPoint(0, 0), aFrame->GetSize());
4249 }
4251 #else
4253 nsRect
4254 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
4255 {
4256 NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
4258 nsRect result;
4260 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
4261 // TODO: SVG needs to define what percentage translations resolve against.
4262 return result;
4263 }
4265 /* Iterate through the continuation list, unioning together all the
4266 * bounding rects.
4267 */
4268 for (const nsIFrame *currFrame = aFrame->FirstContinuation();
4269 currFrame != nullptr;
4270 currFrame = currFrame->GetNextContinuation())
4271 {
4272 /* Get the frame rect in local coordinates, then translate back to the
4273 * original coordinates.
4274 */
4275 result.UnionRect(result, nsRect(currFrame->GetOffsetTo(aFrame),
4276 currFrame->GetSize()));
4277 }
4279 return result;
4280 }
4282 #endif
4284 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame,
4285 nsDisplayList *aList, ComputeTransformFunction aTransformGetter,
4286 uint32_t aIndex)
4287 : nsDisplayItem(aBuilder, aFrame)
4288 , mStoredList(aBuilder, aFrame, aList)
4289 , mTransformGetter(aTransformGetter)
4290 , mIndex(aIndex)
4291 {
4292 MOZ_COUNT_CTOR(nsDisplayTransform);
4293 NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
4294 NS_ABORT_IF_FALSE(!aFrame->IsTransformed(), "Can't specify a transform getter for a transformed frame!");
4295 mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip());
4296 }
4298 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame,
4299 nsDisplayList *aList, uint32_t aIndex)
4300 : nsDisplayItem(aBuilder, aFrame)
4301 , mStoredList(aBuilder, aFrame, aList)
4302 , mTransformGetter(nullptr)
4303 , mIndex(aIndex)
4304 {
4305 MOZ_COUNT_CTOR(nsDisplayTransform);
4306 NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
4307 mReferenceFrame =
4308 aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame));
4309 mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
4310 mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip());
4311 }
4313 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame,
4314 nsDisplayItem *aItem, uint32_t aIndex)
4315 : nsDisplayItem(aBuilder, aFrame)
4316 , mStoredList(aBuilder, aFrame, aItem)
4317 , mTransformGetter(nullptr)
4318 , mIndex(aIndex)
4319 {
4320 MOZ_COUNT_CTOR(nsDisplayTransform);
4321 NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
4322 mReferenceFrame =
4323 aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame));
4324 mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
4325 mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip());
4326 }
4328 /* Returns the delta specified by the -moz-transform-origin property.
4329 * This is a positive delta, meaning that it indicates the direction to move
4330 * to get from (0, 0) of the frame to the transform origin. This function is
4331 * called off the main thread.
4332 */
4333 /* static */ gfxPoint3D
4334 nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame,
4335 float aAppUnitsPerPixel,
4336 const nsRect* aBoundsOverride)
4337 {
4338 NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
4339 NS_PRECONDITION(aFrame->IsTransformed() || aFrame->StyleDisplay()->BackfaceIsHidden(),
4340 "Shouldn't get a delta for an untransformed frame!");
4342 if (!aFrame->IsTransformed()) {
4343 return gfxPoint3D();
4344 }
4346 /* For both of the coordinates, if the value of -moz-transform is a
4347 * percentage, it's relative to the size of the frame. Otherwise, if it's
4348 * a distance, it's already computed for us!
4349 */
4350 const nsStyleDisplay* display = aFrame->StyleDisplay();
4351 nsRect boundingRect = (aBoundsOverride ? *aBoundsOverride :
4352 nsDisplayTransform::GetFrameBoundsForTransform(aFrame));
4354 /* Allows us to access named variables by index. */
4355 float coords[3];
4356 const nscoord* dimensions[2] =
4357 {&boundingRect.width, &boundingRect.height};
4359 for (uint8_t index = 0; index < 2; ++index) {
4360 /* If the -moz-transform-origin specifies a percentage, take the percentage
4361 * of the size of the box.
4362 */
4363 const nsStyleCoord &coord = display->mTransformOrigin[index];
4364 if (coord.GetUnit() == eStyleUnit_Calc) {
4365 const nsStyleCoord::Calc *calc = coord.GetCalcValue();
4366 coords[index] =
4367 NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
4368 calc->mPercent +
4369 NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
4370 } else if (coord.GetUnit() == eStyleUnit_Percent) {
4371 coords[index] =
4372 NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
4373 coord.GetPercentValue();
4374 } else {
4375 NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
4376 coords[index] =
4377 NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
4378 }
4379 if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
4380 coord.GetUnit() != eStyleUnit_Percent) {
4381 // <length> values represent offsets from the origin of the SVG element's
4382 // user space, not the top left of its bounds, so we must adjust for that:
4383 nscoord offset =
4384 (index == 0) ? aFrame->GetPosition().x : aFrame->GetPosition().y;
4385 coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel);
4386 }
4387 }
4389 coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
4390 aAppUnitsPerPixel);
4391 /* Adjust based on the origin of the rectangle. */
4392 coords[0] += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel);
4393 coords[1] += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel);
4395 return gfxPoint3D(coords[0], coords[1], coords[2]);
4396 }
4398 /* Returns the delta specified by the -moz-perspective-origin property.
4399 * This is a positive delta, meaning that it indicates the direction to move
4400 * to get from (0, 0) of the frame to the perspective origin. This function is
4401 * called off the main thread.
4402 */
4403 /* static */ gfxPoint3D
4404 nsDisplayTransform::GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
4405 float aAppUnitsPerPixel)
4406 {
4407 NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
4408 NS_PRECONDITION(aFrame->IsTransformed() || aFrame->StyleDisplay()->BackfaceIsHidden(),
4409 "Shouldn't get a delta for an untransformed frame!");
4411 if (!aFrame->IsTransformed()) {
4412 return gfxPoint3D();
4413 }
4415 /* For both of the coordinates, if the value of -moz-perspective-origin is a
4416 * percentage, it's relative to the size of the frame. Otherwise, if it's
4417 * a distance, it's already computed for us!
4418 */
4420 //TODO: Should this be using our bounds or the parent's bounds?
4421 // How do we handle aBoundsOverride in the latter case?
4422 nsIFrame* parent = aFrame->GetParentStyleContextFrame();
4423 if (!parent) {
4424 return gfxPoint3D();
4425 }
4426 const nsStyleDisplay* display = parent->StyleDisplay();
4427 nsRect boundingRect = nsDisplayTransform::GetFrameBoundsForTransform(parent);
4429 /* Allows us to access named variables by index. */
4430 gfxPoint3D result;
4431 result.z = 0.0f;
4432 gfxFloat* coords[2] = {&result.x, &result.y};
4433 const nscoord* dimensions[2] =
4434 {&boundingRect.width, &boundingRect.height};
4436 for (uint8_t index = 0; index < 2; ++index) {
4437 /* If the -moz-transform-origin specifies a percentage, take the percentage
4438 * of the size of the box.
4439 */
4440 const nsStyleCoord &coord = display->mPerspectiveOrigin[index];
4441 if (coord.GetUnit() == eStyleUnit_Calc) {
4442 const nsStyleCoord::Calc *calc = coord.GetCalcValue();
4443 *coords[index] =
4444 NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
4445 calc->mPercent +
4446 NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
4447 } else if (coord.GetUnit() == eStyleUnit_Percent) {
4448 *coords[index] =
4449 NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
4450 coord.GetPercentValue();
4451 } else {
4452 NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
4453 *coords[index] =
4454 NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
4455 }
4456 }
4458 nsPoint parentOffset = aFrame->GetOffsetTo(parent);
4459 gfxPoint3D gfxOffset(
4460 NSAppUnitsToFloatPixels(parentOffset.x, aAppUnitsPerPixel),
4461 NSAppUnitsToFloatPixels(parentOffset.y, aAppUnitsPerPixel),
4462 0.0f);
4464 return result - gfxOffset;
4465 }
4467 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsIFrame* aFrame,
4468 float aAppUnitsPerPixel,
4469 const nsRect* aBoundsOverride)
4470 : mFrame(aFrame)
4471 , mTransformList(aFrame->StyleDisplay()->mSpecifiedTransform)
4472 , mToTransformOrigin(GetDeltaToTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride))
4473 , mToPerspectiveOrigin(GetDeltaToPerspectiveOrigin(aFrame, aAppUnitsPerPixel))
4474 , mChildPerspective(0)
4475 {
4476 const nsStyleDisplay* parentDisp = nullptr;
4477 nsStyleContext* parentStyleContext = aFrame->StyleContext()->GetParent();
4478 if (parentStyleContext) {
4479 parentDisp = parentStyleContext->StyleDisplay();
4480 }
4481 if (parentDisp && parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord) {
4482 mChildPerspective = parentDisp->mChildPerspective.GetCoordValue();
4483 }
4484 }
4486 /* Wraps up the -moz-transform matrix in a change-of-basis matrix pair that
4487 * translates from local coordinate space to transform coordinate space, then
4488 * hands it back.
4489 */
4490 gfx3DMatrix
4491 nsDisplayTransform::GetResultingTransformMatrix(const FrameTransformProperties& aProperties,
4492 const nsPoint& aOrigin,
4493 float aAppUnitsPerPixel,
4494 const nsRect* aBoundsOverride,
4495 nsIFrame** aOutAncestor)
4496 {
4497 return GetResultingTransformMatrixInternal(aProperties, aOrigin, aAppUnitsPerPixel,
4498 aBoundsOverride, aOutAncestor);
4499 }
4501 gfx3DMatrix
4502 nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame,
4503 const nsPoint& aOrigin,
4504 float aAppUnitsPerPixel,
4505 const nsRect* aBoundsOverride,
4506 nsIFrame** aOutAncestor)
4507 {
4508 FrameTransformProperties props(aFrame,
4509 aAppUnitsPerPixel,
4510 aBoundsOverride);
4512 return GetResultingTransformMatrixInternal(props, aOrigin, aAppUnitsPerPixel,
4513 aBoundsOverride, aOutAncestor);
4514 }
4516 gfx3DMatrix
4517 nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties,
4518 const nsPoint& aOrigin,
4519 float aAppUnitsPerPixel,
4520 const nsRect* aBoundsOverride,
4521 nsIFrame** aOutAncestor)
4522 {
4523 const nsIFrame *frame = aProperties.mFrame;
4525 if (aOutAncestor) {
4526 *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(frame);
4527 }
4529 /* Account for the -moz-transform-origin property by translating the
4530 * coordinate space to the new origin.
4531 */
4532 gfxPoint3D newOrigin =
4533 gfxPoint3D(NSAppUnitsToFloatPixels(aOrigin.x, aAppUnitsPerPixel),
4534 NSAppUnitsToFloatPixels(aOrigin.y, aAppUnitsPerPixel),
4535 0.0f);
4537 /* Get the underlying transform matrix. This requires us to get the
4538 * bounds of the frame.
4539 */
4540 nsRect bounds = (aBoundsOverride ? *aBoundsOverride :
4541 nsDisplayTransform::GetFrameBoundsForTransform(frame));
4543 /* Get the matrix, then change its basis to factor in the origin. */
4544 bool dummy;
4545 gfx3DMatrix result;
4546 // Call IsSVGTransformed() regardless of the value of
4547 // disp->mSpecifiedTransform, since we still need any transformFromSVGParent.
4548 mozilla::gfx::Matrix svgTransform, transformFromSVGParent;
4549 bool hasSVGTransforms =
4550 frame && frame->IsSVGTransformed(&svgTransform, &transformFromSVGParent);
4551 /* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */
4552 if (aProperties.mTransformList) {
4553 result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead,
4554 frame ? frame->StyleContext() : nullptr,
4555 frame ? frame->PresContext() : nullptr,
4556 dummy, bounds, aAppUnitsPerPixel);
4557 } else if (hasSVGTransforms) {
4558 // Correct the translation components for zoom:
4559 float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() /
4560 aAppUnitsPerPixel;
4561 svgTransform._31 *= pixelsPerCSSPx;
4562 svgTransform._32 *= pixelsPerCSSPx;
4563 result = gfx3DMatrix::From2D(ThebesMatrix(svgTransform));
4564 }
4566 if (hasSVGTransforms && !transformFromSVGParent.IsIdentity()) {
4567 // Correct the translation components for zoom:
4568 float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() /
4569 aAppUnitsPerPixel;
4570 transformFromSVGParent._31 *= pixelsPerCSSPx;
4571 transformFromSVGParent._32 *= pixelsPerCSSPx;
4572 result = result * gfx3DMatrix::From2D(ThebesMatrix(transformFromSVGParent));
4573 }
4575 if (aProperties.mChildPerspective > 0.0) {
4576 gfx3DMatrix perspective;
4577 perspective._34 =
4578 -1.0 / NSAppUnitsToFloatPixels(aProperties.mChildPerspective, aAppUnitsPerPixel);
4579 /* At the point when perspective is applied, we have been translated to the transform origin.
4580 * The translation to the perspective origin is the difference between these values.
4581 */
4582 result = result * nsLayoutUtils::ChangeMatrixBasis(aProperties.mToPerspectiveOrigin - aProperties.mToTransformOrigin, perspective);
4583 }
4585 gfxPoint3D rounded(hasSVGTransforms ? newOrigin.x : NS_round(newOrigin.x),
4586 hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y),
4587 0);
4589 if (frame && frame->Preserves3D()) {
4590 // Include the transform set on our parent
4591 NS_ASSERTION(frame->GetParent() &&
4592 frame->GetParent()->IsTransformed() &&
4593 frame->GetParent()->Preserves3DChildren(),
4594 "Preserve3D mismatch!");
4595 FrameTransformProperties props(frame->GetParent(),
4596 aAppUnitsPerPixel,
4597 nullptr);
4598 gfx3DMatrix parent =
4599 GetResultingTransformMatrixInternal(props,
4600 aOrigin - frame->GetPosition(),
4601 aAppUnitsPerPixel, nullptr, aOutAncestor);
4602 return nsLayoutUtils::ChangeMatrixBasis(rounded + aProperties.mToTransformOrigin, result) * parent;
4603 }
4605 return nsLayoutUtils::ChangeMatrixBasis
4606 (rounded + aProperties.mToTransformOrigin, result);
4607 }
4609 bool
4610 nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
4611 {
4612 if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_opacity)) {
4613 return true;
4614 }
4616 if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
4617 nsCString message;
4618 message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for opacity animation");
4619 CommonElementAnimationData::LogAsyncAnimationFailure(message,
4620 Frame()->GetContent());
4621 }
4622 return false;
4623 }
4625 bool
4626 nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
4627 {
4628 return ShouldPrerenderTransformedContent(aBuilder,
4629 Frame(),
4630 nsLayoutUtils::IsAnimationLoggingEnabled());
4631 }
4633 /* static */ bool
4634 nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
4635 nsIFrame* aFrame,
4636 bool aLogAnimations)
4637 {
4638 // Elements whose transform has been modified recently, or which
4639 // have a compositor-animated transform, can be prerendered. An element
4640 // might have only just had its transform animated in which case
4641 // the ActiveLayerManager may not have been notified yet.
4642 if (!ActiveLayerTracker::IsStyleAnimated(aFrame, eCSSProperty_transform) &&
4643 (!aFrame->GetContent() ||
4644 !nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(),
4645 eCSSProperty_transform))) {
4646 if (aLogAnimations) {
4647 nsCString message;
4648 message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for transform animation");
4649 CommonElementAnimationData::LogAsyncAnimationFailure(message,
4650 aFrame->GetContent());
4651 }
4652 return false;
4653 }
4655 nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
4656 // Only prerender if the transformed frame's size (in the reference
4657 // frames coordinate space) is <= the reference frame size (~viewport),
4658 // allowing a 1/8th fuzz factor for shadows, borders, etc.
4659 refSize += nsSize(refSize.width / 8, refSize.height / 8);
4660 nsRect frameRect = aFrame->GetVisualOverflowRectRelativeToSelf();
4662 frameRect =
4663 nsLayoutUtils::TransformFrameRectToAncestor(aFrame, frameRect,
4664 aBuilder->RootReferenceFrame());
4666 if (frameRect.Size() <= refSize) {
4667 return true;
4668 }
4670 if (aLogAnimations) {
4671 nsCString message;
4672 message.AppendLiteral("Performance warning: Async animation disabled because frame size (");
4673 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameRect.width));
4674 message.AppendLiteral(", ");
4675 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameRect.height));
4676 message.AppendLiteral(") is bigger than the viewport (");
4677 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.width));
4678 message.AppendLiteral(", ");
4679 message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.height));
4680 message.AppendLiteral(")");
4681 CommonElementAnimationData::LogAsyncAnimationFailure(message,
4682 aFrame->GetContent());
4683 }
4684 return false;
4685 }
4687 /* If the matrix is singular, or a hidden backface is shown, the frame won't be visible or hit. */
4688 static bool IsFrameVisible(nsIFrame* aFrame, const gfx3DMatrix& aMatrix)
4689 {
4690 if (aMatrix.IsSingular()) {
4691 return false;
4692 }
4693 if (aFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
4694 aMatrix.IsBackfaceVisible()) {
4695 return false;
4696 }
4697 return true;
4698 }
4700 const gfx3DMatrix&
4701 nsDisplayTransform::GetTransform()
4702 {
4703 if (mTransform.IsIdentity()) {
4704 float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
4705 gfxPoint3D newOrigin =
4706 gfxPoint3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
4707 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale),
4708 0.0f);
4709 if (mTransformGetter) {
4710 mTransform = mTransformGetter(mFrame, scale);
4711 mTransform = nsLayoutUtils::ChangeMatrixBasis(newOrigin, mTransform);
4712 } else {
4713 mTransform =
4714 GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale);
4716 /**
4717 * Shift the coorindates to be relative to our reference frame instead of relative to this frame.
4718 * When we have preserve-3d, our reference frame is already guaranteed to be an ancestor of the
4719 * preserve-3d chain, so we only need to do this once.
4720 */
4721 bool hasSVGTransforms = mFrame->IsSVGTransformed();
4722 gfxPoint3D rounded(hasSVGTransforms ? newOrigin.x : NS_round(newOrigin.x),
4723 hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y),
4724 0);
4725 mTransform.Translate(rounded);
4726 }
4727 }
4728 return mTransform;
4729 }
4731 bool
4732 nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
4733 {
4734 return ShouldPrerenderTransformedContent(aBuilder, mFrame, false);
4735 }
4737 already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder,
4738 LayerManager *aManager,
4739 const ContainerLayerParameters& aContainerParameters)
4740 {
4741 const gfx3DMatrix& newTransformMatrix = GetTransform();
4743 if (mFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
4744 newTransformMatrix.IsBackfaceVisible()) {
4745 return nullptr;
4746 }
4748 uint32_t flags = ShouldPrerenderTransformedContent(aBuilder, mFrame, false) ?
4749 FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0;
4750 nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
4751 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetChildren(),
4752 aContainerParameters, &newTransformMatrix, flags);
4754 if (!container) {
4755 return nullptr;
4756 }
4758 // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags,
4759 // so we never need to explicitely unset this flag.
4760 if (mFrame->Preserves3D() || mFrame->Preserves3DChildren()) {
4761 container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D);
4762 } else {
4763 container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_PRESERVE_3D);
4764 }
4766 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
4767 this, mFrame,
4768 eCSSProperty_transform);
4769 if (ShouldPrerenderTransformedContent(aBuilder, mFrame, false)) {
4770 container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
4771 /*the value is irrelevant*/nullptr);
4772 container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_MAY_CHANGE_TRANSFORM);
4773 } else {
4774 container->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
4775 container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_MAY_CHANGE_TRANSFORM);
4776 }
4777 return container.forget();
4778 }
4780 nsDisplayItem::LayerState
4781 nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder,
4782 LayerManager* aManager,
4783 const ContainerLayerParameters& aParameters) {
4784 // If the transform is 3d, or the layer takes part in preserve-3d sorting
4785 // then we *always* want this to be an active layer.
4786 if (!GetTransform().Is2D() || mFrame->Preserves3D()) {
4787 return LAYER_ACTIVE_FORCE;
4788 }
4789 // Here we check if the *post-transform* bounds of this item are big enough
4790 // to justify an active layer.
4791 if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_transform) &&
4792 !IsItemTooSmallForActiveLayer(this))
4793 return LAYER_ACTIVE;
4794 if (mFrame->GetContent()) {
4795 if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(),
4796 eCSSProperty_transform)) {
4797 return LAYER_ACTIVE;
4798 }
4799 }
4801 const nsStyleDisplay* disp = mFrame->StyleDisplay();
4802 if ((disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM)) {
4803 return LAYER_ACTIVE;
4804 }
4806 // Expect the child display items to have this frame as their animated
4807 // geometry root (since it will be their reference frame). If they have a
4808 // different animated geometry root, we'll make this an active layer so the
4809 // animation can be accelerated.
4810 return RequiredLayerStateForChildren(aBuilder, aManager, aParameters,
4811 *mStoredList.GetChildren(), Frame());
4812 }
4814 bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder,
4815 nsRegion *aVisibleRegion,
4816 const nsRect& aAllowVisibleRegionExpansion)
4817 {
4818 /* As we do this, we need to be sure to
4819 * untransform the visible rect, since we want everything that's painting to
4820 * think that it's painting in its original rectangular coordinate space.
4821 * If we can't untransform, take the entire overflow rect */
4822 nsRect untransformedVisibleRect;
4823 if (ShouldPrerenderTransformedContent(aBuilder, mFrame) ||
4824 !UntransformVisibleRect(aBuilder, &untransformedVisibleRect))
4825 {
4826 untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf();
4827 }
4828 nsRegion untransformedVisible = untransformedVisibleRect;
4829 // Call RecomputeVisiblity instead of ComputeVisibility since
4830 // nsDisplayItem::ComputeVisibility should only be called from
4831 // nsDisplayList::ComputeVisibility (which sets mVisibleRect on the item)
4832 mStoredList.RecomputeVisibility(aBuilder, &untransformedVisible);
4833 return true;
4834 }
4836 #ifdef DEBUG_HIT
4837 #include <time.h>
4838 #endif
4840 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
4841 void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder,
4842 const nsRect& aRect,
4843 HitTestState *aState,
4844 nsTArray<nsIFrame*> *aOutFrames)
4845 {
4846 /* Here's how this works:
4847 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
4848 * anything).
4849 * 2. Invert the matrix.
4850 * 3. Use it to transform the rect into the correct space.
4851 * 4. Pass that rect down through to the list's version of HitTest.
4852 */
4853 // GetTransform always operates in dev pixels.
4854 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
4855 gfx3DMatrix matrix = GetTransform();
4857 if (!IsFrameVisible(mFrame, matrix)) {
4858 return;
4859 }
4861 /* We want to go from transformed-space to regular space.
4862 * Thus we have to invert the matrix, which normally does
4863 * the reverse operation (e.g. regular->transformed)
4864 */
4865 bool snap;
4866 nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
4867 gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
4868 NSAppUnitsToFloatPixels(childBounds.y, factor),
4869 NSAppUnitsToFloatPixels(childBounds.width, factor),
4870 NSAppUnitsToFloatPixels(childBounds.height, factor));
4872 /* Now, apply the transform and pass it down the channel. */
4873 nsRect resultingRect;
4874 if (aRect.width == 1 && aRect.height == 1) {
4875 // Magic width/height indicating we're hit testing a point, not a rect
4876 gfxPoint point;
4877 if (!matrix.UntransformPoint(gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor),
4878 NSAppUnitsToFloatPixels(aRect.y, factor)),
4879 childGfxBounds,
4880 &point)) {
4881 return;
4882 }
4884 resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point.x), factor),
4885 NSFloatPixelsToAppUnits(float(point.y), factor),
4886 1, 1);
4888 } else {
4889 gfxRect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
4890 NSAppUnitsToFloatPixels(aRect.y, factor),
4891 NSAppUnitsToFloatPixels(aRect.width, factor),
4892 NSAppUnitsToFloatPixels(aRect.height, factor));
4894 gfxRect rect = matrix.UntransformBounds(originalRect, childGfxBounds);
4896 resultingRect = nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
4897 NSFloatPixelsToAppUnits(float(rect.Y()), factor),
4898 NSFloatPixelsToAppUnits(float(rect.Width()), factor),
4899 NSFloatPixelsToAppUnits(float(rect.Height()), factor));
4900 }
4902 if (resultingRect.IsEmpty()) {
4903 return;
4904 }
4907 #ifdef DEBUG_HIT
4908 printf("Frame: %p\n", dynamic_cast<void *>(mFrame));
4909 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(), resultingRect.Y());
4910 uint32_t originalFrameCount = aOutFrames.Length();
4911 #endif
4913 mStoredList.HitTest(aBuilder, resultingRect, aState, aOutFrames);
4915 #ifdef DEBUG_HIT
4916 if (originalFrameCount != aOutFrames.Length())
4917 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()),
4918 dynamic_cast<void *>(aOutFrames.ElementAt(0)));
4919 printf("=== end of hit test ===\n");
4920 #endif
4922 }
4924 float
4925 nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint)
4926 {
4927 // GetTransform always operates in dev pixels.
4928 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
4929 gfx3DMatrix matrix = GetTransform();
4931 NS_ASSERTION(IsFrameVisible(mFrame, matrix), "We can't have hit a frame that isn't visible!");
4933 bool snap;
4934 nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
4935 gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
4936 NSAppUnitsToFloatPixels(childBounds.y, factor),
4937 NSAppUnitsToFloatPixels(childBounds.width, factor),
4938 NSAppUnitsToFloatPixels(childBounds.height, factor));
4940 gfxPoint point;
4941 DebugOnly<bool> result = matrix.UntransformPoint(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, factor),
4942 NSAppUnitsToFloatPixels(aPoint.y, factor)),
4943 childGfxBounds,
4944 &point);
4945 NS_ASSERTION(result, "Why are we trying to get the depth for a point we didn't hit?");
4947 gfxPoint3D transformed = matrix.Transform3D(gfxPoint3D(point.x, point.y, 0));
4948 return transformed.z;
4949 }
4951 /* The bounding rectangle for the object is the overflow rectangle translated
4952 * by the reference point.
4953 */
4954 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder, bool* aSnap)
4955 {
4956 nsRect untransformedBounds =
4957 ShouldPrerenderTransformedContent(aBuilder, mFrame) ?
4958 mFrame->GetVisualOverflowRectRelativeToSelf() :
4959 mStoredList.GetBounds(aBuilder, aSnap);
4960 *aSnap = false;
4961 // GetTransform always operates in dev pixels.
4962 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
4963 return nsLayoutUtils::MatrixTransformRect(untransformedBounds,
4964 GetTransform(),
4965 factor);
4966 }
4968 /* The transform is opaque iff the transform consists solely of scales and
4969 * translations and if the underlying content is opaque. Thus if the transform
4970 * is of the form
4971 *
4972 * |a c e|
4973 * |b d f|
4974 * |0 0 1|
4975 *
4976 * We need b and c to be zero.
4977 *
4978 * We also need to check whether the underlying opaque content completely fills
4979 * our visible rect. We use UntransformRect which expands to the axis-aligned
4980 * bounding rect, but that's OK since if
4981 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it
4982 * certainly contains the actual (non-axis-aligned) untransformed rect.
4983 */
4984 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder,
4985 bool* aSnap)
4986 {
4987 *aSnap = false;
4988 nsRect untransformedVisible;
4989 // If we're going to prerender all our content, pretend like we
4990 // don't have opqaue content so that everything under us is rendered
4991 // as well. That will increase graphics memory usage if our frame
4992 // covers the entire window, but it allows our transform to be
4993 // updated extremely cheaply, without invalidating any other
4994 // content.
4995 if (ShouldPrerenderTransformedContent(aBuilder, mFrame) ||
4996 !UntransformVisibleRect(aBuilder, &untransformedVisible)) {
4997 return nsRegion();
4998 }
5000 const gfx3DMatrix& matrix = GetTransform();
5002 nsRegion result;
5003 gfxMatrix matrix2d;
5004 bool tmpSnap;
5005 if (matrix.Is2D(&matrix2d) &&
5006 matrix2d.PreservesAxisAlignedRectangles() &&
5007 mStoredList.GetOpaqueRegion(aBuilder, &tmpSnap).Contains(untransformedVisible)) {
5008 result = mVisibleRect;
5009 }
5010 return result;
5011 }
5013 /* The transform is uniform if it fills the entire bounding rect and the
5014 * wrapped list is uniform. See GetOpaqueRegion for discussion of why this
5015 * works.
5016 */
5017 bool nsDisplayTransform::IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor)
5018 {
5019 nsRect untransformedVisible;
5020 if (!UntransformVisibleRect(aBuilder, &untransformedVisible)) {
5021 return false;
5022 }
5023 const gfx3DMatrix& matrix = GetTransform();
5025 gfxMatrix matrix2d;
5026 return matrix.Is2D(&matrix2d) &&
5027 matrix2d.PreservesAxisAlignedRectangles() &&
5028 mStoredList.GetVisibleRect().Contains(untransformedVisible) &&
5029 mStoredList.IsUniform(aBuilder, aColor);
5030 }
5032 /* If UNIFIED_CONTINUATIONS is defined, we can merge two display lists that
5033 * share the same underlying content. Otherwise, doing so results in graphical
5034 * glitches.
5035 */
5036 #ifndef UNIFIED_CONTINUATIONS
5038 bool
5039 nsDisplayTransform::TryMerge(nsDisplayListBuilder *aBuilder,
5040 nsDisplayItem *aItem)
5041 {
5042 return false;
5043 }
5045 #else
5047 bool
5048 nsDisplayTransform::TryMerge(nsDisplayListBuilder *aBuilder,
5049 nsDisplayItem *aItem)
5050 {
5051 NS_PRECONDITION(aItem, "Why did you try merging with a null item?");
5052 NS_PRECONDITION(aBuilder, "Why did you try merging with a null builder?");
5054 /* Make sure that we're dealing with two transforms. */
5055 if (aItem->GetType() != TYPE_TRANSFORM)
5056 return false;
5058 /* Check to see that both frames are part of the same content. */
5059 if (aItem->Frame()->GetContent() != mFrame->GetContent())
5060 return false;
5062 if (aItem->GetClip() != GetClip())
5063 return false;
5065 /* Now, move everything over to this frame and signal that
5066 * we merged things!
5067 */
5068 mStoredList.MergeFrom(&static_cast<nsDisplayTransform*>(aItem)->mStoredList);
5069 return true;
5070 }
5072 #endif
5074 /* TransformRect takes in as parameters a rectangle (in app space) and returns
5075 * the smallest rectangle (in app space) containing the transformed image of
5076 * that rectangle. That is, it takes the four corners of the rectangle,
5077 * transforms them according to the matrix associated with the specified frame,
5078 * then returns the smallest rectangle containing the four transformed points.
5079 *
5080 * @param aUntransformedBounds The rectangle (in app units) to transform.
5081 * @param aFrame The frame whose transformation should be applied.
5082 * @param aOrigin The delta from the frame origin to the coordinate space origin
5083 * @param aBoundsOverride (optional) Force the frame bounds to be the
5084 * specified bounds.
5085 * @return The smallest rectangle containing the image of the transformed
5086 * rectangle.
5087 */
5088 nsRect nsDisplayTransform::TransformRect(const nsRect &aUntransformedBounds,
5089 const nsIFrame* aFrame,
5090 const nsPoint &aOrigin,
5091 const nsRect* aBoundsOverride)
5092 {
5093 NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
5095 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
5096 return nsLayoutUtils::MatrixTransformRect
5097 (aUntransformedBounds,
5098 GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride),
5099 factor);
5100 }
5102 nsRect nsDisplayTransform::TransformRectOut(const nsRect &aUntransformedBounds,
5103 const nsIFrame* aFrame,
5104 const nsPoint &aOrigin,
5105 const nsRect* aBoundsOverride)
5106 {
5107 NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
5109 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
5110 return nsLayoutUtils::MatrixTransformRectOut
5111 (aUntransformedBounds,
5112 GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride),
5113 factor);
5114 }
5116 bool nsDisplayTransform::UntransformRect(const nsRect &aTransformedBounds,
5117 const nsRect &aChildBounds,
5118 const nsIFrame* aFrame,
5119 const nsPoint &aOrigin,
5120 nsRect *aOutRect)
5121 {
5122 NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
5124 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
5126 gfx3DMatrix transform = GetResultingTransformMatrix(aFrame, aOrigin, factor, nullptr);
5127 if (transform.IsSingular()) {
5128 return false;
5129 }
5131 gfxRect result(NSAppUnitsToFloatPixels(aTransformedBounds.x, factor),
5132 NSAppUnitsToFloatPixels(aTransformedBounds.y, factor),
5133 NSAppUnitsToFloatPixels(aTransformedBounds.width, factor),
5134 NSAppUnitsToFloatPixels(aTransformedBounds.height, factor));
5136 gfxRect childGfxBounds(NSAppUnitsToFloatPixels(aChildBounds.x, factor),
5137 NSAppUnitsToFloatPixels(aChildBounds.y, factor),
5138 NSAppUnitsToFloatPixels(aChildBounds.width, factor),
5139 NSAppUnitsToFloatPixels(aChildBounds.height, factor));
5141 result = transform.UntransformBounds(result, childGfxBounds);
5142 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor);
5143 return true;
5144 }
5146 bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
5147 nsRect *aOutRect)
5148 {
5149 const gfx3DMatrix& matrix = GetTransform();
5150 if (matrix.IsSingular())
5151 return false;
5153 // GetTransform always operates in dev pixels.
5154 float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
5155 gfxRect result(NSAppUnitsToFloatPixels(mVisibleRect.x, factor),
5156 NSAppUnitsToFloatPixels(mVisibleRect.y, factor),
5157 NSAppUnitsToFloatPixels(mVisibleRect.width, factor),
5158 NSAppUnitsToFloatPixels(mVisibleRect.height, factor));
5160 bool snap;
5161 nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap);
5162 gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
5163 NSAppUnitsToFloatPixels(childBounds.y, factor),
5164 NSAppUnitsToFloatPixels(childBounds.width, factor),
5165 NSAppUnitsToFloatPixels(childBounds.height, factor));
5167 /* We want to untransform the matrix, so invert the transformation first! */
5168 result = matrix.UntransformBounds(result, childGfxBounds);
5170 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor);
5172 return true;
5173 }
5175 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
5176 nsIFrame* aFrame, nsDisplayList* aList)
5177 : nsDisplayWrapList(aBuilder, aFrame, aList),
5178 mEffectsBounds(aFrame->GetVisualOverflowRectRelativeToSelf())
5179 {
5180 MOZ_COUNT_CTOR(nsDisplaySVGEffects);
5181 }
5183 #ifdef NS_BUILD_REFCNT_LOGGING
5184 nsDisplaySVGEffects::~nsDisplaySVGEffects()
5185 {
5186 MOZ_COUNT_DTOR(nsDisplaySVGEffects);
5187 }
5188 #endif
5190 nsRegion nsDisplaySVGEffects::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
5191 bool* aSnap)
5192 {
5193 *aSnap = false;
5194 return nsRegion();
5195 }
5197 void
5198 nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
5199 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
5200 {
5201 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
5202 if (nsSVGIntegrationUtils::HitTestFrameForEffects(mFrame,
5203 rectCenter - ToReferenceFrame())) {
5204 mList.HitTest(aBuilder, aRect, aState, aOutFrames);
5205 }
5206 }
5208 void
5209 nsDisplaySVGEffects::PaintAsLayer(nsDisplayListBuilder* aBuilder,
5210 nsRenderingContext* aCtx,
5211 LayerManager* aManager)
5212 {
5213 nsSVGIntegrationUtils::PaintFramesWithEffects(aCtx, mFrame,
5214 mVisibleRect,
5215 aBuilder, aManager);
5216 }
5218 LayerState
5219 nsDisplaySVGEffects::GetLayerState(nsDisplayListBuilder* aBuilder,
5220 LayerManager* aManager,
5221 const ContainerLayerParameters& aParameters)
5222 {
5223 return LAYER_SVG_EFFECTS;
5224 }
5226 already_AddRefed<Layer>
5227 nsDisplaySVGEffects::BuildLayer(nsDisplayListBuilder* aBuilder,
5228 LayerManager* aManager,
5229 const ContainerLayerParameters& aContainerParameters)
5230 {
5231 const nsIContent* content = mFrame->GetContent();
5232 bool hasSVGLayout = (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
5233 if (hasSVGLayout) {
5234 nsISVGChildFrame *svgChildFrame = do_QueryFrame(mFrame);
5235 if (!svgChildFrame || !mFrame->GetContent()->IsSVG()) {
5236 NS_ASSERTION(false, "why?");
5237 return nullptr;
5238 }
5239 if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
5240 return nullptr; // The SVG spec says not to draw filters for this
5241 }
5242 }
5244 float opacity = mFrame->StyleDisplay()->mOpacity;
5245 if (opacity == 0.0f)
5246 return nullptr;
5248 nsIFrame* firstFrame =
5249 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
5250 nsSVGEffects::EffectProperties effectProperties =
5251 nsSVGEffects::GetEffectProperties(firstFrame);
5253 bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
5254 effectProperties.GetClipPathFrame(&isOK);
5255 effectProperties.GetMaskFrame(&isOK);
5257 if (!isOK) {
5258 return nullptr;
5259 }
5261 ContainerLayerParameters newContainerParameters = aContainerParameters;
5262 if (effectProperties.HasValidFilter()) {
5263 newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
5264 }
5266 nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
5267 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
5268 newContainerParameters, nullptr);
5270 return container.forget();
5271 }
5273 bool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder,
5274 nsRegion* aVisibleRegion,
5275 const nsRect& aAllowVisibleRegionExpansion) {
5276 nsPoint offset = ToReferenceFrame();
5277 nsRect dirtyRect =
5278 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mFrame,
5279 mVisibleRect - offset) +
5280 offset;
5282 // Our children may be made translucent or arbitrarily deformed so we should
5283 // not allow them to subtract area from aVisibleRegion.
5284 nsRegion childrenVisible(dirtyRect);
5285 nsRect r = dirtyRect.Intersect(mList.GetBounds(aBuilder));
5286 mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r, nsRect());
5287 return true;
5288 }
5290 bool nsDisplaySVGEffects::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem)
5291 {
5292 if (aItem->GetType() != TYPE_SVG_EFFECTS)
5293 return false;
5294 // items for the same content element should be merged into a single
5295 // compositing group
5296 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects
5297 if (aItem->Frame()->GetContent() != mFrame->GetContent())
5298 return false;
5299 if (aItem->GetClip() != GetClip())
5300 return false;
5301 nsDisplaySVGEffects* other = static_cast<nsDisplaySVGEffects*>(aItem);
5302 MergeFromTrackingMergedFrames(other);
5303 mEffectsBounds.UnionRect(mEffectsBounds,
5304 other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame));
5305 return true;
5306 }
5308 #ifdef MOZ_DUMP_PAINTING
5309 void
5310 nsDisplaySVGEffects::PrintEffects(nsACString& aTo)
5311 {
5312 nsIFrame* firstFrame =
5313 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
5314 nsSVGEffects::EffectProperties effectProperties =
5315 nsSVGEffects::GetEffectProperties(firstFrame);
5316 bool isOK = true;
5317 nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
5318 bool first = true;
5319 aTo += " effects=(";
5320 if (mFrame->StyleDisplay()->mOpacity != 1.0f) {
5321 first = false;
5322 aTo += nsPrintfCString("opacity(%f)", mFrame->StyleDisplay()->mOpacity);
5323 }
5324 if (clipPathFrame) {
5325 if (!first) {
5326 aTo += ", ";
5327 }
5328 aTo += nsPrintfCString("clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
5329 first = false;
5330 }
5331 if (effectProperties.HasValidFilter()) {
5332 if (!first) {
5333 aTo += ", ";
5334 }
5335 aTo += "filter";
5336 first = false;
5337 }
5338 if (effectProperties.GetMaskFrame(&isOK)) {
5339 if (!first) {
5340 aTo += ", ";
5341 }
5342 aTo += "mask";
5343 }
5344 aTo += ")";
5345 }
5346 #endif