|
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 */ |
|
7 |
|
8 /* |
|
9 * structures that represent things to be painted (ordered in z-order), |
|
10 * used during painting and hit testing |
|
11 */ |
|
12 |
|
13 #include "mozilla/dom/TabChild.h" |
|
14 #include "mozilla/layers/PLayerTransaction.h" |
|
15 |
|
16 #include "nsDisplayList.h" |
|
17 |
|
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" |
|
32 |
|
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" |
|
56 |
|
57 #include <stdint.h> |
|
58 #include <algorithm> |
|
59 |
|
60 using namespace mozilla; |
|
61 using namespace mozilla::css; |
|
62 using namespace mozilla::layers; |
|
63 using namespace mozilla::dom; |
|
64 typedef FrameMetrics::ViewID ViewID; |
|
65 |
|
66 static inline nsIFrame* |
|
67 GetTransformRootFrame(nsIFrame* aFrame) |
|
68 { |
|
69 return nsLayoutUtils::GetTransformRootFrame(aFrame); |
|
70 } |
|
71 |
|
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 } |
|
82 |
|
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); |
|
207 |
|
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 } |
|
302 |
|
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 } |
|
311 |
|
312 uint32_t type = aCTF.GetType() == nsTimingFunction::StepStart ? 1 : 2; |
|
313 return TimingFunction(StepFunction(aCTF.GetSteps(), type)); |
|
314 } |
|
315 |
|
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(); |
|
327 |
|
328 mozilla::layers::Animation* animation = |
|
329 aPending ? |
|
330 aLayer->AddAnimationForNextTransaction() : |
|
331 aLayer->AddAnimation(); |
|
332 |
|
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; |
|
340 |
|
341 for (uint32_t propIdx = 0; propIdx < ea->mProperties.Length(); propIdx++) { |
|
342 AnimationProperty* property = &ea->mProperties[propIdx]; |
|
343 |
|
344 if (aProperty != property->mProperty) { |
|
345 continue; |
|
346 } |
|
347 |
|
348 for (uint32_t segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) { |
|
349 AnimationPropertySegment* segment = &property->mSegments[segIdx]; |
|
350 |
|
351 AnimationSegment* animSegment = animation->segments().AppendElement(); |
|
352 if (aProperty == eCSSProperty_transform) { |
|
353 animSegment->startState() = InfallibleTArray<TransformFunction>(); |
|
354 animSegment->endState() = InfallibleTArray<TransformFunction>(); |
|
355 |
|
356 nsCSSValueSharedList* list = segment->mFromValue.GetCSSValueSharedListValue(); |
|
357 AddTransformFunctions(list->mHead, styleContext, presContext, bounds, scale, |
|
358 animSegment->startState().get_ArrayOfTransformFunction()); |
|
359 |
|
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 } |
|
367 |
|
368 animSegment->startPortion() = segment->mFromKey; |
|
369 animSegment->endPortion() = segment->mToKey; |
|
370 animSegment->sampleFn() = ToTimingFunction(segment->mTimingFunction); |
|
371 } |
|
372 } |
|
373 } |
|
374 |
|
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 } |
|
393 |
|
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"); |
|
409 |
|
410 bool pending = !aBuilder; |
|
411 |
|
412 if (pending) { |
|
413 aLayer->ClearAnimationsForNextTransaction(); |
|
414 } else { |
|
415 aLayer->ClearAnimations(); |
|
416 } |
|
417 |
|
418 nsIContent* content = aFrame->GetContent(); |
|
419 if (!content) { |
|
420 return; |
|
421 } |
|
422 ElementTransitions* et = |
|
423 nsTransitionManager::GetTransitionsForCompositor(content, aProperty); |
|
424 |
|
425 ElementAnimations* ea = |
|
426 nsAnimationManager::GetAnimationsForCompositor(content, aProperty); |
|
427 |
|
428 if (!ea && !et) { |
|
429 return; |
|
430 } |
|
431 |
|
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))); |
|
441 |
|
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 } |
|
447 |
|
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 } |
|
475 |
|
476 data = TransformData(origin, offsetToTransformOrigin, |
|
477 offsetToPerspectiveOrigin, bounds, perspective, |
|
478 aFrame->PresContext()->AppUnitsPerDevPixel()); |
|
479 } else if (aProperty == eCSSProperty_opacity) { |
|
480 data = null_t(); |
|
481 } |
|
482 |
|
483 if (et) { |
|
484 AddAnimationsForProperty(aFrame, aProperty, et->mPropertyTransitions, |
|
485 aLayer, data, pending); |
|
486 aLayer->SetAnimationGeneration(et->mAnimationGeneration); |
|
487 } |
|
488 |
|
489 if (ea) { |
|
490 AddAnimationsForProperty(aFrame, aProperty, ea->mAnimations, |
|
491 aLayer, data, pending); |
|
492 aLayer->SetAnimationGeneration(ea->mAnimationGeneration); |
|
493 } |
|
494 } |
|
495 |
|
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); |
|
532 |
|
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 } |
|
542 |
|
543 nsCSSRendering::BeginFrameTreesLocked(); |
|
544 PR_STATIC_ASSERT(nsDisplayItem::TYPE_MAX < (1 << nsDisplayItem::TYPE_BITS)); |
|
545 } |
|
546 |
|
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 } |
|
559 |
|
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 } |
|
578 |
|
579 nsRect dirty = dirtyRectRelativeToDirtyFrame - aFrame->GetOffsetTo(aDirtyFrame); |
|
580 nsRect overflowRect = aFrame->GetVisualOverflowRect(); |
|
581 |
|
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 } |
|
592 |
|
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); |
|
599 |
|
600 MarkFrameForDisplay(aFrame, aDirtyFrame); |
|
601 } |
|
602 |
|
603 static void UnmarkFrameForDisplay(nsIFrame* aFrame) { |
|
604 nsPresContext* presContext = aFrame->PresContext(); |
|
605 presContext->PropertyTable()-> |
|
606 Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty()); |
|
607 |
|
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 } |
|
615 |
|
616 static bool gPrintApzcTree = false; |
|
617 |
|
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 } |
|
626 |
|
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); |
|
641 |
|
642 nsIntRect visible = aVisibleRect.ScaleToNearestPixels( |
|
643 resolution.scale, resolution.scale, auPerDevPixel); |
|
644 aRoot->SetVisibleRegion(visible); |
|
645 |
|
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 } |
|
654 |
|
655 nsIScrollableFrame* scrollableFrame = nullptr; |
|
656 if (aScrollFrame) |
|
657 scrollableFrame = aScrollFrame->GetScrollTargetFrame(); |
|
658 |
|
659 metrics.mScrollableRect = CSSRect::FromAppUnits( |
|
660 nsLayoutUtils::CalculateScrollableRectForFrame(scrollableFrame, aForFrame)); |
|
661 |
|
662 if (scrollableFrame) { |
|
663 nsPoint scrollPosition = scrollableFrame->GetScrollPosition(); |
|
664 metrics.SetScrollOffset(CSSPoint::FromAppUnits(scrollPosition)); |
|
665 |
|
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 } |
|
674 |
|
675 metrics.SetScrollId(aScrollId); |
|
676 metrics.mIsRoot = aIsRoot; |
|
677 |
|
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 } |
|
687 |
|
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 } |
|
699 |
|
700 metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale( |
|
701 (float)nsPresContext::AppUnitsPerCSSPixel() / auPerDevPixel); |
|
702 |
|
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); |
|
708 |
|
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 } |
|
719 |
|
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); |
|
724 |
|
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); |
|
734 |
|
735 |
|
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); |
|
752 |
|
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 } |
|
783 |
|
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 } |
|
791 |
|
792 metrics.SetRootCompositionSize( |
|
793 nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame ? aScrollFrame : aForFrame, |
|
794 isRootContentDocRootScrollFrame, metrics)); |
|
795 |
|
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 } |
|
803 |
|
804 metrics.mPresShellId = presShell->GetPresShellId(); |
|
805 |
|
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 } |
|
812 |
|
813 aRoot->SetFrameMetrics(metrics); |
|
814 } |
|
815 |
|
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"); |
|
822 |
|
823 nsCSSRendering::EndFrameTreesLocked(); |
|
824 |
|
825 for (uint32_t i = 0; i < mDisplayItemClipsToDestroy.Length(); ++i) { |
|
826 mDisplayItemClipsToDestroy[i]->DisplayItemClip::~DisplayItemClip(); |
|
827 } |
|
828 |
|
829 PL_FinishArenaPool(&mPool); |
|
830 MOZ_COUNT_DTOR(nsDisplayListBuilder); |
|
831 } |
|
832 |
|
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 } |
|
844 |
|
845 void |
|
846 nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion, |
|
847 const nsRegion& aRegion) |
|
848 { |
|
849 if (aRegion.IsEmpty()) |
|
850 return; |
|
851 |
|
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 } |
|
863 |
|
864 nsCaret * |
|
865 nsDisplayListBuilder::GetCaret() { |
|
866 nsRefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret(); |
|
867 return caret; |
|
868 } |
|
869 |
|
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(); |
|
879 |
|
880 state->mPresShell->UpdateCanvasBackground(); |
|
881 |
|
882 if (mIsPaintingToWindow) { |
|
883 mReferenceFrame->AddPaintedPresShell(state->mPresShell); |
|
884 |
|
885 state->mPresShell->IncrementPaintCount(); |
|
886 } |
|
887 |
|
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 } |
|
898 |
|
899 if (!buildCaret) |
|
900 return; |
|
901 |
|
902 nsRefPtr<nsCaret> caret = state->mPresShell->GetCaret(); |
|
903 state->mCaretFrame = caret->GetCaretFrame(); |
|
904 NS_ASSERTION(state->mCaretFrame == caret->GetCaretFrame(), |
|
905 "GetCaretFrame() is unstable"); |
|
906 |
|
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 } |
|
918 |
|
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 } |
|
927 |
|
928 ResetMarkedFramesForDisplayList(); |
|
929 mPresShellStates.SetLength(mPresShellStates.Length() - 1); |
|
930 } |
|
931 |
|
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 } |
|
943 |
|
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 } |
|
954 |
|
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); |
|
968 |
|
969 child->Properties().Set(nsDisplayListBuilder::Preserve3DDirtyRectProperty(), |
|
970 new nsRect(dirty)); |
|
971 |
|
972 MarkFrameForDisplay(child, aDirtyFrame); |
|
973 } |
|
974 } |
|
975 } |
|
976 } |
|
977 |
|
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 } |
|
987 |
|
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 } |
|
996 |
|
997 DisplayItemClip* c = new (p) DisplayItemClip(aOriginal); |
|
998 mDisplayItemClipsToDestroy.AppendElement(c); |
|
999 return c; |
|
1000 } |
|
1001 |
|
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 } |
|
1011 |
|
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 } |
|
1024 |
|
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 } |
|
1033 |
|
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 } |
|
1045 |
|
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 } |
|
1077 |
|
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 } |
|
1102 |
|
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 |
|
1115 |
|
1116 mVisibleRect = aListVisibleBounds; |
|
1117 bool anyVisible = false; |
|
1118 |
|
1119 nsAutoTArray<nsDisplayItem*, 512> elements; |
|
1120 FlattenTo(&elements); |
|
1121 |
|
1122 bool forceTransparentSurface = false; |
|
1123 |
|
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]; |
|
1127 |
|
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 } |
|
1135 |
|
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 } |
|
1146 |
|
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 } |
|
1156 |
|
1157 nsRect bounds = item->GetClippedBounds(aBuilder); |
|
1158 |
|
1159 nsRegion itemVisible; |
|
1160 itemVisible.And(*aVisibleRegion, bounds); |
|
1161 item->mVisibleRect = itemVisible.GetBounds(); |
|
1162 |
|
1163 if (item->ComputeVisibility(aBuilder, aVisibleRegion, |
|
1164 aAllowVisibleRegionExpansion.Intersect(bounds))) { |
|
1165 anyVisible = true; |
|
1166 |
|
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 } |
|
1180 |
|
1181 if (occlude) { |
|
1182 nsRegion opaque = TreatAsOpaque(item, aBuilder); |
|
1183 // Subtract opaque item from the visible region |
|
1184 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque); |
|
1185 } |
|
1186 |
|
1187 if (aBuilder->NeedToForceTransparentSurfaceForItem(item) || |
|
1188 (list && list->NeedsTransparentSurface())) { |
|
1189 forceTransparentSurface = true; |
|
1190 } |
|
1191 } |
|
1192 AppendToBottom(item); |
|
1193 } |
|
1194 |
|
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 } |
|
1202 |
|
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 } |
|
1209 |
|
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"); |
|
1221 |
|
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 } |
|
1248 |
|
1249 // Store the existing layer builder to reinstate it on return. |
|
1250 FrameLayerBuilder *oldBuilder = layerManager->GetLayerBuilder(); |
|
1251 |
|
1252 FrameLayerBuilder *layerBuilder = new FrameLayerBuilder(); |
|
1253 layerBuilder->Init(aBuilder, layerManager); |
|
1254 |
|
1255 if (aFlags & PAINT_COMPRESSED) { |
|
1256 layerBuilder->SetLayerTreeCompressionMode(); |
|
1257 } |
|
1258 |
|
1259 if (aFlags & PAINT_FLUSH_LAYERS) { |
|
1260 FrameLayerBuilder::InvalidateAllLayers(layerManager); |
|
1261 } |
|
1262 |
|
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 } |
|
1273 |
|
1274 nsPresContext* presContext = aForFrame->PresContext(); |
|
1275 nsIPresShell* presShell = presContext->GetPresShell(); |
|
1276 |
|
1277 NotifySubDocInvalidationFunc computeInvalidFunc = |
|
1278 presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0; |
|
1279 bool computeInvalidRect = (computeInvalidFunc || |
|
1280 (!layerManager->IsCompositingCheap() && layerManager->NeedsWidgetInvalidation())) && |
|
1281 widgetTransaction; |
|
1282 |
|
1283 nsAutoPtr<LayerProperties> props(computeInvalidRect ? |
|
1284 LayerProperties::CloneFrom(layerManager->GetRoot()) : |
|
1285 nullptr); |
|
1286 |
|
1287 ContainerLayerParameters containerParameters |
|
1288 (presShell->GetXResolution(), presShell->GetYResolution()); |
|
1289 nsRefPtr<ContainerLayer> root = layerBuilder-> |
|
1290 BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nullptr, *this, |
|
1291 containerParameters, nullptr); |
|
1292 |
|
1293 nsIDocument* document = nullptr; |
|
1294 if (presShell) { |
|
1295 document = presShell->GetDocument(); |
|
1296 } |
|
1297 |
|
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 } |
|
1305 |
|
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); |
|
1313 |
|
1314 ViewID id = FrameMetrics::NULL_SCROLL_ID; |
|
1315 bool isRoot = presContext->IsRootContentDocument(); |
|
1316 |
|
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); |
|
1327 |
|
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 } |
|
1334 |
|
1335 nsRect viewport(aBuilder->ToReferenceFrame(aForFrame), aForFrame->GetSize()); |
|
1336 |
|
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 } |
|
1348 |
|
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 } |
|
1364 |
|
1365 layerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, |
|
1366 aBuilder, flags); |
|
1367 aBuilder->SetIsCompositingCheap(temp); |
|
1368 layerBuilder->DidEndTransaction(); |
|
1369 |
|
1370 nsIntRegion invalid; |
|
1371 if (props) { |
|
1372 invalid = props->ComputeDifferences(root, computeInvalidFunc); |
|
1373 } else if (widgetTransaction) { |
|
1374 LayerProperties::ClearInvalidations(root); |
|
1375 } |
|
1376 |
|
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 } |
|
1395 |
|
1396 if (aFlags & PAINT_FLUSH_LAYERS) { |
|
1397 FrameLayerBuilder::InvalidateAllLayers(layerManager); |
|
1398 } |
|
1399 |
|
1400 layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder); |
|
1401 } |
|
1402 |
|
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 } |
|
1410 |
|
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 } |
|
1423 |
|
1424 void nsDisplayList::DeleteAll() { |
|
1425 nsDisplayItem* item; |
|
1426 while ((item = RemoveBottom()) != nullptr) { |
|
1427 item->~nsDisplayItem(); |
|
1428 } |
|
1429 } |
|
1430 |
|
1431 static bool |
|
1432 GetMouseThrough(const nsIFrame* aFrame) |
|
1433 { |
|
1434 if (!aFrame->IsBoxFrame()) |
|
1435 return false; |
|
1436 |
|
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 } |
|
1448 |
|
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 } |
|
1459 |
|
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 {} |
|
1467 |
|
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 } |
|
1478 |
|
1479 float mDepth; |
|
1480 nsTArray<nsIFrame*> mFrames; |
|
1481 }; |
|
1482 |
|
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 } |
|
1496 |
|
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); |
|
1511 |
|
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); |
|
1517 |
|
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 } |
|
1538 |
|
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 } |
|
1553 |
|
1554 static void Sort(nsDisplayList* aList, int32_t aCount, nsDisplayList::SortLEQ aCmp, |
|
1555 void* aClosure) { |
|
1556 if (aCount < 2) |
|
1557 return; |
|
1558 |
|
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 } |
|
1578 |
|
1579 Sort(&list1, half, aCmp, aClosure); |
|
1580 Sort(&list2, aCount - half, aCmp, aClosure); |
|
1581 |
|
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 } |
|
1592 |
|
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 } |
|
1604 |
|
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 } |
|
1622 |
|
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 } |
|
1629 |
|
1630 void nsDisplayList::SortByZOrder(nsDisplayListBuilder* aBuilder, |
|
1631 nsIContent* aCommonAncestor) { |
|
1632 Sort(aBuilder, IsZOrderLEQ, aCommonAncestor); |
|
1633 } |
|
1634 |
|
1635 void nsDisplayList::SortByContentOrder(nsDisplayListBuilder* aBuilder, |
|
1636 nsIContent* aCommonAncestor) { |
|
1637 Sort(aBuilder, IsContentLEQ, aCommonAncestor); |
|
1638 } |
|
1639 |
|
1640 void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder, |
|
1641 SortLEQ aCmp, void* aClosure) { |
|
1642 ::Sort(this, Count(), aCmp, aClosure); |
|
1643 } |
|
1644 |
|
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 } |
|
1658 |
|
1659 /* static */ bool |
|
1660 nsDisplayItem::ForceActiveLayers() |
|
1661 { |
|
1662 static bool sForce = false; |
|
1663 static bool sForceCached = false; |
|
1664 |
|
1665 if (!sForceCached) { |
|
1666 Preferences::AddBoolVarCache(&sForce, "layers.force-active", false); |
|
1667 sForceCached = true; |
|
1668 } |
|
1669 |
|
1670 return sForce; |
|
1671 } |
|
1672 |
|
1673 /* static */ int32_t |
|
1674 nsDisplayItem::MaxActiveLayers() |
|
1675 { |
|
1676 static int32_t sMaxLayers = false; |
|
1677 static bool sMaxLayersCached = false; |
|
1678 |
|
1679 if (!sMaxLayersCached) { |
|
1680 Preferences::AddIntVarCache(&sMaxLayers, "layers.max-active", -1); |
|
1681 sMaxLayersCached = true; |
|
1682 } |
|
1683 |
|
1684 return sMaxLayers; |
|
1685 } |
|
1686 |
|
1687 int32_t |
|
1688 nsDisplayItem::ZIndex() const |
|
1689 { |
|
1690 if (!mFrame->IsPositioned() && !mFrame->IsFlexItem()) |
|
1691 return 0; |
|
1692 |
|
1693 const nsStylePosition* position = mFrame->StylePosition(); |
|
1694 if (position->mZIndex.GetUnit() == eStyleUnit_Integer) |
|
1695 return position->mZIndex.GetIntValue(); |
|
1696 |
|
1697 // sort the auto and 0 elements together |
|
1698 return 0; |
|
1699 } |
|
1700 |
|
1701 bool |
|
1702 nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder, |
|
1703 nsRegion* aVisibleRegion) { |
|
1704 nsRect bounds = GetClippedBounds(aBuilder); |
|
1705 |
|
1706 nsRegion itemVisible; |
|
1707 itemVisible.And(*aVisibleRegion, bounds); |
|
1708 mVisibleRect = itemVisible.GetBounds(); |
|
1709 |
|
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; |
|
1715 |
|
1716 nsRegion opaque = TreatAsOpaque(this, aBuilder); |
|
1717 aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque); |
|
1718 return true; |
|
1719 } |
|
1720 |
|
1721 nsRect |
|
1722 nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) |
|
1723 { |
|
1724 bool snap; |
|
1725 nsRect r = GetBounds(aBuilder, &snap); |
|
1726 return GetClip().ApplyNonRoundedIntersection(r); |
|
1727 } |
|
1728 |
|
1729 nsRect |
|
1730 nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) |
|
1731 { |
|
1732 *aSnap = true; |
|
1733 return mBounds; |
|
1734 } |
|
1735 |
|
1736 void |
|
1737 nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder, |
|
1738 nsRenderingContext* aCtx) |
|
1739 { |
|
1740 aCtx->SetColor(mColor); |
|
1741 aCtx->FillRect(mVisibleRect); |
|
1742 } |
|
1743 |
|
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 |
|
1753 |
|
1754 static void |
|
1755 RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) |
|
1756 { |
|
1757 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame); |
|
1758 |
|
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 } |
|
1767 |
|
1768 nsRect borderBox(aFrame->GetOffsetTo(displayRoot), aFrame->GetSize()); |
|
1769 aBuilder->RegisterThemeGeometry(aFrame->StyleDisplay()->mAppearance, |
|
1770 borderBox.ToNearestPixels(aFrame->PresContext()->AppUnitsPerDevPixel())); |
|
1771 } |
|
1772 |
|
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); |
|
1782 |
|
1783 mBounds = GetBoundsInternal(aBuilder); |
|
1784 } |
|
1785 |
|
1786 nsDisplayBackgroundImage::~nsDisplayBackgroundImage() |
|
1787 { |
|
1788 #ifdef NS_BUILD_REFCNT_LOGGING |
|
1789 MOZ_COUNT_DTOR(nsDisplayBackgroundImage); |
|
1790 #endif |
|
1791 } |
|
1792 |
|
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 } |
|
1805 |
|
1806 nsIContent* content = aFrame->GetContent(); |
|
1807 if (!content || content->GetParent()) { |
|
1808 return nullptr; |
|
1809 } |
|
1810 |
|
1811 sc = aFrame->StyleContext(); |
|
1812 } |
|
1813 return sc; |
|
1814 } |
|
1815 |
|
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 } |
|
1831 |
|
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 } |
|
1840 |
|
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 } |
|
1853 |
|
1854 if (isThemed) { |
|
1855 nsDisplayThemedBackground* bgItem = |
|
1856 new (aBuilder) nsDisplayThemedBackground(aBuilder, aFrame); |
|
1857 bgItemList.AppendNewToTop(bgItem); |
|
1858 aList->AppendToTop(&bgItemList); |
|
1859 return true; |
|
1860 } |
|
1861 |
|
1862 if (!bg) { |
|
1863 aList->AppendToTop(&bgItemList); |
|
1864 return false; |
|
1865 } |
|
1866 |
|
1867 bool needBlendContainer = false; |
|
1868 |
|
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 } |
|
1875 |
|
1876 if (bg->mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) { |
|
1877 needBlendContainer = true; |
|
1878 } |
|
1879 |
|
1880 nsDisplayBackgroundImage* bgItem = |
|
1881 new (aBuilder) nsDisplayBackgroundImage(aBuilder, aFrame, i, bg); |
|
1882 bgItemList.AppendNewToTop(bgItem); |
|
1883 } |
|
1884 |
|
1885 if (needBlendContainer) { |
|
1886 bgItemList.AppendNewToTop( |
|
1887 new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, &bgItemList)); |
|
1888 } |
|
1889 |
|
1890 aList->AppendToTop(&bgItemList); |
|
1891 return false; |
|
1892 } |
|
1893 |
|
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; |
|
1904 |
|
1905 nscoord radii[8]; |
|
1906 return !aFrame->GetBorderRadii(radii) || |
|
1907 nsLayoutUtils::RoundedRectIntersectsRect(nsRect(aFrameToReferenceFrame, |
|
1908 aFrame->GetSize()), |
|
1909 radii, aTestRect); |
|
1910 } |
|
1911 |
|
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 } |
|
1924 |
|
1925 bool |
|
1926 nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder, |
|
1927 const nsRect& aClipRect, |
|
1928 gfxRect* aDestRect) |
|
1929 { |
|
1930 if (!mBackgroundStyle) |
|
1931 return false; |
|
1932 |
|
1933 if (mBackgroundStyle->mLayers.Length() != 1) |
|
1934 return false; |
|
1935 |
|
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]; |
|
1940 |
|
1941 if (layer.mAttachment != NS_STYLE_BG_ATTACHMENT_FIXED) |
|
1942 return false; |
|
1943 |
|
1944 nsBackgroundLayerState state = |
|
1945 nsCSSRendering::PrepareBackgroundLayer(presContext, |
|
1946 mFrame, |
|
1947 flags, |
|
1948 borderArea, |
|
1949 aClipRect, |
|
1950 *mBackgroundStyle, |
|
1951 layer); |
|
1952 |
|
1953 nsImageRenderer* imageRenderer = &state.mImageRenderer; |
|
1954 // We only care about images here, not gradients. |
|
1955 if (!imageRenderer->IsRasterImage()) |
|
1956 return false; |
|
1957 |
|
1958 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); |
|
1959 *aDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel); |
|
1960 |
|
1961 return true; |
|
1962 } |
|
1963 |
|
1964 bool |
|
1965 nsDisplayBackgroundImage::TryOptimizeToImageLayer(LayerManager* aManager, |
|
1966 nsDisplayListBuilder* aBuilder) |
|
1967 { |
|
1968 if (!mBackgroundStyle) |
|
1969 return false; |
|
1970 |
|
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]; |
|
1975 |
|
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 } |
|
1983 |
|
1984 nsBackgroundLayerState state = |
|
1985 nsCSSRendering::PrepareBackgroundLayer(presContext, |
|
1986 mFrame, |
|
1987 flags, |
|
1988 borderArea, |
|
1989 borderArea, |
|
1990 *mBackgroundStyle, |
|
1991 layer); |
|
1992 |
|
1993 nsImageRenderer* imageRenderer = &state.mImageRenderer; |
|
1994 // We only care about images here, not gradients. |
|
1995 if (!imageRenderer->IsRasterImage()) |
|
1996 return false; |
|
1997 |
|
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; |
|
2002 |
|
2003 // We currently can't handle tiled or partial backgrounds. |
|
2004 if (!state.mDestArea.IsEqualEdges(state.mFillArea)) { |
|
2005 return false; |
|
2006 } |
|
2007 |
|
2008 // XXX Ignoring state.mAnchor. ImageLayer drawing snaps mDestArea edges to |
|
2009 // layer pixel boundaries. This should be OK for now. |
|
2010 |
|
2011 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); |
|
2012 mDestRect = nsLayoutUtils::RectToGfxRect(state.mDestArea, appUnitsPerDevPixel); |
|
2013 mImageContainer = imageContainer; |
|
2014 |
|
2015 // Ok, we can turn this into a layer if needed. |
|
2016 return true; |
|
2017 } |
|
2018 |
|
2019 already_AddRefed<ImageContainer> |
|
2020 nsDisplayBackgroundImage::GetContainer(LayerManager* aManager, |
|
2021 nsDisplayListBuilder *aBuilder) |
|
2022 { |
|
2023 if (!TryOptimizeToImageLayer(aManager, aBuilder)) { |
|
2024 return nullptr; |
|
2025 } |
|
2026 |
|
2027 nsRefPtr<ImageContainer> container = mImageContainer; |
|
2028 |
|
2029 return container.forget(); |
|
2030 } |
|
2031 |
|
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 } |
|
2051 |
|
2052 if (!animated || |
|
2053 !nsLayoutUtils::AnimatedImageLayersEnabled()) { |
|
2054 if (!aManager->IsCompositingCheap() || |
|
2055 !nsLayoutUtils::GPUImageScalingEnabled()) { |
|
2056 return LAYER_NONE; |
|
2057 } |
|
2058 } |
|
2059 |
|
2060 if (!TryOptimizeToImageLayer(aManager, aBuilder)) { |
|
2061 return LAYER_NONE; |
|
2062 } |
|
2063 |
|
2064 if (!animated) { |
|
2065 mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize(); |
|
2066 NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!"); |
|
2067 |
|
2068 gfxRect destRect = mDestRect; |
|
2069 |
|
2070 destRect.width *= aParameters.mXScale; |
|
2071 destRect.height *= aParameters.mYScale; |
|
2072 |
|
2073 // Calculate the scaling factor for the frame. |
|
2074 gfxSize scale = gfxSize(destRect.width / imageSize.width, destRect.height / imageSize.height); |
|
2075 |
|
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 } |
|
2080 |
|
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 } |
|
2086 |
|
2087 return LAYER_ACTIVE; |
|
2088 } |
|
2089 |
|
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 } |
|
2106 |
|
2107 void |
|
2108 nsDisplayBackgroundImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset) |
|
2109 { |
|
2110 aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame)); |
|
2111 |
|
2112 mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize(); |
|
2113 NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!"); |
|
2114 |
|
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 } |
|
2123 |
|
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 } |
|
2134 |
|
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 } |
|
2144 |
|
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 } |
|
2150 |
|
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; |
|
2160 |
|
2161 nsIFrame *frame = aItem->Frame(); |
|
2162 |
|
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 } |
|
2183 |
|
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 } |
|
2192 |
|
2193 nsRegion |
|
2194 nsDisplayBackgroundImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
|
2195 bool* aSnap) { |
|
2196 nsRegion result; |
|
2197 *aSnap = false; |
|
2198 |
|
2199 if (!mBackgroundStyle) |
|
2200 return result; |
|
2201 |
|
2202 |
|
2203 *aSnap = true; |
|
2204 |
|
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 } |
|
2218 |
|
2219 return result; |
|
2220 } |
|
2221 |
|
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 } |
|
2230 |
|
2231 bool |
|
2232 nsDisplayBackgroundImage::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, |
|
2233 nsIFrame* aFrame) |
|
2234 { |
|
2235 if (!mBackgroundStyle) |
|
2236 return false; |
|
2237 if (!mBackgroundStyle->HasFixedBackground()) |
|
2238 return false; |
|
2239 |
|
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 } |
|
2247 |
|
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 } |
|
2261 |
|
2262 bool |
|
2263 nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange() |
|
2264 { |
|
2265 if (!mBackgroundStyle) |
|
2266 return false; |
|
2267 |
|
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 } |
|
2274 |
|
2275 const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer]; |
|
2276 if (layer.RenderingMightDependOnPositioningAreaSizeChange()) { |
|
2277 return true; |
|
2278 } |
|
2279 return false; |
|
2280 } |
|
2281 |
|
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 } |
|
2294 |
|
2295 void |
|
2296 nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder, |
|
2297 nsRenderingContext* aCtx) { |
|
2298 PaintInternal(aBuilder, aCtx, mVisibleRect, nullptr); |
|
2299 } |
|
2300 |
|
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); |
|
2308 |
|
2309 nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame, |
|
2310 aBounds, |
|
2311 nsRect(offset, mFrame->GetSize()), |
|
2312 flags, aClipRect, mLayer); |
|
2313 |
|
2314 } |
|
2315 |
|
2316 void nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
|
2317 const nsDisplayItemGeometry* aGeometry, |
|
2318 nsRegion* aInvalidRegion) |
|
2319 { |
|
2320 if (!mBackgroundStyle) { |
|
2321 return; |
|
2322 } |
|
2323 |
|
2324 const nsDisplayBackgroundGeometry* geometry = static_cast<const nsDisplayBackgroundGeometry*>(aGeometry); |
|
2325 |
|
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); |
|
2335 |
|
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); |
|
2345 |
|
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); |
|
2353 |
|
2354 NotifyRenderingChanged(); |
|
2355 } |
|
2356 } |
|
2357 |
|
2358 nsRect |
|
2359 nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
|
2360 *aSnap = true; |
|
2361 return mBounds; |
|
2362 } |
|
2363 |
|
2364 nsRect |
|
2365 nsDisplayBackgroundImage::GetBoundsInternal(nsDisplayListBuilder* aBuilder) { |
|
2366 nsPresContext* presContext = mFrame->PresContext(); |
|
2367 |
|
2368 if (!mBackgroundStyle) { |
|
2369 return nsRect(); |
|
2370 } |
|
2371 |
|
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 } |
|
2384 |
|
2385 uint32_t |
|
2386 nsDisplayBackgroundImage::GetPerFrameKey() |
|
2387 { |
|
2388 return (mLayer << nsDisplayItem::TYPE_BITS) | |
|
2389 nsDisplayItem::GetPerFrameKey(); |
|
2390 } |
|
2391 |
|
2392 nsDisplayThemedBackground::nsDisplayThemedBackground(nsDisplayListBuilder* aBuilder, |
|
2393 nsIFrame* aFrame) |
|
2394 : nsDisplayItem(aBuilder, aFrame) |
|
2395 { |
|
2396 MOZ_COUNT_CTOR(nsDisplayThemedBackground); |
|
2397 |
|
2398 const nsStyleDisplay* disp = mFrame->StyleDisplay(); |
|
2399 mAppearance = disp->mAppearance; |
|
2400 mFrame->IsThemed(disp, &mThemeTransparency); |
|
2401 |
|
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 } |
|
2417 |
|
2418 mBounds = GetBoundsInternal(); |
|
2419 } |
|
2420 |
|
2421 nsDisplayThemedBackground::~nsDisplayThemedBackground() |
|
2422 { |
|
2423 #ifdef NS_BUILD_REFCNT_LOGGING |
|
2424 MOZ_COUNT_DTOR(nsDisplayThemedBackground); |
|
2425 #endif |
|
2426 } |
|
2427 |
|
2428 #ifdef MOZ_DUMP_PAINTING |
|
2429 void |
|
2430 nsDisplayThemedBackground::WriteDebugInfo(nsACString& aTo) |
|
2431 { |
|
2432 aTo += nsPrintfCString(" (themed, appearance:%d)", mAppearance); |
|
2433 } |
|
2434 #endif |
|
2435 |
|
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 } |
|
2447 |
|
2448 nsRegion |
|
2449 nsDisplayThemedBackground::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
|
2450 bool* aSnap) { |
|
2451 nsRegion result; |
|
2452 *aSnap = false; |
|
2453 |
|
2454 if (mThemeTransparency == nsITheme::eOpaque) { |
|
2455 result = nsRect(ToReferenceFrame(), mFrame->GetSize()); |
|
2456 } |
|
2457 return result; |
|
2458 } |
|
2459 |
|
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 } |
|
2469 |
|
2470 nsRect |
|
2471 nsDisplayThemedBackground::GetPositioningArea() |
|
2472 { |
|
2473 return nsRect(ToReferenceFrame(), mFrame->GetSize()); |
|
2474 } |
|
2475 |
|
2476 void |
|
2477 nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder, |
|
2478 nsRenderingContext* aCtx) |
|
2479 { |
|
2480 PaintInternal(aBuilder, aCtx, mVisibleRect, nullptr); |
|
2481 } |
|
2482 |
|
2483 |
|
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 } |
|
2499 |
|
2500 bool nsDisplayThemedBackground::IsWindowActive() |
|
2501 { |
|
2502 EventStates docState = mFrame->GetContent()->OwnerDoc()->GetDocumentState(); |
|
2503 return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE); |
|
2504 } |
|
2505 |
|
2506 void nsDisplayThemedBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
|
2507 const nsDisplayItemGeometry* aGeometry, |
|
2508 nsRegion* aInvalidRegion) |
|
2509 { |
|
2510 const nsDisplayThemedBackgroundGeometry* geometry = static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry); |
|
2511 |
|
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 } |
|
2531 |
|
2532 nsRect |
|
2533 nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
|
2534 *aSnap = true; |
|
2535 return mBounds; |
|
2536 } |
|
2537 |
|
2538 nsRect |
|
2539 nsDisplayThemedBackground::GetBoundsInternal() { |
|
2540 nsPresContext* presContext = mFrame->PresContext(); |
|
2541 |
|
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 |
|
2550 |
|
2551 return r + ToReferenceFrame(); |
|
2552 } |
|
2553 |
|
2554 void |
|
2555 nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder, |
|
2556 nsRenderingContext* aCtx) |
|
2557 { |
|
2558 if (mColor == NS_RGBA(0, 0, 0, 0)) { |
|
2559 return; |
|
2560 } |
|
2561 |
|
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 } |
|
2570 |
|
2571 nsRegion |
|
2572 nsDisplayBackgroundColor::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
|
2573 bool* aSnap) |
|
2574 { |
|
2575 if (NS_GET_A(mColor) != 255) { |
|
2576 return nsRegion(); |
|
2577 } |
|
2578 |
|
2579 if (!mBackgroundStyle) |
|
2580 return nsRegion(); |
|
2581 |
|
2582 *aSnap = true; |
|
2583 |
|
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 } |
|
2589 |
|
2590 bool |
|
2591 nsDisplayBackgroundColor::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) |
|
2592 { |
|
2593 *aColor = mColor; |
|
2594 |
|
2595 if (!mBackgroundStyle) |
|
2596 return true; |
|
2597 |
|
2598 return (!nsLayoutUtils::HasNonZeroCorner(mFrame->StyleBorder()->mBorderRadius) && |
|
2599 mBackgroundStyle->BottomLayer().mClip == NS_STYLE_BG_CLIP_BORDER); |
|
2600 } |
|
2601 |
|
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 } |
|
2612 |
|
2613 aOutFrames->AppendElement(mFrame); |
|
2614 } |
|
2615 |
|
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 |
|
2625 |
|
2626 nsRect |
|
2627 nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
|
2628 *aSnap = false; |
|
2629 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); |
|
2630 } |
|
2631 |
|
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 } |
|
2642 |
|
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 } |
|
2651 |
|
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 } |
|
2662 |
|
2663 return true; |
|
2664 } |
|
2665 |
|
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 } |
|
2676 |
|
2677 aOutFrames->AppendElement(mFrame); |
|
2678 } |
|
2679 |
|
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 } |
|
2713 |
|
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 } |
|
2721 |
|
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 } |
|
2730 |
|
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 } |
|
2745 |
|
2746 return true; |
|
2747 } |
|
2748 |
|
2749 nsDisplayItemGeometry* |
|
2750 nsDisplayBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder) |
|
2751 { |
|
2752 return new nsDisplayBorderGeometry(this, aBuilder); |
|
2753 } |
|
2754 |
|
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 } |
|
2770 |
|
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 } |
|
2781 |
|
2782 nsRect |
|
2783 nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) |
|
2784 { |
|
2785 *aSnap = true; |
|
2786 return CalculateBounds(*mFrame->StyleBorder()); |
|
2787 } |
|
2788 |
|
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 } |
|
2811 |
|
2812 return result; |
|
2813 } |
|
2814 } |
|
2815 |
|
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 } |
|
2834 |
|
2835 if (!accumulated.IsEmpty()) { |
|
2836 aRects->AppendElement(accumulated); |
|
2837 accumulated.SetEmpty(); |
|
2838 } |
|
2839 |
|
2840 if (!r) |
|
2841 break; |
|
2842 |
|
2843 accumulated = *r; |
|
2844 } |
|
2845 } |
|
2846 |
|
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); |
|
2855 |
|
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 } |
|
2865 |
|
2866 nsRect |
|
2867 nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
|
2868 *aSnap = false; |
|
2869 return mBounds; |
|
2870 } |
|
2871 |
|
2872 nsRect |
|
2873 nsDisplayBoxShadowOuter::GetBoundsInternal() { |
|
2874 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) + |
|
2875 ToReferenceFrame(); |
|
2876 } |
|
2877 |
|
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 } |
|
2886 |
|
2887 // Store the actual visible region |
|
2888 mVisibleRegion.And(*aVisibleRegion, mVisibleRect); |
|
2889 |
|
2890 nsPoint origin = ToReferenceFrame(); |
|
2891 nsRect visibleBounds = aVisibleRegion->GetBounds(); |
|
2892 nsRect frameRect(origin, mFrame->GetSize()); |
|
2893 if (!frameRect.Contains(visibleBounds)) |
|
2894 return true; |
|
2895 |
|
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; |
|
2902 |
|
2903 return !RoundedRectContainsRect(frameRect, twipsRadii, visibleBounds); |
|
2904 } |
|
2905 |
|
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 } |
|
2931 |
|
2932 |
|
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); |
|
2941 |
|
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 } |
|
2951 |
|
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 } |
|
2960 |
|
2961 // Store the actual visible region |
|
2962 mVisibleRegion.And(*aVisibleRegion, mVisibleRect); |
|
2963 return true; |
|
2964 } |
|
2965 |
|
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); |
|
2973 |
|
2974 if (!aFrame || !aFrame->IsTransformed()) { |
|
2975 return; |
|
2976 } |
|
2977 |
|
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 } |
|
2988 |
|
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 } |
|
3009 |
|
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); |
|
3017 |
|
3018 if (!aFrame || !aFrame->IsTransformed()) { |
|
3019 return; |
|
3020 } |
|
3021 |
|
3022 if (aFrame->Preserves3DChildren()) { |
|
3023 mReferenceFrame = |
|
3024 aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame)); |
|
3025 mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame); |
|
3026 return; |
|
3027 } |
|
3028 |
|
3029 // See the previous nsDisplayWrapList constructor |
|
3030 if (aItem->Frame() == aFrame) { |
|
3031 mReferenceFrame = aItem->ReferenceFrame(); |
|
3032 mToReferenceFrame = aItem->ToReferenceFrame(); |
|
3033 } |
|
3034 } |
|
3035 |
|
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 } |
|
3046 |
|
3047 nsDisplayWrapList::~nsDisplayWrapList() { |
|
3048 mList.DeleteAll(); |
|
3049 } |
|
3050 |
|
3051 void |
|
3052 nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, |
|
3053 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) { |
|
3054 mList.HitTest(aBuilder, aRect, aState, aOutFrames); |
|
3055 } |
|
3056 |
|
3057 nsRect |
|
3058 nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
|
3059 *aSnap = false; |
|
3060 return mBounds; |
|
3061 } |
|
3062 |
|
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; |
|
3072 |
|
3073 bool retval = |
|
3074 mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion, |
|
3075 mVisibleRect, |
|
3076 aAllowVisibleRegionExpansion); |
|
3077 |
|
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); |
|
3084 |
|
3085 return retval; |
|
3086 } |
|
3087 |
|
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 } |
|
3099 |
|
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 } |
|
3104 |
|
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 } |
|
3111 |
|
3112 void nsDisplayWrapList::Paint(nsDisplayListBuilder* aBuilder, |
|
3113 nsRenderingContext* aCtx) { |
|
3114 NS_ERROR("nsDisplayWrapList should have been flattened away for painting"); |
|
3115 } |
|
3116 |
|
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 } |
|
3136 |
|
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 } |
|
3159 |
|
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 } |
|
3168 |
|
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 } |
|
3181 |
|
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 } |
|
3197 |
|
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); |
|
3203 |
|
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 } |
|
3214 |
|
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 } |
|
3239 |
|
3240 nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder, |
|
3241 nsIFrame* aFrame, nsDisplayList* aList) |
|
3242 : nsDisplayWrapList(aBuilder, aFrame, aList) { |
|
3243 MOZ_COUNT_CTOR(nsDisplayOpacity); |
|
3244 } |
|
3245 |
|
3246 #ifdef NS_BUILD_REFCNT_LOGGING |
|
3247 nsDisplayOpacity::~nsDisplayOpacity() { |
|
3248 MOZ_COUNT_DTOR(nsDisplayOpacity); |
|
3249 } |
|
3250 #endif |
|
3251 |
|
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 } |
|
3259 |
|
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; |
|
3274 |
|
3275 container->SetOpacity(mFrame->StyleDisplay()->mOpacity); |
|
3276 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder, |
|
3277 this, mFrame, |
|
3278 eCSSProperty_opacity); |
|
3279 return container.forget(); |
|
3280 } |
|
3281 |
|
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 } |
|
3296 |
|
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 } |
|
3311 |
|
3312 bool |
|
3313 nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) |
|
3314 { |
|
3315 if (NeedsActiveLayer()) |
|
3316 return false; |
|
3317 |
|
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 } |
|
3325 |
|
3326 return child->ApplyOpacity(aBuilder, mFrame->StyleDisplay()->mOpacity, mClip); |
|
3327 } |
|
3328 |
|
3329 nsDisplayItem::LayerState |
|
3330 nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder, |
|
3331 LayerManager* aManager, |
|
3332 const ContainerLayerParameters& aParameters) { |
|
3333 if (NeedsActiveLayer()) |
|
3334 return LAYER_ACTIVE; |
|
3335 |
|
3336 return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, |
|
3337 nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder)); |
|
3338 } |
|
3339 |
|
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 } |
|
3357 |
|
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 } |
|
3371 |
|
3372 #ifdef MOZ_DUMP_PAINTING |
|
3373 void |
|
3374 nsDisplayOpacity::WriteDebugInfo(nsACString& aTo) |
|
3375 { |
|
3376 aTo += nsPrintfCString(" (opacity %f)", mFrame->StyleDisplay()->mOpacity); |
|
3377 } |
|
3378 #endif |
|
3379 |
|
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 } |
|
3386 |
|
3387 #ifdef NS_BUILD_REFCNT_LOGGING |
|
3388 nsDisplayMixBlendMode::~nsDisplayMixBlendMode() { |
|
3389 MOZ_COUNT_DTOR(nsDisplayMixBlendMode); |
|
3390 } |
|
3391 #endif |
|
3392 |
|
3393 nsRegion nsDisplayMixBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
|
3394 bool* aSnap) { |
|
3395 *aSnap = false; |
|
3396 // We are never considered opaque |
|
3397 return nsRegion(); |
|
3398 } |
|
3399 |
|
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; |
|
3407 |
|
3408 nsRefPtr<Layer> container = aManager->GetLayerBuilder()-> |
|
3409 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, |
|
3410 newContainerParameters, nullptr); |
|
3411 if (!container) { |
|
3412 return nullptr; |
|
3413 } |
|
3414 |
|
3415 container->DeprecatedSetMixBlendMode(nsCSSRendering::GetGFXBlendMode(mFrame->StyleDisplay()->mMixBlendMode)); |
|
3416 |
|
3417 return container.forget(); |
|
3418 } |
|
3419 |
|
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 } |
|
3436 |
|
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 } |
|
3450 |
|
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 } |
|
3457 |
|
3458 #ifdef NS_BUILD_REFCNT_LOGGING |
|
3459 nsDisplayBlendContainer::~nsDisplayBlendContainer() { |
|
3460 MOZ_COUNT_DTOR(nsDisplayBlendContainer); |
|
3461 } |
|
3462 #endif |
|
3463 |
|
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; |
|
3473 |
|
3474 nsRefPtr<Layer> container = aManager->GetLayerBuilder()-> |
|
3475 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, |
|
3476 newContainerParameters, nullptr); |
|
3477 if (!container) { |
|
3478 return nullptr; |
|
3479 } |
|
3480 |
|
3481 container->SetForceIsolatedGroup(true); |
|
3482 return container.forget(); |
|
3483 } |
|
3484 |
|
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 } |
|
3498 |
|
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 } |
|
3507 |
|
3508 #ifdef NS_BUILD_REFCNT_LOGGING |
|
3509 nsDisplayOwnLayer::~nsDisplayOwnLayer() { |
|
3510 MOZ_COUNT_DTOR(nsDisplayOwnLayer); |
|
3511 } |
|
3512 #endif |
|
3513 |
|
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 } |
|
3528 |
|
3529 if (mFlags & GENERATE_SUBDOC_INVALIDATIONS) { |
|
3530 mFrame->PresContext()->SetNotifySubDocInvalidationData(layer); |
|
3531 } |
|
3532 return layer.forget(); |
|
3533 } |
|
3534 |
|
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 } |
|
3543 |
|
3544 #ifdef NS_BUILD_REFCNT_LOGGING |
|
3545 nsDisplaySubDocument::~nsDisplaySubDocument() { |
|
3546 MOZ_COUNT_DTOR(nsDisplaySubDocument); |
|
3547 } |
|
3548 #endif |
|
3549 |
|
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); |
|
3556 |
|
3557 if (!(mFlags & GENERATE_SCROLLABLE_LAYER)) { |
|
3558 return layer.forget(); |
|
3559 } |
|
3560 |
|
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(); |
|
3566 |
|
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); |
|
3577 |
|
3578 if (isRootContentDocument) { |
|
3579 scrollId = nsLayoutUtils::FindOrCreateIDFor(content); |
|
3580 } else { |
|
3581 nsLayoutUtils::FindIDFor(content, &scrollId); |
|
3582 } |
|
3583 } |
|
3584 } |
|
3585 |
|
3586 nsRect viewport = mFrame->GetRect() - |
|
3587 mFrame->GetPosition() + |
|
3588 mFrame->GetOffsetToCrossDoc(ReferenceFrame()); |
|
3589 |
|
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 } |
|
3597 |
|
3598 return layer.forget(); |
|
3599 } |
|
3600 |
|
3601 nsRect |
|
3602 nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) |
|
3603 { |
|
3604 bool usingDisplayPort = |
|
3605 nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); |
|
3606 |
|
3607 if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) { |
|
3608 *aSnap = false; |
|
3609 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame); |
|
3610 } |
|
3611 |
|
3612 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap); |
|
3613 } |
|
3614 |
|
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); |
|
3623 |
|
3624 if (!(mFlags & GENERATE_SCROLLABLE_LAYER) || !usingDisplayPort) { |
|
3625 return nsDisplayWrapList::ComputeVisibility(aBuilder, aVisibleRegion, |
|
3626 aAllowVisibleRegionExpansion); |
|
3627 } |
|
3628 |
|
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()); |
|
3634 |
|
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. |
|
3644 |
|
3645 return visible; |
|
3646 } |
|
3647 |
|
3648 bool |
|
3649 nsDisplaySubDocument::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) |
|
3650 { |
|
3651 bool usingDisplayPort = |
|
3652 nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); |
|
3653 |
|
3654 if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) { |
|
3655 return true; |
|
3656 } |
|
3657 |
|
3658 return nsDisplayOwnLayer::ShouldBuildLayerEvenIfInvisible(aBuilder); |
|
3659 } |
|
3660 |
|
3661 nsRegion |
|
3662 nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) |
|
3663 { |
|
3664 bool usingDisplayPort = |
|
3665 nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); |
|
3666 |
|
3667 if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) { |
|
3668 *aSnap = false; |
|
3669 return nsRegion(); |
|
3670 } |
|
3671 |
|
3672 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap); |
|
3673 } |
|
3674 |
|
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 } |
|
3681 |
|
3682 #ifdef NS_BUILD_REFCNT_LOGGING |
|
3683 nsDisplayResolution::~nsDisplayResolution() { |
|
3684 MOZ_COUNT_DTOR(nsDisplayResolution); |
|
3685 } |
|
3686 #endif |
|
3687 |
|
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); |
|
3696 |
|
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 } |
|
3703 |
|
3704 nsDisplayStickyPosition::nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, |
|
3705 nsIFrame* aFrame, |
|
3706 nsDisplayList* aList) |
|
3707 : nsDisplayOwnLayer(aBuilder, aFrame, aList) |
|
3708 { |
|
3709 MOZ_COUNT_CTOR(nsDisplayStickyPosition); |
|
3710 } |
|
3711 |
|
3712 #ifdef NS_BUILD_REFCNT_LOGGING |
|
3713 nsDisplayStickyPosition::~nsDisplayStickyPosition() { |
|
3714 MOZ_COUNT_DTOR(nsDisplayStickyPosition); |
|
3715 } |
|
3716 #endif |
|
3717 |
|
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); |
|
3724 |
|
3725 StickyScrollContainer* stickyScrollContainer = StickyScrollContainer:: |
|
3726 GetStickyScrollContainerForFrame(mFrame); |
|
3727 if (!stickyScrollContainer) { |
|
3728 return layer.forget(); |
|
3729 } |
|
3730 |
|
3731 nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame()); |
|
3732 nsPresContext* presContext = scrollFrame->PresContext(); |
|
3733 |
|
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 } |
|
3742 |
|
3743 nsLayoutUtils::SetFixedPositionLayerData(layer, scrollFrame, |
|
3744 nsRect(scrollFrame->GetOffsetToCrossDoc(ReferenceFrame()), scrollFrameSize), |
|
3745 mFrame, presContext, aContainerParameters); |
|
3746 |
|
3747 ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor( |
|
3748 stickyScrollContainer->ScrollFrame()->GetScrolledFrame()->GetContent()); |
|
3749 |
|
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); |
|
3771 |
|
3772 return layer.forget(); |
|
3773 } |
|
3774 |
|
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 } |
|
3787 |
|
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 |
|
3801 |
|
3802 NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(), |
|
3803 "Need a child frame with content"); |
|
3804 } |
|
3805 |
|
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 |
|
3819 |
|
3820 NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(), |
|
3821 "Need a child frame with content"); |
|
3822 } |
|
3823 |
|
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 |
|
3836 |
|
3837 NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(), |
|
3838 "Need a child frame with content"); |
|
3839 } |
|
3840 |
|
3841 #ifdef NS_BUILD_REFCNT_LOGGING |
|
3842 nsDisplayScrollLayer::~nsDisplayScrollLayer() |
|
3843 { |
|
3844 MOZ_COUNT_DTOR(nsDisplayScrollLayer); |
|
3845 } |
|
3846 #endif |
|
3847 |
|
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 } |
|
3858 |
|
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); |
|
3866 |
|
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); |
|
3871 |
|
3872 nsRect viewport = mScrollFrame->GetRect() - |
|
3873 mScrollFrame->GetPosition() + |
|
3874 mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame()); |
|
3875 |
|
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); |
|
3890 |
|
3891 return layer.forget(); |
|
3892 } |
|
3893 |
|
3894 bool |
|
3895 nsDisplayScrollLayer::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) |
|
3896 { |
|
3897 if (nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), nullptr)) { |
|
3898 return true; |
|
3899 } |
|
3900 |
|
3901 return nsDisplayWrapList::ShouldBuildLayerEvenIfInvisible(aBuilder); |
|
3902 } |
|
3903 |
|
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 } |
|
3922 |
|
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. |
|
3932 |
|
3933 return visible; |
|
3934 } |
|
3935 |
|
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 } |
|
3945 |
|
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 } |
|
3969 |
|
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 } |
|
3984 |
|
3985 if (WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, this) || |
|
3986 WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, other)) { |
|
3987 return false; |
|
3988 } |
|
3989 |
|
3990 NS_ASSERTION(other->mReferenceFrame == mReferenceFrame, |
|
3991 "Must have the same reference frame!"); |
|
3992 |
|
3993 FrameProperties props = mScrolledFrame->Properties(); |
|
3994 props.Set(nsIFrame::ScrollLayerCount(), |
|
3995 reinterpret_cast<void*>(GetScrollLayerCount() - 1)); |
|
3996 |
|
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 } |
|
4008 |
|
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 } |
|
4023 |
|
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 } |
|
4045 |
|
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 } |
|
4064 |
|
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 |
|
4073 |
|
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 } |
|
4084 |
|
4085 nsDisplayScrollInfoLayer::~nsDisplayScrollInfoLayer() |
|
4086 { |
|
4087 FrameProperties props = mScrolledFrame->Properties(); |
|
4088 props.Remove(nsIFrame::ScrollLayerCount()); |
|
4089 MOZ_COUNT_DTOR(nsDisplayScrollInfoLayer); |
|
4090 } |
|
4091 |
|
4092 nsRect |
|
4093 nsDisplayScrollInfoLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) |
|
4094 { |
|
4095 return nsDisplayWrapList::GetBounds(aBuilder, aSnap); |
|
4096 } |
|
4097 |
|
4098 LayerState |
|
4099 nsDisplayScrollInfoLayer::GetLayerState(nsDisplayListBuilder* aBuilder, |
|
4100 LayerManager* aManager, |
|
4101 const ContainerLayerParameters& aParameters) |
|
4102 { |
|
4103 return LAYER_ACTIVE_EMPTY; |
|
4104 } |
|
4105 |
|
4106 bool |
|
4107 nsDisplayScrollInfoLayer::TryMerge(nsDisplayListBuilder* aBuilder, |
|
4108 nsDisplayItem* aItem) |
|
4109 { |
|
4110 return false; |
|
4111 } |
|
4112 |
|
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 } |
|
4122 |
|
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 } |
|
4131 |
|
4132 #ifdef NS_BUILD_REFCNT_LOGGING |
|
4133 nsDisplayZoom::~nsDisplayZoom() { |
|
4134 MOZ_COUNT_DTOR(nsDisplayZoom); |
|
4135 } |
|
4136 #endif |
|
4137 |
|
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 } |
|
4144 |
|
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 } |
|
4161 |
|
4162 void nsDisplayZoom::Paint(nsDisplayListBuilder* aBuilder, |
|
4163 nsRenderingContext* aCtx) |
|
4164 { |
|
4165 mList.PaintForFrame(aBuilder, aCtx, mFrame, nsDisplayList::PAINT_DEFAULT); |
|
4166 } |
|
4167 |
|
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; |
|
4178 |
|
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 } |
|
4199 |
|
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); |
|
4208 |
|
4209 return retval; |
|
4210 } |
|
4211 |
|
4212 /////////////////////////////////////////////////// |
|
4213 // nsDisplayTransform Implementation |
|
4214 // |
|
4215 |
|
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 |
|
4226 |
|
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 |
|
4237 |
|
4238 nsRect |
|
4239 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame) |
|
4240 { |
|
4241 NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!"); |
|
4242 |
|
4243 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { |
|
4244 // TODO: SVG needs to define what percentage translations resolve against. |
|
4245 return nsRect(); |
|
4246 } |
|
4247 |
|
4248 return nsRect(nsPoint(0, 0), aFrame->GetSize()); |
|
4249 } |
|
4250 |
|
4251 #else |
|
4252 |
|
4253 nsRect |
|
4254 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame) |
|
4255 { |
|
4256 NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!"); |
|
4257 |
|
4258 nsRect result; |
|
4259 |
|
4260 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { |
|
4261 // TODO: SVG needs to define what percentage translations resolve against. |
|
4262 return result; |
|
4263 } |
|
4264 |
|
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 } |
|
4278 |
|
4279 return result; |
|
4280 } |
|
4281 |
|
4282 #endif |
|
4283 |
|
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 } |
|
4297 |
|
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 } |
|
4312 |
|
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 } |
|
4327 |
|
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!"); |
|
4341 |
|
4342 if (!aFrame->IsTransformed()) { |
|
4343 return gfxPoint3D(); |
|
4344 } |
|
4345 |
|
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)); |
|
4353 |
|
4354 /* Allows us to access named variables by index. */ |
|
4355 float coords[3]; |
|
4356 const nscoord* dimensions[2] = |
|
4357 {&boundingRect.width, &boundingRect.height}; |
|
4358 |
|
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 } |
|
4388 |
|
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); |
|
4394 |
|
4395 return gfxPoint3D(coords[0], coords[1], coords[2]); |
|
4396 } |
|
4397 |
|
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!"); |
|
4410 |
|
4411 if (!aFrame->IsTransformed()) { |
|
4412 return gfxPoint3D(); |
|
4413 } |
|
4414 |
|
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 */ |
|
4419 |
|
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); |
|
4428 |
|
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}; |
|
4435 |
|
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 } |
|
4457 |
|
4458 nsPoint parentOffset = aFrame->GetOffsetTo(parent); |
|
4459 gfxPoint3D gfxOffset( |
|
4460 NSAppUnitsToFloatPixels(parentOffset.x, aAppUnitsPerPixel), |
|
4461 NSAppUnitsToFloatPixels(parentOffset.y, aAppUnitsPerPixel), |
|
4462 0.0f); |
|
4463 |
|
4464 return result - gfxOffset; |
|
4465 } |
|
4466 |
|
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 } |
|
4485 |
|
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 } |
|
4500 |
|
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); |
|
4511 |
|
4512 return GetResultingTransformMatrixInternal(props, aOrigin, aAppUnitsPerPixel, |
|
4513 aBoundsOverride, aOutAncestor); |
|
4514 } |
|
4515 |
|
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; |
|
4524 |
|
4525 if (aOutAncestor) { |
|
4526 *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(frame); |
|
4527 } |
|
4528 |
|
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); |
|
4536 |
|
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)); |
|
4542 |
|
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 } |
|
4565 |
|
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 } |
|
4574 |
|
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 } |
|
4584 |
|
4585 gfxPoint3D rounded(hasSVGTransforms ? newOrigin.x : NS_round(newOrigin.x), |
|
4586 hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y), |
|
4587 0); |
|
4588 |
|
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 } |
|
4604 |
|
4605 return nsLayoutUtils::ChangeMatrixBasis |
|
4606 (rounded + aProperties.mToTransformOrigin, result); |
|
4607 } |
|
4608 |
|
4609 bool |
|
4610 nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) |
|
4611 { |
|
4612 if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_opacity)) { |
|
4613 return true; |
|
4614 } |
|
4615 |
|
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 } |
|
4624 |
|
4625 bool |
|
4626 nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) |
|
4627 { |
|
4628 return ShouldPrerenderTransformedContent(aBuilder, |
|
4629 Frame(), |
|
4630 nsLayoutUtils::IsAnimationLoggingEnabled()); |
|
4631 } |
|
4632 |
|
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 } |
|
4654 |
|
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(); |
|
4661 |
|
4662 frameRect = |
|
4663 nsLayoutUtils::TransformFrameRectToAncestor(aFrame, frameRect, |
|
4664 aBuilder->RootReferenceFrame()); |
|
4665 |
|
4666 if (frameRect.Size() <= refSize) { |
|
4667 return true; |
|
4668 } |
|
4669 |
|
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 } |
|
4686 |
|
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 } |
|
4699 |
|
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); |
|
4715 |
|
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 } |
|
4730 |
|
4731 bool |
|
4732 nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) |
|
4733 { |
|
4734 return ShouldPrerenderTransformedContent(aBuilder, mFrame, false); |
|
4735 } |
|
4736 |
|
4737 already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder, |
|
4738 LayerManager *aManager, |
|
4739 const ContainerLayerParameters& aContainerParameters) |
|
4740 { |
|
4741 const gfx3DMatrix& newTransformMatrix = GetTransform(); |
|
4742 |
|
4743 if (mFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN && |
|
4744 newTransformMatrix.IsBackfaceVisible()) { |
|
4745 return nullptr; |
|
4746 } |
|
4747 |
|
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); |
|
4753 |
|
4754 if (!container) { |
|
4755 return nullptr; |
|
4756 } |
|
4757 |
|
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 } |
|
4765 |
|
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 } |
|
4779 |
|
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 } |
|
4800 |
|
4801 const nsStyleDisplay* disp = mFrame->StyleDisplay(); |
|
4802 if ((disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM)) { |
|
4803 return LAYER_ACTIVE; |
|
4804 } |
|
4805 |
|
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 } |
|
4813 |
|
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 } |
|
4835 |
|
4836 #ifdef DEBUG_HIT |
|
4837 #include <time.h> |
|
4838 #endif |
|
4839 |
|
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(); |
|
4856 |
|
4857 if (!IsFrameVisible(mFrame, matrix)) { |
|
4858 return; |
|
4859 } |
|
4860 |
|
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)); |
|
4871 |
|
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 } |
|
4883 |
|
4884 resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point.x), factor), |
|
4885 NSFloatPixelsToAppUnits(float(point.y), factor), |
|
4886 1, 1); |
|
4887 |
|
4888 } else { |
|
4889 gfxRect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor), |
|
4890 NSAppUnitsToFloatPixels(aRect.y, factor), |
|
4891 NSAppUnitsToFloatPixels(aRect.width, factor), |
|
4892 NSAppUnitsToFloatPixels(aRect.height, factor)); |
|
4893 |
|
4894 gfxRect rect = matrix.UntransformBounds(originalRect, childGfxBounds); |
|
4895 |
|
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 } |
|
4901 |
|
4902 if (resultingRect.IsEmpty()) { |
|
4903 return; |
|
4904 } |
|
4905 |
|
4906 |
|
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 |
|
4912 |
|
4913 mStoredList.HitTest(aBuilder, resultingRect, aState, aOutFrames); |
|
4914 |
|
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 |
|
4921 |
|
4922 } |
|
4923 |
|
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(); |
|
4930 |
|
4931 NS_ASSERTION(IsFrameVisible(mFrame, matrix), "We can't have hit a frame that isn't visible!"); |
|
4932 |
|
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)); |
|
4939 |
|
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?"); |
|
4946 |
|
4947 gfxPoint3D transformed = matrix.Transform3D(gfxPoint3D(point.x, point.y, 0)); |
|
4948 return transformed.z; |
|
4949 } |
|
4950 |
|
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 } |
|
4967 |
|
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 } |
|
4999 |
|
5000 const gfx3DMatrix& matrix = GetTransform(); |
|
5001 |
|
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 } |
|
5012 |
|
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(); |
|
5024 |
|
5025 gfxMatrix matrix2d; |
|
5026 return matrix.Is2D(&matrix2d) && |
|
5027 matrix2d.PreservesAxisAlignedRectangles() && |
|
5028 mStoredList.GetVisibleRect().Contains(untransformedVisible) && |
|
5029 mStoredList.IsUniform(aBuilder, aColor); |
|
5030 } |
|
5031 |
|
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 |
|
5037 |
|
5038 bool |
|
5039 nsDisplayTransform::TryMerge(nsDisplayListBuilder *aBuilder, |
|
5040 nsDisplayItem *aItem) |
|
5041 { |
|
5042 return false; |
|
5043 } |
|
5044 |
|
5045 #else |
|
5046 |
|
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?"); |
|
5053 |
|
5054 /* Make sure that we're dealing with two transforms. */ |
|
5055 if (aItem->GetType() != TYPE_TRANSFORM) |
|
5056 return false; |
|
5057 |
|
5058 /* Check to see that both frames are part of the same content. */ |
|
5059 if (aItem->Frame()->GetContent() != mFrame->GetContent()) |
|
5060 return false; |
|
5061 |
|
5062 if (aItem->GetClip() != GetClip()) |
|
5063 return false; |
|
5064 |
|
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 } |
|
5071 |
|
5072 #endif |
|
5073 |
|
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!"); |
|
5094 |
|
5095 float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
5096 return nsLayoutUtils::MatrixTransformRect |
|
5097 (aUntransformedBounds, |
|
5098 GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride), |
|
5099 factor); |
|
5100 } |
|
5101 |
|
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!"); |
|
5108 |
|
5109 float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
5110 return nsLayoutUtils::MatrixTransformRectOut |
|
5111 (aUntransformedBounds, |
|
5112 GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride), |
|
5113 factor); |
|
5114 } |
|
5115 |
|
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!"); |
|
5123 |
|
5124 float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
5125 |
|
5126 gfx3DMatrix transform = GetResultingTransformMatrix(aFrame, aOrigin, factor, nullptr); |
|
5127 if (transform.IsSingular()) { |
|
5128 return false; |
|
5129 } |
|
5130 |
|
5131 gfxRect result(NSAppUnitsToFloatPixels(aTransformedBounds.x, factor), |
|
5132 NSAppUnitsToFloatPixels(aTransformedBounds.y, factor), |
|
5133 NSAppUnitsToFloatPixels(aTransformedBounds.width, factor), |
|
5134 NSAppUnitsToFloatPixels(aTransformedBounds.height, factor)); |
|
5135 |
|
5136 gfxRect childGfxBounds(NSAppUnitsToFloatPixels(aChildBounds.x, factor), |
|
5137 NSAppUnitsToFloatPixels(aChildBounds.y, factor), |
|
5138 NSAppUnitsToFloatPixels(aChildBounds.width, factor), |
|
5139 NSAppUnitsToFloatPixels(aChildBounds.height, factor)); |
|
5140 |
|
5141 result = transform.UntransformBounds(result, childGfxBounds); |
|
5142 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor); |
|
5143 return true; |
|
5144 } |
|
5145 |
|
5146 bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder, |
|
5147 nsRect *aOutRect) |
|
5148 { |
|
5149 const gfx3DMatrix& matrix = GetTransform(); |
|
5150 if (matrix.IsSingular()) |
|
5151 return false; |
|
5152 |
|
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)); |
|
5159 |
|
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)); |
|
5166 |
|
5167 /* We want to untransform the matrix, so invert the transformation first! */ |
|
5168 result = matrix.UntransformBounds(result, childGfxBounds); |
|
5169 |
|
5170 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor); |
|
5171 |
|
5172 return true; |
|
5173 } |
|
5174 |
|
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 } |
|
5182 |
|
5183 #ifdef NS_BUILD_REFCNT_LOGGING |
|
5184 nsDisplaySVGEffects::~nsDisplaySVGEffects() |
|
5185 { |
|
5186 MOZ_COUNT_DTOR(nsDisplaySVGEffects); |
|
5187 } |
|
5188 #endif |
|
5189 |
|
5190 nsRegion nsDisplaySVGEffects::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
|
5191 bool* aSnap) |
|
5192 { |
|
5193 *aSnap = false; |
|
5194 return nsRegion(); |
|
5195 } |
|
5196 |
|
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 } |
|
5207 |
|
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 } |
|
5217 |
|
5218 LayerState |
|
5219 nsDisplaySVGEffects::GetLayerState(nsDisplayListBuilder* aBuilder, |
|
5220 LayerManager* aManager, |
|
5221 const ContainerLayerParameters& aParameters) |
|
5222 { |
|
5223 return LAYER_SVG_EFFECTS; |
|
5224 } |
|
5225 |
|
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 } |
|
5243 |
|
5244 float opacity = mFrame->StyleDisplay()->mOpacity; |
|
5245 if (opacity == 0.0f) |
|
5246 return nullptr; |
|
5247 |
|
5248 nsIFrame* firstFrame = |
|
5249 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); |
|
5250 nsSVGEffects::EffectProperties effectProperties = |
|
5251 nsSVGEffects::GetEffectProperties(firstFrame); |
|
5252 |
|
5253 bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); |
|
5254 effectProperties.GetClipPathFrame(&isOK); |
|
5255 effectProperties.GetMaskFrame(&isOK); |
|
5256 |
|
5257 if (!isOK) { |
|
5258 return nullptr; |
|
5259 } |
|
5260 |
|
5261 ContainerLayerParameters newContainerParameters = aContainerParameters; |
|
5262 if (effectProperties.HasValidFilter()) { |
|
5263 newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true; |
|
5264 } |
|
5265 |
|
5266 nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()-> |
|
5267 BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, |
|
5268 newContainerParameters, nullptr); |
|
5269 |
|
5270 return container.forget(); |
|
5271 } |
|
5272 |
|
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; |
|
5281 |
|
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 } |
|
5289 |
|
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 } |
|
5307 |
|
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 |
|
5347 |