|
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 #include "nsLayoutUtils.h" |
|
8 |
|
9 #include "mozilla/ArrayUtils.h" |
|
10 #include "mozilla/BasicEvents.h" |
|
11 #include "mozilla/MemoryReporting.h" |
|
12 #include "nsPresContext.h" |
|
13 #include "nsIContent.h" |
|
14 #include "nsIDOMHTMLDocument.h" |
|
15 #include "nsIDOMHTMLElement.h" |
|
16 #include "nsFrameList.h" |
|
17 #include "nsGkAtoms.h" |
|
18 #include "nsIAtom.h" |
|
19 #include "nsCSSPseudoElements.h" |
|
20 #include "nsCSSAnonBoxes.h" |
|
21 #include "nsCSSColorUtils.h" |
|
22 #include "nsView.h" |
|
23 #include "nsPlaceholderFrame.h" |
|
24 #include "nsIScrollableFrame.h" |
|
25 #include "nsIDOMEvent.h" |
|
26 #include "nsDisplayList.h" |
|
27 #include "nsRegion.h" |
|
28 #include "nsFrameManager.h" |
|
29 #include "nsBlockFrame.h" |
|
30 #include "nsBidiPresUtils.h" |
|
31 #include "imgIContainer.h" |
|
32 #include "ImageOps.h" |
|
33 #include "gfxRect.h" |
|
34 #include "gfxContext.h" |
|
35 #include "nsRenderingContext.h" |
|
36 #include "nsIInterfaceRequestorUtils.h" |
|
37 #include "nsCSSRendering.h" |
|
38 #include "nsThemeConstants.h" |
|
39 #include "nsPIDOMWindow.h" |
|
40 #include "nsIDocShell.h" |
|
41 #include "nsIWidget.h" |
|
42 #include "gfxMatrix.h" |
|
43 #include "gfxPoint3D.h" |
|
44 #include "gfxPrefs.h" |
|
45 #include "gfxTypes.h" |
|
46 #include "nsTArray.h" |
|
47 #include "mozilla/dom/HTMLCanvasElement.h" |
|
48 #include "nsICanvasRenderingContextInternal.h" |
|
49 #include "gfxPlatform.h" |
|
50 #include <algorithm> |
|
51 #include "mozilla/dom/HTMLVideoElement.h" |
|
52 #include "mozilla/dom/HTMLImageElement.h" |
|
53 #include "mozilla/dom/DOMRect.h" |
|
54 #include "imgIRequest.h" |
|
55 #include "nsIImageLoadingContent.h" |
|
56 #include "nsCOMPtr.h" |
|
57 #include "nsCSSProps.h" |
|
58 #include "nsListControlFrame.h" |
|
59 #include "mozilla/dom/Element.h" |
|
60 #include "nsCanvasFrame.h" |
|
61 #include "gfxDrawable.h" |
|
62 #include "gfxUtils.h" |
|
63 #include "nsDataHashtable.h" |
|
64 #include "nsTextFrame.h" |
|
65 #include "nsFontFaceList.h" |
|
66 #include "nsFontInflationData.h" |
|
67 #include "nsSVGUtils.h" |
|
68 #include "SVGTextFrame.h" |
|
69 #include "nsStyleStructInlines.h" |
|
70 #include "nsStyleTransformMatrix.h" |
|
71 #include "nsIFrameInlines.h" |
|
72 #include "ImageContainer.h" |
|
73 #include "nsComputedDOMStyle.h" |
|
74 #include "ActiveLayerTracker.h" |
|
75 #include "mozilla/gfx/2D.h" |
|
76 #include "gfx2DGlue.h" |
|
77 #include "mozilla/LookAndFeel.h" |
|
78 #include "UnitTransforms.h" |
|
79 #include "TiledLayerBuffer.h" // For TILEDLAYERBUFFER_TILE_SIZE |
|
80 |
|
81 #include "mozilla/Preferences.h" |
|
82 |
|
83 #include "mozilla/LookAndFeel.h" |
|
84 |
|
85 #ifdef MOZ_XUL |
|
86 #include "nsXULPopupManager.h" |
|
87 #endif |
|
88 |
|
89 #include "GeckoProfiler.h" |
|
90 #include "nsAnimationManager.h" |
|
91 #include "nsTransitionManager.h" |
|
92 #include "RestyleManager.h" |
|
93 |
|
94 // Additional includes used on B2G by code in GetOrMaybeCreateDisplayPort(). |
|
95 #ifdef MOZ_WIDGET_GONK |
|
96 #include "mozilla/layers/AsyncPanZoomController.h" |
|
97 #endif |
|
98 |
|
99 using namespace mozilla; |
|
100 using namespace mozilla::css; |
|
101 using namespace mozilla::dom; |
|
102 using namespace mozilla::layers; |
|
103 using namespace mozilla::layout; |
|
104 using namespace mozilla::gfx; |
|
105 |
|
106 using mozilla::image::Angle; |
|
107 using mozilla::image::Flip; |
|
108 using mozilla::image::ImageOps; |
|
109 using mozilla::image::Orientation; |
|
110 |
|
111 #define GRID_ENABLED_PREF_NAME "layout.css.grid.enabled" |
|
112 #define STICKY_ENABLED_PREF_NAME "layout.css.sticky.enabled" |
|
113 #define TEXT_ALIGN_TRUE_ENABLED_PREF_NAME "layout.css.text-align-true-value.enabled" |
|
114 |
|
115 #ifdef DEBUG |
|
116 // TODO: remove, see bug 598468. |
|
117 bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false; |
|
118 #endif // DEBUG |
|
119 |
|
120 typedef FrameMetrics::ViewID ViewID; |
|
121 |
|
122 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationEmPerLine; |
|
123 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationMinTwips; |
|
124 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationLineThreshold; |
|
125 /* static */ int32_t nsLayoutUtils::sFontSizeInflationMappingIntercept; |
|
126 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationMaxRatio; |
|
127 /* static */ bool nsLayoutUtils::sFontSizeInflationForceEnabled; |
|
128 /* static */ bool nsLayoutUtils::sFontSizeInflationDisabledInMasterProcess; |
|
129 /* static */ bool nsLayoutUtils::sInvalidationDebuggingIsEnabled; |
|
130 /* static */ bool nsLayoutUtils::sCSSVariablesEnabled; |
|
131 /* static */ bool nsLayoutUtils::sInterruptibleReflowEnabled; |
|
132 |
|
133 static ViewID sScrollIdCounter = FrameMetrics::START_SCROLL_ID; |
|
134 |
|
135 typedef nsDataHashtable<nsUint64HashKey, nsIContent*> ContentMap; |
|
136 static ContentMap* sContentMap = nullptr; |
|
137 static ContentMap& GetContentMap() { |
|
138 if (!sContentMap) { |
|
139 sContentMap = new ContentMap(); |
|
140 } |
|
141 return *sContentMap; |
|
142 } |
|
143 |
|
144 // When the pref "layout.css.grid.enabled" changes, this function is invoked |
|
145 // to let us update kDisplayKTable, to selectively disable or restore the |
|
146 // entries for "grid" and "inline-grid" in that table. |
|
147 static void |
|
148 GridEnabledPrefChangeCallback(const char* aPrefName, void* aClosure) |
|
149 { |
|
150 MOZ_ASSERT(strncmp(aPrefName, GRID_ENABLED_PREF_NAME, |
|
151 ArrayLength(GRID_ENABLED_PREF_NAME)) == 0, |
|
152 "We only registered this callback for a single pref, so it " |
|
153 "should only be called for that pref"); |
|
154 |
|
155 static int32_t sIndexOfGridInDisplayTable; |
|
156 static int32_t sIndexOfInlineGridInDisplayTable; |
|
157 static bool sAreGridKeywordIndicesInitialized; // initialized to false |
|
158 |
|
159 bool isGridEnabled = |
|
160 Preferences::GetBool(GRID_ENABLED_PREF_NAME, false); |
|
161 if (!sAreGridKeywordIndicesInitialized) { |
|
162 // First run: find the position of "grid" and "inline-grid" in |
|
163 // kDisplayKTable. |
|
164 sIndexOfGridInDisplayTable = |
|
165 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_grid, |
|
166 nsCSSProps::kDisplayKTable); |
|
167 MOZ_ASSERT(sIndexOfGridInDisplayTable >= 0, |
|
168 "Couldn't find grid in kDisplayKTable"); |
|
169 sIndexOfInlineGridInDisplayTable = |
|
170 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_grid, |
|
171 nsCSSProps::kDisplayKTable); |
|
172 MOZ_ASSERT(sIndexOfInlineGridInDisplayTable >= 0, |
|
173 "Couldn't find inline-grid in kDisplayKTable"); |
|
174 sAreGridKeywordIndicesInitialized = true; |
|
175 } |
|
176 |
|
177 // OK -- now, stomp on or restore the "grid" entries in kDisplayKTable, |
|
178 // depending on whether the grid pref is enabled vs. disabled. |
|
179 if (sIndexOfGridInDisplayTable >= 0) { |
|
180 nsCSSProps::kDisplayKTable[sIndexOfGridInDisplayTable] = |
|
181 isGridEnabled ? eCSSKeyword_grid : eCSSKeyword_UNKNOWN; |
|
182 } |
|
183 if (sIndexOfInlineGridInDisplayTable >= 0) { |
|
184 nsCSSProps::kDisplayKTable[sIndexOfInlineGridInDisplayTable] = |
|
185 isGridEnabled ? eCSSKeyword_inline_grid : eCSSKeyword_UNKNOWN; |
|
186 } |
|
187 } |
|
188 |
|
189 // When the pref "layout.css.sticky.enabled" changes, this function is invoked |
|
190 // to let us update kPositionKTable, to selectively disable or restore the |
|
191 // entry for "sticky" in that table. |
|
192 static void |
|
193 StickyEnabledPrefChangeCallback(const char* aPrefName, void* aClosure) |
|
194 { |
|
195 MOZ_ASSERT(strncmp(aPrefName, STICKY_ENABLED_PREF_NAME, |
|
196 ArrayLength(STICKY_ENABLED_PREF_NAME)) == 0, |
|
197 "We only registered this callback for a single pref, so it " |
|
198 "should only be called for that pref"); |
|
199 |
|
200 static int32_t sIndexOfStickyInPositionTable; |
|
201 static bool sIsStickyKeywordIndexInitialized; // initialized to false |
|
202 |
|
203 bool isStickyEnabled = |
|
204 Preferences::GetBool(STICKY_ENABLED_PREF_NAME, false); |
|
205 |
|
206 if (!sIsStickyKeywordIndexInitialized) { |
|
207 // First run: find the position of "sticky" in kPositionKTable. |
|
208 sIndexOfStickyInPositionTable = |
|
209 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_sticky, |
|
210 nsCSSProps::kPositionKTable); |
|
211 MOZ_ASSERT(sIndexOfStickyInPositionTable >= 0, |
|
212 "Couldn't find sticky in kPositionKTable"); |
|
213 sIsStickyKeywordIndexInitialized = true; |
|
214 } |
|
215 |
|
216 // OK -- now, stomp on or restore the "sticky" entry in kPositionKTable, |
|
217 // depending on whether the sticky pref is enabled vs. disabled. |
|
218 nsCSSProps::kPositionKTable[sIndexOfStickyInPositionTable] = |
|
219 isStickyEnabled ? eCSSKeyword_sticky : eCSSKeyword_UNKNOWN; |
|
220 } |
|
221 |
|
222 // When the pref "layout.css.text-align-true-value.enabled" changes, this |
|
223 // function is called to let us update kTextAlignKTable & kTextAlignLastKTable, |
|
224 // to selectively disable or restore the entries for "true" in those tables. |
|
225 static void |
|
226 TextAlignTrueEnabledPrefChangeCallback(const char* aPrefName, void* aClosure) |
|
227 { |
|
228 NS_ASSERTION(strcmp(aPrefName, TEXT_ALIGN_TRUE_ENABLED_PREF_NAME) == 0, |
|
229 "Did you misspell " TEXT_ALIGN_TRUE_ENABLED_PREF_NAME " ?"); |
|
230 |
|
231 static bool sIsInitialized; |
|
232 static int32_t sIndexOfTrueInTextAlignTable; |
|
233 static int32_t sIndexOfTrueInTextAlignLastTable; |
|
234 bool isTextAlignTrueEnabled = |
|
235 Preferences::GetBool(TEXT_ALIGN_TRUE_ENABLED_PREF_NAME, false); |
|
236 |
|
237 if (!sIsInitialized) { |
|
238 // First run: find the position of "true" in kTextAlignKTable. |
|
239 sIndexOfTrueInTextAlignTable = |
|
240 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_true, |
|
241 nsCSSProps::kTextAlignKTable); |
|
242 // First run: find the position of "true" in kTextAlignLastKTable. |
|
243 sIndexOfTrueInTextAlignLastTable = |
|
244 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_true, |
|
245 nsCSSProps::kTextAlignLastKTable); |
|
246 sIsInitialized = true; |
|
247 } |
|
248 |
|
249 // OK -- now, stomp on or restore the "true" entry in the keyword tables, |
|
250 // depending on whether the pref is enabled vs. disabled. |
|
251 MOZ_ASSERT(sIndexOfTrueInTextAlignTable >= 0); |
|
252 nsCSSProps::kTextAlignKTable[sIndexOfTrueInTextAlignTable] = |
|
253 isTextAlignTrueEnabled ? eCSSKeyword_true : eCSSKeyword_UNKNOWN; |
|
254 MOZ_ASSERT(sIndexOfTrueInTextAlignLastTable >= 0); |
|
255 nsCSSProps::kTextAlignLastKTable[sIndexOfTrueInTextAlignLastTable] = |
|
256 isTextAlignTrueEnabled ? eCSSKeyword_true : eCSSKeyword_UNKNOWN; |
|
257 } |
|
258 |
|
259 template <class AnimationsOrTransitions> |
|
260 static AnimationsOrTransitions* |
|
261 HasAnimationOrTransitionForCompositor(nsIContent* aContent, |
|
262 nsIAtom* aAnimationProperty, |
|
263 nsCSSProperty aProperty) |
|
264 { |
|
265 AnimationsOrTransitions* animations = |
|
266 static_cast<AnimationsOrTransitions*>(aContent->GetProperty(aAnimationProperty)); |
|
267 if (animations) { |
|
268 bool propertyMatches = animations->HasAnimationOfProperty(aProperty); |
|
269 if (propertyMatches && |
|
270 animations->CanPerformOnCompositorThread( |
|
271 CommonElementAnimationData::CanAnimate_AllowPartial)) { |
|
272 return animations; |
|
273 } |
|
274 } |
|
275 |
|
276 return nullptr; |
|
277 } |
|
278 |
|
279 bool |
|
280 nsLayoutUtils::HasAnimationsForCompositor(nsIContent* aContent, |
|
281 nsCSSProperty aProperty) |
|
282 { |
|
283 if (!aContent->MayHaveAnimations()) |
|
284 return false; |
|
285 return HasAnimationOrTransitionForCompositor<ElementAnimations> |
|
286 (aContent, nsGkAtoms::animationsProperty, aProperty) || |
|
287 HasAnimationOrTransitionForCompositor<ElementTransitions> |
|
288 (aContent, nsGkAtoms::transitionsProperty, aProperty); |
|
289 } |
|
290 |
|
291 template <class AnimationsOrTransitions> |
|
292 AnimationsOrTransitions* |
|
293 mozilla::HasAnimationOrTransition(nsIContent* aContent, |
|
294 nsIAtom* aAnimationProperty, |
|
295 nsCSSProperty aProperty) |
|
296 { |
|
297 AnimationsOrTransitions* animations = |
|
298 static_cast<AnimationsOrTransitions*>(aContent->GetProperty(aAnimationProperty)); |
|
299 if (animations) { |
|
300 bool propertyMatches = animations->HasAnimationOfProperty(aProperty); |
|
301 if (propertyMatches) { |
|
302 return animations; |
|
303 } |
|
304 } |
|
305 |
|
306 return nullptr; |
|
307 } |
|
308 |
|
309 template ElementAnimations* |
|
310 mozilla::HasAnimationOrTransition<ElementAnimations>(nsIContent* aContent, |
|
311 nsIAtom* aAnimationProperty, |
|
312 nsCSSProperty aProperty); |
|
313 |
|
314 template ElementTransitions* |
|
315 mozilla::HasAnimationOrTransition<ElementTransitions>(nsIContent* aContent, |
|
316 nsIAtom* aAnimationProperty, |
|
317 nsCSSProperty aProperty); |
|
318 |
|
319 |
|
320 bool |
|
321 nsLayoutUtils::HasAnimations(nsIContent* aContent, |
|
322 nsCSSProperty aProperty) |
|
323 { |
|
324 if (!aContent->MayHaveAnimations()) |
|
325 return false; |
|
326 return HasAnimationOrTransition<ElementAnimations> |
|
327 (aContent, nsGkAtoms::animationsProperty, aProperty) || |
|
328 HasAnimationOrTransition<ElementTransitions> |
|
329 (aContent, nsGkAtoms::transitionsProperty, aProperty); |
|
330 } |
|
331 |
|
332 static gfxSize |
|
333 GetScaleForValue(const nsStyleAnimation::Value& aValue, |
|
334 nsIFrame* aFrame) |
|
335 { |
|
336 if (!aFrame) { |
|
337 NS_WARNING("No frame."); |
|
338 return gfxSize(); |
|
339 } |
|
340 if (aValue.GetUnit() != nsStyleAnimation::eUnit_Transform) { |
|
341 NS_WARNING("Expected a transform."); |
|
342 return gfxSize(); |
|
343 } |
|
344 |
|
345 nsCSSValueSharedList* list = aValue.GetCSSValueSharedListValue(); |
|
346 MOZ_ASSERT(list->mHead); |
|
347 |
|
348 if (list->mHead->mValue.GetUnit() == eCSSUnit_None) { |
|
349 // There is an animation, but no actual transform yet. |
|
350 return gfxSize(); |
|
351 } |
|
352 |
|
353 nsRect frameBounds = aFrame->GetRect(); |
|
354 bool dontCare; |
|
355 gfx3DMatrix transform = nsStyleTransformMatrix::ReadTransforms( |
|
356 list->mHead, |
|
357 aFrame->StyleContext(), |
|
358 aFrame->PresContext(), dontCare, frameBounds, |
|
359 aFrame->PresContext()->AppUnitsPerDevPixel()); |
|
360 |
|
361 gfxMatrix transform2d; |
|
362 bool canDraw2D = transform.CanDraw2D(&transform2d); |
|
363 if (!canDraw2D) { |
|
364 return gfxSize(); |
|
365 } |
|
366 |
|
367 return transform2d.ScaleFactors(true); |
|
368 } |
|
369 |
|
370 float |
|
371 GetSuitableScale(float aMaxScale, float aMinScale) |
|
372 { |
|
373 // If the minimum scale >= 1.0f, use it; if the maximum <= 1.0f, use it; |
|
374 // otherwise use 1.0f. |
|
375 if (aMinScale >= 1.0f) { |
|
376 return aMinScale; |
|
377 } |
|
378 else if (aMaxScale <= 1.0f) { |
|
379 return aMaxScale; |
|
380 } |
|
381 |
|
382 return 1.0f; |
|
383 } |
|
384 |
|
385 gfxSize |
|
386 nsLayoutUtils::ComputeSuitableScaleForAnimation(nsIContent* aContent) |
|
387 { |
|
388 gfxSize maxScale(1.0f, 1.0f); |
|
389 gfxSize minScale(1.0f, 1.0f); |
|
390 |
|
391 ElementAnimations* animations = HasAnimationOrTransitionForCompositor<ElementAnimations> |
|
392 (aContent, nsGkAtoms::animationsProperty, eCSSProperty_transform); |
|
393 if (animations) { |
|
394 for (uint32_t animIdx = animations->mAnimations.Length(); animIdx-- != 0; ) { |
|
395 mozilla::StyleAnimation& anim = animations->mAnimations[animIdx]; |
|
396 for (uint32_t propIdx = anim.mProperties.Length(); propIdx-- != 0; ) { |
|
397 AnimationProperty& prop = anim.mProperties[propIdx]; |
|
398 if (prop.mProperty == eCSSProperty_transform) { |
|
399 for (uint32_t segIdx = prop.mSegments.Length(); segIdx-- != 0; ) { |
|
400 AnimationPropertySegment& segment = prop.mSegments[segIdx]; |
|
401 gfxSize from = GetScaleForValue(segment.mFromValue, |
|
402 aContent->GetPrimaryFrame()); |
|
403 maxScale.width = std::max<float>(maxScale.width, from.width); |
|
404 maxScale.height = std::max<float>(maxScale.height, from.height); |
|
405 minScale.width = std::min<float>(minScale.width, from.width); |
|
406 minScale.height = std::min<float>(minScale.height, from.height); |
|
407 gfxSize to = GetScaleForValue(segment.mToValue, |
|
408 aContent->GetPrimaryFrame()); |
|
409 maxScale.width = std::max<float>(maxScale.width, to.width); |
|
410 maxScale.height = std::max<float>(maxScale.height, to.height); |
|
411 minScale.width = std::min<float>(minScale.width, to.width); |
|
412 minScale.height = std::min<float>(minScale.height, to.height); |
|
413 } |
|
414 } |
|
415 } |
|
416 } |
|
417 } |
|
418 |
|
419 ElementTransitions* transitions = HasAnimationOrTransitionForCompositor<ElementTransitions> |
|
420 (aContent, nsGkAtoms::transitionsProperty, eCSSProperty_transform); |
|
421 if (transitions) { |
|
422 for (uint32_t i = 0, i_end = transitions->mPropertyTransitions.Length(); |
|
423 i < i_end; ++i){ |
|
424 ElementPropertyTransition &pt = transitions->mPropertyTransitions[i]; |
|
425 if (pt.IsRemovedSentinel()) { |
|
426 continue; |
|
427 } |
|
428 MOZ_ASSERT(pt.mProperties.Length() == 1, |
|
429 "Should have one animation property for a transition"); |
|
430 MOZ_ASSERT(pt.mProperties[0].mSegments.Length() == 1, |
|
431 "Animation property should have one segment for a transition"); |
|
432 const AnimationPropertySegment& segment = pt.mProperties[0].mSegments[0]; |
|
433 |
|
434 if (pt.mProperties[0].mProperty == eCSSProperty_transform) { |
|
435 gfxSize start = GetScaleForValue(segment.mFromValue, |
|
436 aContent->GetPrimaryFrame()); |
|
437 maxScale.width = std::max<float>(maxScale.width, start.width); |
|
438 maxScale.height = std::max<float>(maxScale.height, start.height); |
|
439 minScale.width = std::min<float>(minScale.width, start.width); |
|
440 minScale.height = std::min<float>(minScale.height, start.height); |
|
441 gfxSize end = GetScaleForValue(segment.mToValue, |
|
442 aContent->GetPrimaryFrame()); |
|
443 maxScale.width = std::max<float>(maxScale.width, end.width); |
|
444 maxScale.height = std::max<float>(maxScale.height, end.height); |
|
445 minScale.width = std::min<float>(minScale.width, end.width); |
|
446 minScale.height = std::min<float>(minScale.height, end.height); |
|
447 } |
|
448 } |
|
449 } |
|
450 |
|
451 return gfxSize(GetSuitableScale(maxScale.width, minScale.width), |
|
452 GetSuitableScale(maxScale.height, minScale.height)); |
|
453 } |
|
454 |
|
455 bool |
|
456 nsLayoutUtils::AreAsyncAnimationsEnabled() |
|
457 { |
|
458 static bool sAreAsyncAnimationsEnabled; |
|
459 static bool sAsyncPrefCached = false; |
|
460 |
|
461 if (!sAsyncPrefCached) { |
|
462 sAsyncPrefCached = true; |
|
463 Preferences::AddBoolVarCache(&sAreAsyncAnimationsEnabled, |
|
464 "layers.offmainthreadcomposition.async-animations"); |
|
465 } |
|
466 |
|
467 return sAreAsyncAnimationsEnabled && |
|
468 gfxPlatform::OffMainThreadCompositingEnabled(); |
|
469 } |
|
470 |
|
471 bool |
|
472 nsLayoutUtils::IsAnimationLoggingEnabled() |
|
473 { |
|
474 static bool sShouldLog; |
|
475 static bool sShouldLogPrefCached; |
|
476 |
|
477 if (!sShouldLogPrefCached) { |
|
478 sShouldLogPrefCached = true; |
|
479 Preferences::AddBoolVarCache(&sShouldLog, |
|
480 "layers.offmainthreadcomposition.log-animations"); |
|
481 } |
|
482 |
|
483 return sShouldLog; |
|
484 } |
|
485 |
|
486 bool |
|
487 nsLayoutUtils::UseBackgroundNearestFiltering() |
|
488 { |
|
489 static bool sUseBackgroundNearestFilteringEnabled; |
|
490 static bool sUseBackgroundNearestFilteringPrefInitialised = false; |
|
491 |
|
492 if (!sUseBackgroundNearestFilteringPrefInitialised) { |
|
493 sUseBackgroundNearestFilteringPrefInitialised = true; |
|
494 sUseBackgroundNearestFilteringEnabled = |
|
495 Preferences::GetBool("gfx.filter.nearest.force-enabled", false); |
|
496 } |
|
497 |
|
498 return sUseBackgroundNearestFilteringEnabled; |
|
499 } |
|
500 |
|
501 bool |
|
502 nsLayoutUtils::GPUImageScalingEnabled() |
|
503 { |
|
504 static bool sGPUImageScalingEnabled; |
|
505 static bool sGPUImageScalingPrefInitialised = false; |
|
506 |
|
507 if (!sGPUImageScalingPrefInitialised) { |
|
508 sGPUImageScalingPrefInitialised = true; |
|
509 sGPUImageScalingEnabled = |
|
510 Preferences::GetBool("layout.gpu-image-scaling.enabled", false); |
|
511 } |
|
512 |
|
513 return sGPUImageScalingEnabled; |
|
514 } |
|
515 |
|
516 bool |
|
517 nsLayoutUtils::AnimatedImageLayersEnabled() |
|
518 { |
|
519 static bool sAnimatedImageLayersEnabled; |
|
520 static bool sAnimatedImageLayersPrefCached = false; |
|
521 |
|
522 if (!sAnimatedImageLayersPrefCached) { |
|
523 sAnimatedImageLayersPrefCached = true; |
|
524 Preferences::AddBoolVarCache(&sAnimatedImageLayersEnabled, |
|
525 "layout.animated-image-layers.enabled", |
|
526 false); |
|
527 } |
|
528 |
|
529 return sAnimatedImageLayersEnabled; |
|
530 } |
|
531 |
|
532 bool |
|
533 nsLayoutUtils::CSSFiltersEnabled() |
|
534 { |
|
535 static bool sCSSFiltersEnabled; |
|
536 static bool sCSSFiltersPrefCached = false; |
|
537 |
|
538 if (!sCSSFiltersPrefCached) { |
|
539 sCSSFiltersPrefCached = true; |
|
540 Preferences::AddBoolVarCache(&sCSSFiltersEnabled, |
|
541 "layout.css.filters.enabled", |
|
542 false); |
|
543 } |
|
544 |
|
545 return sCSSFiltersEnabled; |
|
546 } |
|
547 |
|
548 bool |
|
549 nsLayoutUtils::UnsetValueEnabled() |
|
550 { |
|
551 static bool sUnsetValueEnabled; |
|
552 static bool sUnsetValuePrefCached = false; |
|
553 |
|
554 if (!sUnsetValuePrefCached) { |
|
555 sUnsetValuePrefCached = true; |
|
556 Preferences::AddBoolVarCache(&sUnsetValueEnabled, |
|
557 "layout.css.unset-value.enabled", |
|
558 false); |
|
559 } |
|
560 |
|
561 return sUnsetValueEnabled; |
|
562 } |
|
563 |
|
564 bool |
|
565 nsLayoutUtils::IsTextAlignTrueValueEnabled() |
|
566 { |
|
567 static bool sTextAlignTrueValueEnabled; |
|
568 static bool sTextAlignTrueValueEnabledPrefCached = false; |
|
569 |
|
570 if (!sTextAlignTrueValueEnabledPrefCached) { |
|
571 sTextAlignTrueValueEnabledPrefCached = true; |
|
572 Preferences::AddBoolVarCache(&sTextAlignTrueValueEnabled, |
|
573 TEXT_ALIGN_TRUE_ENABLED_PREF_NAME, |
|
574 false); |
|
575 } |
|
576 |
|
577 return sTextAlignTrueValueEnabled; |
|
578 } |
|
579 |
|
580 void |
|
581 nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame, |
|
582 nsOverflowAreas& aOverflowAreas, |
|
583 FrameChildListIDs aSkipChildLists) |
|
584 { |
|
585 // Iterate over all children except pop-ups. |
|
586 FrameChildListIDs skip = aSkipChildLists | |
|
587 nsIFrame::kSelectPopupList | nsIFrame::kPopupList; |
|
588 for (nsIFrame::ChildListIterator childLists(aFrame); |
|
589 !childLists.IsDone(); childLists.Next()) { |
|
590 if (skip.Contains(childLists.CurrentID())) { |
|
591 continue; |
|
592 } |
|
593 |
|
594 nsFrameList children = childLists.CurrentList(); |
|
595 for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { |
|
596 nsIFrame* child = e.get(); |
|
597 nsOverflowAreas childOverflow = |
|
598 child->GetOverflowAreas() + child->GetPosition(); |
|
599 aOverflowAreas.UnionWith(childOverflow); |
|
600 } |
|
601 } |
|
602 } |
|
603 |
|
604 static void DestroyViewID(void* aObject, nsIAtom* aPropertyName, |
|
605 void* aPropertyValue, void* aData) |
|
606 { |
|
607 ViewID* id = static_cast<ViewID*>(aPropertyValue); |
|
608 GetContentMap().Remove(*id); |
|
609 delete id; |
|
610 } |
|
611 |
|
612 /** |
|
613 * A namespace class for static layout utilities. |
|
614 */ |
|
615 |
|
616 bool |
|
617 nsLayoutUtils::FindIDFor(const nsIContent* aContent, ViewID* aOutViewId) |
|
618 { |
|
619 void* scrollIdProperty = aContent->GetProperty(nsGkAtoms::RemoteId); |
|
620 if (scrollIdProperty) { |
|
621 *aOutViewId = *static_cast<ViewID*>(scrollIdProperty); |
|
622 return true; |
|
623 } |
|
624 return false; |
|
625 } |
|
626 |
|
627 ViewID |
|
628 nsLayoutUtils::FindOrCreateIDFor(nsIContent* aContent) |
|
629 { |
|
630 ViewID scrollId; |
|
631 |
|
632 if (!FindIDFor(aContent, &scrollId)) { |
|
633 scrollId = sScrollIdCounter++; |
|
634 aContent->SetProperty(nsGkAtoms::RemoteId, new ViewID(scrollId), |
|
635 DestroyViewID); |
|
636 GetContentMap().Put(scrollId, aContent); |
|
637 } |
|
638 |
|
639 return scrollId; |
|
640 } |
|
641 |
|
642 nsIContent* |
|
643 nsLayoutUtils::FindContentFor(ViewID aId) |
|
644 { |
|
645 NS_ABORT_IF_FALSE(aId != FrameMetrics::NULL_SCROLL_ID, |
|
646 "Cannot find a content element in map for null IDs."); |
|
647 nsIContent* content; |
|
648 bool exists = GetContentMap().Get(aId, &content); |
|
649 |
|
650 if (exists) { |
|
651 return content; |
|
652 } else { |
|
653 return nullptr; |
|
654 } |
|
655 } |
|
656 |
|
657 nsIScrollableFrame* |
|
658 nsLayoutUtils::FindScrollableFrameFor(ViewID aId) |
|
659 { |
|
660 nsIContent* content = FindContentFor(aId); |
|
661 if (!content) { |
|
662 return nullptr; |
|
663 } |
|
664 |
|
665 nsIFrame* scrolledFrame = content->GetPrimaryFrame(); |
|
666 if (scrolledFrame && content->OwnerDoc()->GetRootElement() == content) { |
|
667 // The content is the root element of a subdocument, so return the root scrollable |
|
668 // for the subdocument. |
|
669 scrolledFrame = scrolledFrame->PresContext()->PresShell()->GetRootScrollFrame(); |
|
670 } |
|
671 return scrolledFrame ? scrolledFrame->GetScrollTargetFrame() : nullptr; |
|
672 } |
|
673 |
|
674 bool |
|
675 nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult) |
|
676 { |
|
677 DisplayPortPropertyData* rectData = |
|
678 static_cast<DisplayPortPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPort)); |
|
679 DisplayPortMarginsPropertyData* marginsData = |
|
680 static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins)); |
|
681 if (!rectData && !marginsData) { |
|
682 return false; |
|
683 } |
|
684 |
|
685 if (aResult) { |
|
686 if (rectData && marginsData) { |
|
687 // choose margins if equal priority |
|
688 if (rectData->mPriority > marginsData->mPriority) { |
|
689 marginsData = nullptr; |
|
690 } else { |
|
691 rectData = nullptr; |
|
692 } |
|
693 } |
|
694 |
|
695 if (rectData) { |
|
696 *aResult = rectData->mRect; |
|
697 } else { |
|
698 nsRect* baseData = |
|
699 static_cast<nsRect*>(aContent->GetProperty(nsGkAtoms::DisplayPortBase)); |
|
700 nsRect base; |
|
701 if (baseData) { |
|
702 base = *baseData; |
|
703 } |
|
704 |
|
705 nsIFrame* frame = aContent->GetPrimaryFrame(); |
|
706 if (frame) { |
|
707 bool isRoot = false; |
|
708 if (aContent->OwnerDoc()->GetRootElement() == aContent) { |
|
709 // We want the scroll frame, the root scroll frame differs from all |
|
710 // others in that the primary frame is not the scroll frame. |
|
711 frame = frame->PresContext()->PresShell()->GetRootScrollFrame(); |
|
712 isRoot = true; |
|
713 } |
|
714 |
|
715 // first convert the base rect to layer pixels |
|
716 nsPresContext* presContext = frame->PresContext(); |
|
717 int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel(); |
|
718 gfxSize res = presContext->PresShell()->GetCumulativeResolution(); |
|
719 gfxSize parentRes = res; |
|
720 if (isRoot) { |
|
721 // the base rect for root scroll frames is specified in the parent document |
|
722 // coordinate space, so it doesn't include the local resolution. |
|
723 gfxSize localRes = presContext->PresShell()->GetResolution(); |
|
724 parentRes.width /= localRes.width; |
|
725 parentRes.height /= localRes.height; |
|
726 } |
|
727 LayerRect rect; |
|
728 rect.x = parentRes.width * NSAppUnitsToFloatPixels(base.x, auPerDevPixel); |
|
729 rect.y = parentRes.height * NSAppUnitsToFloatPixels(base.y, auPerDevPixel); |
|
730 rect.width = |
|
731 parentRes.width * NSAppUnitsToFloatPixels(base.width, auPerDevPixel); |
|
732 rect.height = |
|
733 parentRes.height * NSAppUnitsToFloatPixels(base.height, auPerDevPixel); |
|
734 |
|
735 rect.Inflate(marginsData->mMargins); |
|
736 |
|
737 nsIScrollableFrame* scrollableFrame = frame->GetScrollTargetFrame(); |
|
738 nsPoint scrollPos( |
|
739 scrollableFrame ? scrollableFrame->GetScrollPosition() : nsPoint(0,0)); |
|
740 if (marginsData->mAlignmentX > 0 || marginsData->mAlignmentY > 0) { |
|
741 // Avoid division by zero. |
|
742 if (marginsData->mAlignmentX == 0) { |
|
743 marginsData->mAlignmentX = 1; |
|
744 } |
|
745 if (marginsData->mAlignmentY == 0) { |
|
746 marginsData->mAlignmentY = 1; |
|
747 } |
|
748 |
|
749 LayerPoint scrollPosLayer( |
|
750 res.width * NSAppUnitsToFloatPixels(scrollPos.x, auPerDevPixel), |
|
751 res.height * NSAppUnitsToFloatPixels(scrollPos.y, auPerDevPixel)); |
|
752 rect += scrollPosLayer; |
|
753 |
|
754 // Inflate the rectangle by 1 so that we always push to the next tile |
|
755 // boundary. This is desirable to stop from having a rectangle with a |
|
756 // moving origin occasionally being smaller when it coincidentally lines |
|
757 // up to tile boundaries. |
|
758 rect.Inflate(1); |
|
759 |
|
760 float left = |
|
761 marginsData->mAlignmentX * floor(rect.x / marginsData->mAlignmentX); |
|
762 float top = |
|
763 marginsData->mAlignmentY * floor(rect.y / marginsData->mAlignmentY); |
|
764 float right = |
|
765 marginsData->mAlignmentX * ceil(rect.XMost() / marginsData->mAlignmentX); |
|
766 float bottom = |
|
767 marginsData->mAlignmentY * ceil(rect.YMost() / marginsData->mAlignmentY); |
|
768 rect = LayerRect(left, top, right - left, bottom - top); |
|
769 rect -= scrollPosLayer; |
|
770 } |
|
771 |
|
772 nsRect result; |
|
773 result.x = NSFloatPixelsToAppUnits(rect.x / res.width, auPerDevPixel); |
|
774 result.y = NSFloatPixelsToAppUnits(rect.y / res.height, auPerDevPixel); |
|
775 result.width = |
|
776 NSFloatPixelsToAppUnits(rect.width / res.width, auPerDevPixel); |
|
777 result.height = |
|
778 NSFloatPixelsToAppUnits(rect.height / res.height, auPerDevPixel); |
|
779 |
|
780 // Finally, clamp the display port to the expanded scrollable rect. |
|
781 nsRect expandedScrollableRect = CalculateExpandedScrollableRect(frame); |
|
782 result = expandedScrollableRect.Intersect(result + scrollPos) - scrollPos; |
|
783 |
|
784 *aResult = result; |
|
785 } |
|
786 } |
|
787 } |
|
788 |
|
789 return true; |
|
790 } |
|
791 |
|
792 void |
|
793 nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent, |
|
794 nsIPresShell* aPresShell, |
|
795 const LayerMargin& aMargins, |
|
796 uint32_t aAlignmentX, |
|
797 uint32_t aAlignmentY, |
|
798 uint32_t aPriority, |
|
799 RepaintMode aRepaintMode) |
|
800 { |
|
801 DisplayPortMarginsPropertyData* currentData = |
|
802 static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins)); |
|
803 if (currentData && currentData->mPriority > aPriority) { |
|
804 return; |
|
805 } |
|
806 |
|
807 aContent->SetProperty(nsGkAtoms::DisplayPortMargins, |
|
808 new DisplayPortMarginsPropertyData( |
|
809 aMargins, aAlignmentX, aAlignmentY, aPriority), |
|
810 nsINode::DeleteProperty<DisplayPortMarginsPropertyData>); |
|
811 |
|
812 nsIFrame* rootScrollFrame = aPresShell->GetRootScrollFrame(); |
|
813 if (rootScrollFrame && aContent == rootScrollFrame->GetContent()) { |
|
814 // We are setting a root displayport for a document. |
|
815 // The pres shell needs a special flag set. |
|
816 aPresShell->SetIgnoreViewportScrolling(true); |
|
817 } |
|
818 |
|
819 if (aRepaintMode == RepaintMode::Repaint) { |
|
820 nsIFrame* rootFrame = aPresShell->FrameManager()->GetRootFrame(); |
|
821 if (rootFrame) { |
|
822 rootFrame->SchedulePaint(); |
|
823 } |
|
824 } |
|
825 } |
|
826 |
|
827 void |
|
828 nsLayoutUtils::SetDisplayPortBase(nsIContent* aContent, const nsRect& aBase) |
|
829 { |
|
830 aContent->SetProperty(nsGkAtoms::DisplayPortBase, new nsRect(aBase), |
|
831 nsINode::DeleteProperty<nsRect>); |
|
832 } |
|
833 |
|
834 void |
|
835 nsLayoutUtils::SetDisplayPortBaseIfNotSet(nsIContent* aContent, const nsRect& aBase) |
|
836 { |
|
837 if (!aContent->GetProperty(nsGkAtoms::DisplayPortBase)) { |
|
838 SetDisplayPortBase(aContent, aBase); |
|
839 } |
|
840 } |
|
841 |
|
842 bool |
|
843 nsLayoutUtils::GetCriticalDisplayPort(nsIContent* aContent, nsRect* aResult) |
|
844 { |
|
845 void* property = aContent->GetProperty(nsGkAtoms::CriticalDisplayPort); |
|
846 if (!property) { |
|
847 return false; |
|
848 } |
|
849 |
|
850 if (aResult) { |
|
851 *aResult = *static_cast<nsRect*>(property); |
|
852 } |
|
853 return true; |
|
854 } |
|
855 |
|
856 nsIFrame* |
|
857 nsLayoutUtils::LastContinuationWithChild(nsIFrame* aFrame) |
|
858 { |
|
859 NS_PRECONDITION(aFrame, "NULL frame pointer"); |
|
860 aFrame = aFrame->LastContinuation(); |
|
861 while (!aFrame->GetFirstPrincipalChild() && |
|
862 aFrame->GetPrevContinuation()) { |
|
863 aFrame = aFrame->GetPrevContinuation(); |
|
864 } |
|
865 return aFrame; |
|
866 } |
|
867 |
|
868 /** |
|
869 * GetFirstChildFrame returns the first "real" child frame of a |
|
870 * given frame. It will descend down into pseudo-frames (unless the |
|
871 * pseudo-frame is the :before generated frame). |
|
872 * @param aFrame the frame |
|
873 * @param aFrame the frame's content node |
|
874 */ |
|
875 static nsIFrame* |
|
876 GetFirstChildFrame(nsIFrame* aFrame, |
|
877 nsIContent* aContent) |
|
878 { |
|
879 NS_PRECONDITION(aFrame, "NULL frame pointer"); |
|
880 |
|
881 // Get the first child frame |
|
882 nsIFrame* childFrame = aFrame->GetFirstPrincipalChild(); |
|
883 |
|
884 // If the child frame is a pseudo-frame, then return its first child. |
|
885 // Note that the frame we create for the generated content is also a |
|
886 // pseudo-frame and so don't drill down in that case |
|
887 if (childFrame && |
|
888 childFrame->IsPseudoFrame(aContent) && |
|
889 !childFrame->IsGeneratedContentFrame()) { |
|
890 return GetFirstChildFrame(childFrame, aContent); |
|
891 } |
|
892 |
|
893 return childFrame; |
|
894 } |
|
895 |
|
896 /** |
|
897 * GetLastChildFrame returns the last "real" child frame of a |
|
898 * given frame. It will descend down into pseudo-frames (unless the |
|
899 * pseudo-frame is the :after generated frame). |
|
900 * @param aFrame the frame |
|
901 * @param aFrame the frame's content node |
|
902 */ |
|
903 static nsIFrame* |
|
904 GetLastChildFrame(nsIFrame* aFrame, |
|
905 nsIContent* aContent) |
|
906 { |
|
907 NS_PRECONDITION(aFrame, "NULL frame pointer"); |
|
908 |
|
909 // Get the last continuation frame that's a parent |
|
910 nsIFrame* lastParentContinuation = |
|
911 nsLayoutUtils::LastContinuationWithChild(aFrame); |
|
912 nsIFrame* lastChildFrame = |
|
913 lastParentContinuation->GetLastChild(nsIFrame::kPrincipalList); |
|
914 if (lastChildFrame) { |
|
915 // Get the frame's first continuation. This matters in case the frame has |
|
916 // been continued across multiple lines or split by BiDi resolution. |
|
917 lastChildFrame = lastChildFrame->FirstContinuation(); |
|
918 |
|
919 // If the last child frame is a pseudo-frame, then return its last child. |
|
920 // Note that the frame we create for the generated content is also a |
|
921 // pseudo-frame and so don't drill down in that case |
|
922 if (lastChildFrame && |
|
923 lastChildFrame->IsPseudoFrame(aContent) && |
|
924 !lastChildFrame->IsGeneratedContentFrame()) { |
|
925 return GetLastChildFrame(lastChildFrame, aContent); |
|
926 } |
|
927 |
|
928 return lastChildFrame; |
|
929 } |
|
930 |
|
931 return nullptr; |
|
932 } |
|
933 |
|
934 //static |
|
935 FrameChildListID |
|
936 nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame) |
|
937 { |
|
938 nsIFrame::ChildListID id = nsIFrame::kPrincipalList; |
|
939 |
|
940 if (aChildFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { |
|
941 nsIFrame* pif = aChildFrame->GetPrevInFlow(); |
|
942 if (pif->GetParent() == aChildFrame->GetParent()) { |
|
943 id = nsIFrame::kExcessOverflowContainersList; |
|
944 } |
|
945 else { |
|
946 id = nsIFrame::kOverflowContainersList; |
|
947 } |
|
948 } |
|
949 // See if the frame is moved out of the flow |
|
950 else if (aChildFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { |
|
951 // Look at the style information to tell |
|
952 const nsStyleDisplay* disp = aChildFrame->StyleDisplay(); |
|
953 |
|
954 if (NS_STYLE_POSITION_ABSOLUTE == disp->mPosition) { |
|
955 id = nsIFrame::kAbsoluteList; |
|
956 } else if (NS_STYLE_POSITION_FIXED == disp->mPosition) { |
|
957 if (nsLayoutUtils::IsReallyFixedPos(aChildFrame)) { |
|
958 id = nsIFrame::kFixedList; |
|
959 } else { |
|
960 id = nsIFrame::kAbsoluteList; |
|
961 } |
|
962 #ifdef MOZ_XUL |
|
963 } else if (NS_STYLE_DISPLAY_POPUP == disp->mDisplay) { |
|
964 // Out-of-flows that are DISPLAY_POPUP must be kids of the root popup set |
|
965 #ifdef DEBUG |
|
966 nsIFrame* parent = aChildFrame->GetParent(); |
|
967 NS_ASSERTION(parent && parent->GetType() == nsGkAtoms::popupSetFrame, |
|
968 "Unexpected parent"); |
|
969 #endif // DEBUG |
|
970 |
|
971 id = nsIFrame::kPopupList; |
|
972 #endif // MOZ_XUL |
|
973 } else { |
|
974 NS_ASSERTION(aChildFrame->IsFloating(), "not a floated frame"); |
|
975 id = nsIFrame::kFloatList; |
|
976 } |
|
977 |
|
978 } else { |
|
979 nsIAtom* childType = aChildFrame->GetType(); |
|
980 if (nsGkAtoms::menuPopupFrame == childType) { |
|
981 nsIFrame* parent = aChildFrame->GetParent(); |
|
982 MOZ_ASSERT(parent, "nsMenuPopupFrame can't be the root frame"); |
|
983 if (parent) { |
|
984 if (parent->GetType() == nsGkAtoms::popupSetFrame) { |
|
985 id = nsIFrame::kPopupList; |
|
986 } else { |
|
987 nsIFrame* firstPopup = parent->GetFirstChild(nsIFrame::kPopupList); |
|
988 MOZ_ASSERT(!firstPopup || !firstPopup->GetNextSibling(), |
|
989 "We assume popupList only has one child, but it has more."); |
|
990 id = firstPopup == aChildFrame |
|
991 ? nsIFrame::kPopupList |
|
992 : nsIFrame::kPrincipalList; |
|
993 } |
|
994 } else { |
|
995 id = nsIFrame::kPrincipalList; |
|
996 } |
|
997 } else if (nsGkAtoms::tableColGroupFrame == childType) { |
|
998 id = nsIFrame::kColGroupList; |
|
999 } else if (nsGkAtoms::tableCaptionFrame == childType) { |
|
1000 id = nsIFrame::kCaptionList; |
|
1001 } else { |
|
1002 id = nsIFrame::kPrincipalList; |
|
1003 } |
|
1004 } |
|
1005 |
|
1006 #ifdef DEBUG |
|
1007 // Verify that the frame is actually in that child list or in the |
|
1008 // corresponding overflow list. |
|
1009 nsIFrame* parent = aChildFrame->GetParent(); |
|
1010 bool found = parent->GetChildList(id).ContainsFrame(aChildFrame); |
|
1011 if (!found) { |
|
1012 if (!(aChildFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { |
|
1013 found = parent->GetChildList(nsIFrame::kOverflowList) |
|
1014 .ContainsFrame(aChildFrame); |
|
1015 } |
|
1016 else if (aChildFrame->IsFloating()) { |
|
1017 found = parent->GetChildList(nsIFrame::kOverflowOutOfFlowList) |
|
1018 .ContainsFrame(aChildFrame); |
|
1019 if (!found) { |
|
1020 found = parent->GetChildList(nsIFrame::kPushedFloatsList) |
|
1021 .ContainsFrame(aChildFrame); |
|
1022 } |
|
1023 } |
|
1024 // else it's positioned and should have been on the 'id' child list. |
|
1025 NS_POSTCONDITION(found, "not in child list"); |
|
1026 } |
|
1027 #endif |
|
1028 |
|
1029 return id; |
|
1030 } |
|
1031 |
|
1032 // static |
|
1033 nsIFrame* |
|
1034 nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame) |
|
1035 { |
|
1036 NS_PRECONDITION(aFrame, "NULL frame pointer"); |
|
1037 NS_ASSERTION(!aFrame->GetPrevContinuation(), |
|
1038 "aFrame must be first continuation"); |
|
1039 |
|
1040 nsIFrame* cif = aFrame->GetContentInsertionFrame(); |
|
1041 nsIFrame* firstFrame = GetFirstChildFrame(cif, aFrame->GetContent()); |
|
1042 |
|
1043 if (firstFrame && IsGeneratedContentFor(nullptr, firstFrame, |
|
1044 nsCSSPseudoElements::before)) { |
|
1045 return firstFrame; |
|
1046 } |
|
1047 |
|
1048 return nullptr; |
|
1049 } |
|
1050 |
|
1051 // static |
|
1052 nsIFrame* |
|
1053 nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame) |
|
1054 { |
|
1055 NS_PRECONDITION(aFrame, "NULL frame pointer"); |
|
1056 |
|
1057 nsIFrame* cif = aFrame->GetContentInsertionFrame(); |
|
1058 nsIFrame* lastFrame = GetLastChildFrame(cif, aFrame->GetContent()); |
|
1059 |
|
1060 if (lastFrame && IsGeneratedContentFor(nullptr, lastFrame, |
|
1061 nsCSSPseudoElements::after)) { |
|
1062 return lastFrame; |
|
1063 } |
|
1064 |
|
1065 return nullptr; |
|
1066 } |
|
1067 |
|
1068 // static |
|
1069 nsIFrame* |
|
1070 nsLayoutUtils::GetClosestFrameOfType(nsIFrame* aFrame, nsIAtom* aFrameType) |
|
1071 { |
|
1072 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) { |
|
1073 if (frame->GetType() == aFrameType) { |
|
1074 return frame; |
|
1075 } |
|
1076 } |
|
1077 return nullptr; |
|
1078 } |
|
1079 |
|
1080 // static |
|
1081 nsIFrame* |
|
1082 nsLayoutUtils::GetStyleFrame(nsIFrame* aFrame) |
|
1083 { |
|
1084 if (aFrame->GetType() == nsGkAtoms::tableOuterFrame) { |
|
1085 nsIFrame* inner = aFrame->GetFirstPrincipalChild(); |
|
1086 NS_ASSERTION(inner, "Outer table must have an inner"); |
|
1087 return inner; |
|
1088 } |
|
1089 |
|
1090 return aFrame; |
|
1091 } |
|
1092 |
|
1093 nsIFrame* |
|
1094 nsLayoutUtils::GetStyleFrame(const nsIContent* aContent) |
|
1095 { |
|
1096 nsIFrame *frame = aContent->GetPrimaryFrame(); |
|
1097 if (!frame) { |
|
1098 return nullptr; |
|
1099 } |
|
1100 |
|
1101 return nsLayoutUtils::GetStyleFrame(frame); |
|
1102 } |
|
1103 |
|
1104 nsIFrame* |
|
1105 nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) { |
|
1106 NS_ASSERTION(nsGkAtoms::placeholderFrame == aFrame->GetType(), |
|
1107 "Must have a placeholder here"); |
|
1108 if (aFrame->GetStateBits() & PLACEHOLDER_FOR_FLOAT) { |
|
1109 nsIFrame *outOfFlowFrame = |
|
1110 nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame); |
|
1111 NS_ASSERTION(outOfFlowFrame->IsFloating(), |
|
1112 "How did that happen?"); |
|
1113 return outOfFlowFrame; |
|
1114 } |
|
1115 |
|
1116 return nullptr; |
|
1117 } |
|
1118 |
|
1119 // static |
|
1120 bool |
|
1121 nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent, |
|
1122 nsIFrame* aFrame, |
|
1123 nsIAtom* aPseudoElement) |
|
1124 { |
|
1125 NS_PRECONDITION(aFrame, "Must have a frame"); |
|
1126 NS_PRECONDITION(aPseudoElement, "Must have a pseudo name"); |
|
1127 |
|
1128 if (!aFrame->IsGeneratedContentFrame()) { |
|
1129 return false; |
|
1130 } |
|
1131 nsIFrame* parent = aFrame->GetParent(); |
|
1132 NS_ASSERTION(parent, "Generated content can't be root frame"); |
|
1133 if (parent->IsGeneratedContentFrame()) { |
|
1134 // Not the root of the generated content |
|
1135 return false; |
|
1136 } |
|
1137 |
|
1138 if (aContent && parent->GetContent() != aContent) { |
|
1139 return false; |
|
1140 } |
|
1141 |
|
1142 return (aFrame->GetContent()->Tag() == nsGkAtoms::mozgeneratedcontentbefore) == |
|
1143 (aPseudoElement == nsCSSPseudoElements::before); |
|
1144 } |
|
1145 |
|
1146 // static |
|
1147 nsIFrame* |
|
1148 nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame, |
|
1149 nsPoint* aExtraOffset) |
|
1150 { |
|
1151 nsIFrame* p = aFrame->GetParent(); |
|
1152 if (p) |
|
1153 return p; |
|
1154 |
|
1155 nsView* v = aFrame->GetView(); |
|
1156 if (!v) |
|
1157 return nullptr; |
|
1158 v = v->GetParent(); // anonymous inner view |
|
1159 if (!v) |
|
1160 return nullptr; |
|
1161 if (aExtraOffset) { |
|
1162 *aExtraOffset += v->GetPosition(); |
|
1163 } |
|
1164 v = v->GetParent(); // subdocumentframe's view |
|
1165 return v ? v->GetFrame() : nullptr; |
|
1166 } |
|
1167 |
|
1168 // static |
|
1169 bool |
|
1170 nsLayoutUtils::IsProperAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame, |
|
1171 nsIFrame* aCommonAncestor) |
|
1172 { |
|
1173 if (aFrame == aAncestorFrame) |
|
1174 return false; |
|
1175 return IsAncestorFrameCrossDoc(aAncestorFrame, aFrame, aCommonAncestor); |
|
1176 } |
|
1177 |
|
1178 // static |
|
1179 bool |
|
1180 nsLayoutUtils::IsAncestorFrameCrossDoc(const nsIFrame* aAncestorFrame, const nsIFrame* aFrame, |
|
1181 const nsIFrame* aCommonAncestor) |
|
1182 { |
|
1183 for (const nsIFrame* f = aFrame; f != aCommonAncestor; |
|
1184 f = GetCrossDocParentFrame(f)) { |
|
1185 if (f == aAncestorFrame) |
|
1186 return true; |
|
1187 } |
|
1188 return aCommonAncestor == aAncestorFrame; |
|
1189 } |
|
1190 |
|
1191 // static |
|
1192 bool |
|
1193 nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame, |
|
1194 nsIFrame* aCommonAncestor) |
|
1195 { |
|
1196 if (aFrame == aAncestorFrame) |
|
1197 return false; |
|
1198 for (nsIFrame* f = aFrame; f != aCommonAncestor; f = f->GetParent()) { |
|
1199 if (f == aAncestorFrame) |
|
1200 return true; |
|
1201 } |
|
1202 return aCommonAncestor == aAncestorFrame; |
|
1203 } |
|
1204 |
|
1205 // static |
|
1206 int32_t |
|
1207 nsLayoutUtils::DoCompareTreePosition(nsIContent* aContent1, |
|
1208 nsIContent* aContent2, |
|
1209 int32_t aIf1Ancestor, |
|
1210 int32_t aIf2Ancestor, |
|
1211 const nsIContent* aCommonAncestor) |
|
1212 { |
|
1213 NS_PRECONDITION(aContent1, "aContent1 must not be null"); |
|
1214 NS_PRECONDITION(aContent2, "aContent2 must not be null"); |
|
1215 |
|
1216 nsAutoTArray<nsINode*, 32> content1Ancestors; |
|
1217 nsINode* c1; |
|
1218 for (c1 = aContent1; c1 && c1 != aCommonAncestor; c1 = c1->GetParentNode()) { |
|
1219 content1Ancestors.AppendElement(c1); |
|
1220 } |
|
1221 if (!c1 && aCommonAncestor) { |
|
1222 // So, it turns out aCommonAncestor was not an ancestor of c1. Oops. |
|
1223 // Never mind. We can continue as if aCommonAncestor was null. |
|
1224 aCommonAncestor = nullptr; |
|
1225 } |
|
1226 |
|
1227 nsAutoTArray<nsINode*, 32> content2Ancestors; |
|
1228 nsINode* c2; |
|
1229 for (c2 = aContent2; c2 && c2 != aCommonAncestor; c2 = c2->GetParentNode()) { |
|
1230 content2Ancestors.AppendElement(c2); |
|
1231 } |
|
1232 if (!c2 && aCommonAncestor) { |
|
1233 // So, it turns out aCommonAncestor was not an ancestor of c2. |
|
1234 // We need to retry with no common ancestor hint. |
|
1235 return DoCompareTreePosition(aContent1, aContent2, |
|
1236 aIf1Ancestor, aIf2Ancestor, nullptr); |
|
1237 } |
|
1238 |
|
1239 int last1 = content1Ancestors.Length() - 1; |
|
1240 int last2 = content2Ancestors.Length() - 1; |
|
1241 nsINode* content1Ancestor = nullptr; |
|
1242 nsINode* content2Ancestor = nullptr; |
|
1243 while (last1 >= 0 && last2 >= 0 |
|
1244 && ((content1Ancestor = content1Ancestors.ElementAt(last1)) == |
|
1245 (content2Ancestor = content2Ancestors.ElementAt(last2)))) { |
|
1246 last1--; |
|
1247 last2--; |
|
1248 } |
|
1249 |
|
1250 if (last1 < 0) { |
|
1251 if (last2 < 0) { |
|
1252 NS_ASSERTION(aContent1 == aContent2, "internal error?"); |
|
1253 return 0; |
|
1254 } |
|
1255 // aContent1 is an ancestor of aContent2 |
|
1256 return aIf1Ancestor; |
|
1257 } |
|
1258 |
|
1259 if (last2 < 0) { |
|
1260 // aContent2 is an ancestor of aContent1 |
|
1261 return aIf2Ancestor; |
|
1262 } |
|
1263 |
|
1264 // content1Ancestor != content2Ancestor, so they must be siblings with the same parent |
|
1265 nsINode* parent = content1Ancestor->GetParentNode(); |
|
1266 #ifdef DEBUG |
|
1267 // TODO: remove the uglyness, see bug 598468. |
|
1268 NS_ASSERTION(gPreventAssertInCompareTreePosition || parent, |
|
1269 "no common ancestor at all???"); |
|
1270 #endif // DEBUG |
|
1271 if (!parent) { // different documents?? |
|
1272 return 0; |
|
1273 } |
|
1274 |
|
1275 int32_t index1 = parent->IndexOf(content1Ancestor); |
|
1276 int32_t index2 = parent->IndexOf(content2Ancestor); |
|
1277 if (index1 < 0 || index2 < 0) { |
|
1278 // one of them must be anonymous; we can't determine the order |
|
1279 return 0; |
|
1280 } |
|
1281 |
|
1282 return index1 - index2; |
|
1283 } |
|
1284 |
|
1285 // static |
|
1286 nsIFrame* |
|
1287 nsLayoutUtils::FillAncestors(nsIFrame* aFrame, |
|
1288 nsIFrame* aStopAtAncestor, |
|
1289 nsTArray<nsIFrame*>* aAncestors) |
|
1290 { |
|
1291 while (aFrame && aFrame != aStopAtAncestor) { |
|
1292 aAncestors->AppendElement(aFrame); |
|
1293 aFrame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame); |
|
1294 } |
|
1295 return aFrame; |
|
1296 } |
|
1297 |
|
1298 // Return true if aFrame1 is after aFrame2 |
|
1299 static bool IsFrameAfter(nsIFrame* aFrame1, nsIFrame* aFrame2) |
|
1300 { |
|
1301 nsIFrame* f = aFrame2; |
|
1302 do { |
|
1303 f = f->GetNextSibling(); |
|
1304 if (f == aFrame1) |
|
1305 return true; |
|
1306 } while (f); |
|
1307 return false; |
|
1308 } |
|
1309 |
|
1310 // static |
|
1311 int32_t |
|
1312 nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1, |
|
1313 nsIFrame* aFrame2, |
|
1314 int32_t aIf1Ancestor, |
|
1315 int32_t aIf2Ancestor, |
|
1316 nsIFrame* aCommonAncestor) |
|
1317 { |
|
1318 NS_PRECONDITION(aFrame1, "aFrame1 must not be null"); |
|
1319 NS_PRECONDITION(aFrame2, "aFrame2 must not be null"); |
|
1320 |
|
1321 nsAutoTArray<nsIFrame*,20> frame2Ancestors; |
|
1322 nsIFrame* nonCommonAncestor = |
|
1323 FillAncestors(aFrame2, aCommonAncestor, &frame2Ancestors); |
|
1324 |
|
1325 return DoCompareTreePosition(aFrame1, aFrame2, frame2Ancestors, |
|
1326 aIf1Ancestor, aIf2Ancestor, |
|
1327 nonCommonAncestor ? aCommonAncestor : nullptr); |
|
1328 } |
|
1329 |
|
1330 // static |
|
1331 int32_t |
|
1332 nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1, |
|
1333 nsIFrame* aFrame2, |
|
1334 nsTArray<nsIFrame*>& aFrame2Ancestors, |
|
1335 int32_t aIf1Ancestor, |
|
1336 int32_t aIf2Ancestor, |
|
1337 nsIFrame* aCommonAncestor) |
|
1338 { |
|
1339 NS_PRECONDITION(aFrame1, "aFrame1 must not be null"); |
|
1340 NS_PRECONDITION(aFrame2, "aFrame2 must not be null"); |
|
1341 |
|
1342 nsPresContext* presContext = aFrame1->PresContext(); |
|
1343 if (presContext != aFrame2->PresContext()) { |
|
1344 NS_ERROR("no common ancestor at all, different documents"); |
|
1345 return 0; |
|
1346 } |
|
1347 |
|
1348 nsAutoTArray<nsIFrame*,20> frame1Ancestors; |
|
1349 if (aCommonAncestor && |
|
1350 !FillAncestors(aFrame1, aCommonAncestor, &frame1Ancestors)) { |
|
1351 // We reached the root of the frame tree ... if aCommonAncestor was set, |
|
1352 // it is wrong |
|
1353 return DoCompareTreePosition(aFrame1, aFrame2, |
|
1354 aIf1Ancestor, aIf2Ancestor, nullptr); |
|
1355 } |
|
1356 |
|
1357 int32_t last1 = int32_t(frame1Ancestors.Length()) - 1; |
|
1358 int32_t last2 = int32_t(aFrame2Ancestors.Length()) - 1; |
|
1359 while (last1 >= 0 && last2 >= 0 && |
|
1360 frame1Ancestors[last1] == aFrame2Ancestors[last2]) { |
|
1361 last1--; |
|
1362 last2--; |
|
1363 } |
|
1364 |
|
1365 if (last1 < 0) { |
|
1366 if (last2 < 0) { |
|
1367 NS_ASSERTION(aFrame1 == aFrame2, "internal error?"); |
|
1368 return 0; |
|
1369 } |
|
1370 // aFrame1 is an ancestor of aFrame2 |
|
1371 return aIf1Ancestor; |
|
1372 } |
|
1373 |
|
1374 if (last2 < 0) { |
|
1375 // aFrame2 is an ancestor of aFrame1 |
|
1376 return aIf2Ancestor; |
|
1377 } |
|
1378 |
|
1379 nsIFrame* ancestor1 = frame1Ancestors[last1]; |
|
1380 nsIFrame* ancestor2 = aFrame2Ancestors[last2]; |
|
1381 // Now we should be able to walk sibling chains to find which one is first |
|
1382 if (IsFrameAfter(ancestor2, ancestor1)) |
|
1383 return -1; |
|
1384 if (IsFrameAfter(ancestor1, ancestor2)) |
|
1385 return 1; |
|
1386 NS_WARNING("Frames were in different child lists???"); |
|
1387 return 0; |
|
1388 } |
|
1389 |
|
1390 // static |
|
1391 nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) { |
|
1392 if (!aFrame) { |
|
1393 return nullptr; |
|
1394 } |
|
1395 |
|
1396 nsIFrame* next; |
|
1397 while ((next = aFrame->GetNextSibling()) != nullptr) { |
|
1398 aFrame = next; |
|
1399 } |
|
1400 return aFrame; |
|
1401 } |
|
1402 |
|
1403 // static |
|
1404 nsView* |
|
1405 nsLayoutUtils::FindSiblingViewFor(nsView* aParentView, nsIFrame* aFrame) { |
|
1406 nsIFrame* parentViewFrame = aParentView->GetFrame(); |
|
1407 nsIContent* parentViewContent = parentViewFrame ? parentViewFrame->GetContent() : nullptr; |
|
1408 for (nsView* insertBefore = aParentView->GetFirstChild(); insertBefore; |
|
1409 insertBefore = insertBefore->GetNextSibling()) { |
|
1410 nsIFrame* f = insertBefore->GetFrame(); |
|
1411 if (!f) { |
|
1412 // this view could be some anonymous view attached to a meaningful parent |
|
1413 for (nsView* searchView = insertBefore->GetParent(); searchView; |
|
1414 searchView = searchView->GetParent()) { |
|
1415 f = searchView->GetFrame(); |
|
1416 if (f) { |
|
1417 break; |
|
1418 } |
|
1419 } |
|
1420 NS_ASSERTION(f, "Can't find a frame anywhere!"); |
|
1421 } |
|
1422 if (!f || !aFrame->GetContent() || !f->GetContent() || |
|
1423 CompareTreePosition(aFrame->GetContent(), f->GetContent(), parentViewContent) > 0) { |
|
1424 // aFrame's content is after f's content (or we just don't know), |
|
1425 // so put our view before f's view |
|
1426 return insertBefore; |
|
1427 } |
|
1428 } |
|
1429 return nullptr; |
|
1430 } |
|
1431 |
|
1432 //static |
|
1433 nsIScrollableFrame* |
|
1434 nsLayoutUtils::GetScrollableFrameFor(const nsIFrame *aScrolledFrame) |
|
1435 { |
|
1436 nsIFrame *frame = aScrolledFrame->GetParent(); |
|
1437 nsIScrollableFrame *sf = do_QueryFrame(frame); |
|
1438 return sf; |
|
1439 } |
|
1440 |
|
1441 /* static */ void |
|
1442 nsLayoutUtils::SetFixedPositionLayerData(Layer* aLayer, |
|
1443 const nsIFrame* aViewportFrame, |
|
1444 const nsRect& aAnchorRect, |
|
1445 const nsIFrame* aFixedPosFrame, |
|
1446 nsPresContext* aPresContext, |
|
1447 const ContainerLayerParameters& aContainerParameters) { |
|
1448 // Find out the rect of the viewport frame relative to the reference frame. |
|
1449 // This, in conjunction with the container scale, will correspond to the |
|
1450 // coordinate-space of the built layer. |
|
1451 float factor = aPresContext->AppUnitsPerDevPixel(); |
|
1452 Rect anchorRect(NSAppUnitsToFloatPixels(aAnchorRect.x, factor) * |
|
1453 aContainerParameters.mXScale, |
|
1454 NSAppUnitsToFloatPixels(aAnchorRect.y, factor) * |
|
1455 aContainerParameters.mYScale, |
|
1456 NSAppUnitsToFloatPixels(aAnchorRect.width, factor) * |
|
1457 aContainerParameters.mXScale, |
|
1458 NSAppUnitsToFloatPixels(aAnchorRect.height, factor) * |
|
1459 aContainerParameters.mYScale); |
|
1460 // Need to transform anchorRect from the container layer's coordinate system |
|
1461 // into aLayer's coordinate system. |
|
1462 Matrix transform2d; |
|
1463 if (aLayer->GetTransform().Is2D(&transform2d)) { |
|
1464 transform2d.Invert(); |
|
1465 anchorRect = transform2d.TransformBounds(anchorRect); |
|
1466 } else { |
|
1467 NS_ERROR("3D transform found between fixedpos content and its viewport (should never happen)"); |
|
1468 anchorRect = Rect(0,0,0,0); |
|
1469 } |
|
1470 |
|
1471 // Work out the anchor point for this fixed position layer. We assume that |
|
1472 // any positioning set (left/top/right/bottom) indicates that the |
|
1473 // corresponding side of its container should be the anchor point, |
|
1474 // defaulting to top-left. |
|
1475 LayerPoint anchor(anchorRect.x, anchorRect.y); |
|
1476 // Make sure the layer is aware of any fixed position margins that have |
|
1477 // been set. |
|
1478 nsMargin fixedMargins = aPresContext->PresShell()->GetContentDocumentFixedPositionMargins(); |
|
1479 LayerMargin fixedLayerMargins(NSAppUnitsToFloatPixels(fixedMargins.top, factor) * |
|
1480 aContainerParameters.mYScale, |
|
1481 NSAppUnitsToFloatPixels(fixedMargins.right, factor) * |
|
1482 aContainerParameters.mXScale, |
|
1483 NSAppUnitsToFloatPixels(fixedMargins.bottom, factor) * |
|
1484 aContainerParameters.mYScale, |
|
1485 NSAppUnitsToFloatPixels(fixedMargins.left, factor) * |
|
1486 aContainerParameters.mXScale); |
|
1487 |
|
1488 if (aFixedPosFrame != aViewportFrame) { |
|
1489 const nsStylePosition* position = aFixedPosFrame->StylePosition(); |
|
1490 if (position->mOffset.GetRightUnit() != eStyleUnit_Auto) { |
|
1491 if (position->mOffset.GetLeftUnit() != eStyleUnit_Auto) { |
|
1492 anchor.x = anchorRect.x + anchorRect.width / 2.f; |
|
1493 } else { |
|
1494 anchor.x = anchorRect.XMost(); |
|
1495 } |
|
1496 } |
|
1497 if (position->mOffset.GetBottomUnit() != eStyleUnit_Auto) { |
|
1498 if (position->mOffset.GetTopUnit() != eStyleUnit_Auto) { |
|
1499 anchor.y = anchorRect.y + anchorRect.height / 2.f; |
|
1500 } else { |
|
1501 anchor.y = anchorRect.YMost(); |
|
1502 } |
|
1503 } |
|
1504 |
|
1505 // If the frame is auto-positioned on either axis, set the top/left layer |
|
1506 // margins to -1, to indicate to the compositor that this layer is |
|
1507 // unaffected by fixed margins. |
|
1508 if (position->mOffset.GetLeftUnit() == eStyleUnit_Auto && |
|
1509 position->mOffset.GetRightUnit() == eStyleUnit_Auto) { |
|
1510 fixedLayerMargins.left = -1; |
|
1511 } |
|
1512 if (position->mOffset.GetTopUnit() == eStyleUnit_Auto && |
|
1513 position->mOffset.GetBottomUnit() == eStyleUnit_Auto) { |
|
1514 fixedLayerMargins.top = -1; |
|
1515 } |
|
1516 } |
|
1517 |
|
1518 aLayer->SetFixedPositionAnchor(anchor); |
|
1519 aLayer->SetFixedPositionMargins(fixedLayerMargins); |
|
1520 } |
|
1521 |
|
1522 bool |
|
1523 nsLayoutUtils::ViewportHasDisplayPort(nsPresContext* aPresContext, nsRect* aDisplayPort) |
|
1524 { |
|
1525 nsIFrame* rootScrollFrame = |
|
1526 aPresContext->PresShell()->GetRootScrollFrame(); |
|
1527 return rootScrollFrame && |
|
1528 nsLayoutUtils::GetDisplayPort(rootScrollFrame->GetContent(), aDisplayPort); |
|
1529 } |
|
1530 |
|
1531 bool |
|
1532 nsLayoutUtils::IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame, nsRect* aDisplayPort) |
|
1533 { |
|
1534 // Fixed-pos frames are parented by the viewport frame or the page content frame. |
|
1535 // We'll assume that printing/print preview don't have displayports for their |
|
1536 // pages! |
|
1537 nsIFrame* parent = aFrame->GetParent(); |
|
1538 if (!parent || parent->GetParent() || |
|
1539 aFrame->StyleDisplay()->mPosition != NS_STYLE_POSITION_FIXED) { |
|
1540 return false; |
|
1541 } |
|
1542 return ViewportHasDisplayPort(aFrame->PresContext(), aDisplayPort); |
|
1543 } |
|
1544 |
|
1545 static nsIFrame* |
|
1546 GetAnimatedGeometryRootForFrame(nsIFrame* aFrame, |
|
1547 const nsIFrame* aStopAtAncestor) |
|
1548 { |
|
1549 nsIFrame* f = aFrame; |
|
1550 nsIFrame* stickyFrame = nullptr; |
|
1551 while (f != aStopAtAncestor) { |
|
1552 if (nsLayoutUtils::IsPopup(f)) |
|
1553 break; |
|
1554 if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(f)) |
|
1555 break; |
|
1556 if (!f->GetParent() && |
|
1557 nsLayoutUtils::ViewportHasDisplayPort(f->PresContext())) { |
|
1558 // Viewport frames in a display port need to be animated geometry roots |
|
1559 // for background-attachment:fixed elements. |
|
1560 break; |
|
1561 } |
|
1562 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(f); |
|
1563 if (!parent) |
|
1564 break; |
|
1565 nsIAtom* parentType = parent->GetType(); |
|
1566 #ifdef ANDROID |
|
1567 // Treat the slider thumb as being as an active scrolled root |
|
1568 // on mobile so that it can move without repainting. |
|
1569 if (parentType == nsGkAtoms::sliderFrame) |
|
1570 break; |
|
1571 #endif |
|
1572 // Sticky frames are active if their nearest scrollable frame |
|
1573 // is also active, just keep a record of sticky frames that we |
|
1574 // encounter for now. |
|
1575 if (f->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY && |
|
1576 !stickyFrame) { |
|
1577 stickyFrame = f; |
|
1578 } |
|
1579 if (parentType == nsGkAtoms::scrollFrame) { |
|
1580 nsIScrollableFrame* sf = do_QueryFrame(parent); |
|
1581 if (sf->IsScrollingActive() && sf->GetScrolledFrame() == f) { |
|
1582 // If we found a sticky frame inside this active scroll frame, |
|
1583 // then use that. Otherwise use the scroll frame. |
|
1584 if (stickyFrame) { |
|
1585 return stickyFrame; |
|
1586 } |
|
1587 return f; |
|
1588 } else { |
|
1589 stickyFrame = nullptr; |
|
1590 } |
|
1591 } |
|
1592 // Fixed-pos frames are parented by the viewport frame, which has no parent |
|
1593 if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f)) { |
|
1594 return f; |
|
1595 } |
|
1596 f = parent; |
|
1597 } |
|
1598 return f; |
|
1599 } |
|
1600 |
|
1601 nsIFrame* |
|
1602 nsLayoutUtils::GetAnimatedGeometryRootFor(nsDisplayItem* aItem, |
|
1603 nsDisplayListBuilder* aBuilder) |
|
1604 { |
|
1605 nsIFrame* f = aItem->Frame(); |
|
1606 if (aItem->GetType() == nsDisplayItem::TYPE_SCROLL_LAYER) { |
|
1607 nsDisplayScrollLayer* scrollLayerItem = |
|
1608 static_cast<nsDisplayScrollLayer*>(aItem); |
|
1609 nsIFrame* scrolledFrame = scrollLayerItem->GetScrolledFrame(); |
|
1610 return GetAnimatedGeometryRootForFrame(scrolledFrame, |
|
1611 aBuilder->FindReferenceFrameFor(scrolledFrame)); |
|
1612 } |
|
1613 if (aItem->ShouldFixToViewport(aBuilder)) { |
|
1614 // Make its active scrolled root be the active scrolled root of |
|
1615 // the enclosing viewport, since it shouldn't be scrolled by scrolled |
|
1616 // frames in its document. InvalidateFixedBackgroundFramesFromList in |
|
1617 // nsGfxScrollFrame will not repaint this item when scrolling occurs. |
|
1618 nsIFrame* viewportFrame = |
|
1619 nsLayoutUtils::GetClosestFrameOfType(f, nsGkAtoms::viewportFrame); |
|
1620 NS_ASSERTION(viewportFrame, "no viewport???"); |
|
1621 return GetAnimatedGeometryRootForFrame(viewportFrame, |
|
1622 aBuilder->FindReferenceFrameFor(viewportFrame)); |
|
1623 } |
|
1624 return GetAnimatedGeometryRootForFrame(f, aItem->ReferenceFrame()); |
|
1625 } |
|
1626 |
|
1627 // static |
|
1628 nsIScrollableFrame* |
|
1629 nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame* aFrame, |
|
1630 Direction aDirection) |
|
1631 { |
|
1632 NS_ASSERTION(aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame"); |
|
1633 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { |
|
1634 nsIScrollableFrame* scrollableFrame = do_QueryFrame(f); |
|
1635 if (scrollableFrame) { |
|
1636 ScrollbarStyles ss = scrollableFrame->GetScrollbarStyles(); |
|
1637 uint32_t directions = scrollableFrame->GetPerceivedScrollingDirections(); |
|
1638 if (aDirection == eVertical ? |
|
1639 (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN && |
|
1640 (directions & nsIScrollableFrame::VERTICAL)) : |
|
1641 (ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN && |
|
1642 (directions & nsIScrollableFrame::HORIZONTAL))) |
|
1643 return scrollableFrame; |
|
1644 } |
|
1645 } |
|
1646 return nullptr; |
|
1647 } |
|
1648 |
|
1649 // static |
|
1650 nsIScrollableFrame* |
|
1651 nsLayoutUtils::GetNearestScrollableFrame(nsIFrame* aFrame, uint32_t aFlags) |
|
1652 { |
|
1653 NS_ASSERTION(aFrame, "GetNearestScrollableFrame expects a non-null frame"); |
|
1654 for (nsIFrame* f = aFrame; f; f = (aFlags & SCROLLABLE_SAME_DOC) ? |
|
1655 f->GetParent() : nsLayoutUtils::GetCrossDocParentFrame(f)) { |
|
1656 nsIScrollableFrame* scrollableFrame = do_QueryFrame(f); |
|
1657 if (scrollableFrame) { |
|
1658 ScrollbarStyles ss = scrollableFrame->GetScrollbarStyles(); |
|
1659 if ((aFlags & SCROLLABLE_INCLUDE_HIDDEN) || |
|
1660 ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN || |
|
1661 ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) |
|
1662 return scrollableFrame; |
|
1663 } |
|
1664 } |
|
1665 return nullptr; |
|
1666 } |
|
1667 |
|
1668 // static |
|
1669 nsRect |
|
1670 nsLayoutUtils::GetScrolledRect(nsIFrame* aScrolledFrame, |
|
1671 const nsRect& aScrolledFrameOverflowArea, |
|
1672 const nsSize& aScrollPortSize, |
|
1673 uint8_t aDirection) |
|
1674 { |
|
1675 nscoord x1 = aScrolledFrameOverflowArea.x, |
|
1676 x2 = aScrolledFrameOverflowArea.XMost(), |
|
1677 y1 = aScrolledFrameOverflowArea.y, |
|
1678 y2 = aScrolledFrameOverflowArea.YMost(); |
|
1679 if (y1 < 0) { |
|
1680 y1 = 0; |
|
1681 } |
|
1682 if (aDirection != NS_STYLE_DIRECTION_RTL) { |
|
1683 if (x1 < 0) { |
|
1684 x1 = 0; |
|
1685 } |
|
1686 } else { |
|
1687 if (x2 > aScrollPortSize.width) { |
|
1688 x2 = aScrollPortSize.width; |
|
1689 } |
|
1690 // When the scrolled frame chooses a size larger than its available width (because |
|
1691 // its padding alone is larger than the available width), we need to keep the |
|
1692 // start-edge of the scroll frame anchored to the start-edge of the scrollport. |
|
1693 // When the scrolled frame is RTL, this means moving it in our left-based |
|
1694 // coordinate system, so we need to compensate for its extra width here by |
|
1695 // effectively repositioning the frame. |
|
1696 nscoord extraWidth = std::max(0, aScrolledFrame->GetSize().width - aScrollPortSize.width); |
|
1697 x2 += extraWidth; |
|
1698 } |
|
1699 return nsRect(x1, y1, x2 - x1, y2 - y1); |
|
1700 } |
|
1701 |
|
1702 //static |
|
1703 bool |
|
1704 nsLayoutUtils::HasPseudoStyle(nsIContent* aContent, |
|
1705 nsStyleContext* aStyleContext, |
|
1706 nsCSSPseudoElements::Type aPseudoElement, |
|
1707 nsPresContext* aPresContext) |
|
1708 { |
|
1709 NS_PRECONDITION(aPresContext, "Must have a prescontext"); |
|
1710 |
|
1711 nsRefPtr<nsStyleContext> pseudoContext; |
|
1712 if (aContent) { |
|
1713 pseudoContext = aPresContext->StyleSet()-> |
|
1714 ProbePseudoElementStyle(aContent->AsElement(), aPseudoElement, |
|
1715 aStyleContext); |
|
1716 } |
|
1717 return pseudoContext != nullptr; |
|
1718 } |
|
1719 |
|
1720 nsPoint |
|
1721 nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(nsIDOMEvent* aDOMEvent, nsIFrame* aFrame) |
|
1722 { |
|
1723 if (!aDOMEvent) |
|
1724 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
1725 WidgetEvent* event = aDOMEvent->GetInternalNSEvent(); |
|
1726 if (!event) |
|
1727 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
1728 return GetEventCoordinatesRelativeTo(event, aFrame); |
|
1729 } |
|
1730 |
|
1731 nsPoint |
|
1732 nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent, |
|
1733 nsIFrame* aFrame) |
|
1734 { |
|
1735 if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT && |
|
1736 aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && |
|
1737 aEvent->eventStructType != NS_WHEEL_EVENT && |
|
1738 aEvent->eventStructType != NS_DRAG_EVENT && |
|
1739 aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT && |
|
1740 aEvent->eventStructType != NS_POINTER_EVENT && |
|
1741 aEvent->eventStructType != NS_GESTURENOTIFY_EVENT && |
|
1742 aEvent->eventStructType != NS_TOUCH_EVENT && |
|
1743 aEvent->eventStructType != NS_QUERY_CONTENT_EVENT)) |
|
1744 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
1745 |
|
1746 return GetEventCoordinatesRelativeTo(aEvent, |
|
1747 LayoutDeviceIntPoint::ToUntyped(aEvent->AsGUIEvent()->refPoint), |
|
1748 aFrame); |
|
1749 } |
|
1750 |
|
1751 nsPoint |
|
1752 nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent, |
|
1753 const nsIntPoint aPoint, |
|
1754 nsIFrame* aFrame) |
|
1755 { |
|
1756 if (!aFrame) { |
|
1757 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
1758 } |
|
1759 |
|
1760 nsIWidget* widget = aEvent->AsGUIEvent()->widget; |
|
1761 if (!widget) { |
|
1762 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
1763 } |
|
1764 |
|
1765 return GetEventCoordinatesRelativeTo(widget, aPoint, aFrame); |
|
1766 } |
|
1767 |
|
1768 nsPoint |
|
1769 nsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget* aWidget, |
|
1770 const nsIntPoint aPoint, |
|
1771 nsIFrame* aFrame) |
|
1772 { |
|
1773 if (!aFrame || !aWidget) { |
|
1774 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
1775 } |
|
1776 |
|
1777 nsView* view = aFrame->GetView(); |
|
1778 if (view) { |
|
1779 nsIWidget* frameWidget = view->GetWidget(); |
|
1780 if (frameWidget && frameWidget == aWidget) { |
|
1781 // Special case this cause it happens a lot. |
|
1782 // This also fixes bug 664707, events in the extra-special case of select |
|
1783 // dropdown popups that are transformed. |
|
1784 nsPresContext* presContext = aFrame->PresContext(); |
|
1785 nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x), |
|
1786 presContext->DevPixelsToAppUnits(aPoint.y)); |
|
1787 return pt - view->ViewToWidgetOffset(); |
|
1788 } |
|
1789 } |
|
1790 |
|
1791 /* If we walk up the frame tree and discover that any of the frames are |
|
1792 * transformed, we need to do extra work to convert from the global |
|
1793 * space to the local space. |
|
1794 */ |
|
1795 nsIFrame* rootFrame = aFrame; |
|
1796 bool transformFound = false; |
|
1797 for (nsIFrame* f = aFrame; f; f = GetCrossDocParentFrame(f)) { |
|
1798 if (f->IsTransformed()) { |
|
1799 transformFound = true; |
|
1800 } |
|
1801 |
|
1802 rootFrame = f; |
|
1803 } |
|
1804 |
|
1805 nsView* rootView = rootFrame->GetView(); |
|
1806 if (!rootView) { |
|
1807 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
1808 } |
|
1809 |
|
1810 nsPoint widgetToView = TranslateWidgetToView(rootFrame->PresContext(), |
|
1811 aWidget, aPoint, rootView); |
|
1812 |
|
1813 if (widgetToView == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { |
|
1814 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
1815 } |
|
1816 |
|
1817 // Convert from root document app units to app units of the document aFrame |
|
1818 // is in. |
|
1819 int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel(); |
|
1820 int32_t localAPD = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
1821 widgetToView = widgetToView.ConvertAppUnits(rootAPD, localAPD); |
|
1822 |
|
1823 /* If we encountered a transform, we can't do simple arithmetic to figure |
|
1824 * out how to convert back to aFrame's coordinates and must use the CTM. |
|
1825 */ |
|
1826 if (transformFound || aFrame->IsSVGText()) { |
|
1827 return TransformRootPointToFrame(aFrame, widgetToView); |
|
1828 } |
|
1829 |
|
1830 /* Otherwise, all coordinate systems are translations of one another, |
|
1831 * so we can just subtract out the difference. |
|
1832 */ |
|
1833 return widgetToView - aFrame->GetOffsetToCrossDoc(rootFrame); |
|
1834 } |
|
1835 |
|
1836 nsIFrame* |
|
1837 nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext* aPresContext, |
|
1838 const WidgetEvent* aEvent) |
|
1839 { |
|
1840 #ifdef MOZ_XUL |
|
1841 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
1842 if (!pm) { |
|
1843 return nullptr; |
|
1844 } |
|
1845 nsTArray<nsIFrame*> popups; |
|
1846 pm->GetVisiblePopups(popups); |
|
1847 uint32_t i; |
|
1848 // Search from top to bottom |
|
1849 for (i = 0; i < popups.Length(); i++) { |
|
1850 nsIFrame* popup = popups[i]; |
|
1851 if (popup->PresContext()->GetRootPresContext() == aPresContext && |
|
1852 popup->GetScrollableOverflowRect().Contains( |
|
1853 GetEventCoordinatesRelativeTo(aEvent, popup))) { |
|
1854 return popup; |
|
1855 } |
|
1856 } |
|
1857 #endif |
|
1858 return nullptr; |
|
1859 } |
|
1860 |
|
1861 gfx3DMatrix |
|
1862 nsLayoutUtils::ChangeMatrixBasis(const gfxPoint3D &aOrigin, |
|
1863 const gfx3DMatrix &aMatrix) |
|
1864 { |
|
1865 gfx3DMatrix result = aMatrix; |
|
1866 |
|
1867 /* Translate to the origin before aMatrix */ |
|
1868 result.Translate(-aOrigin); |
|
1869 |
|
1870 /* Translate back into position after aMatrix */ |
|
1871 result.TranslatePost(aOrigin); |
|
1872 |
|
1873 return result; |
|
1874 } |
|
1875 |
|
1876 static void ConstrainToCoordValues(float& aStart, float& aSize) |
|
1877 { |
|
1878 MOZ_ASSERT(aSize >= 0); |
|
1879 |
|
1880 // Here we try to make sure that the resulting nsRect will continue to cover |
|
1881 // as much of the area that was covered by the original gfx Rect as possible. |
|
1882 |
|
1883 // We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since |
|
1884 // nsRect::X/Y() and nsRect::XMost/YMost() can't return values outwith this |
|
1885 // range: |
|
1886 float end = aStart + aSize; |
|
1887 aStart = clamped(aStart, float(nscoord_MIN), float(nscoord_MAX)); |
|
1888 end = clamped(end, float(nscoord_MIN), float(nscoord_MAX)); |
|
1889 |
|
1890 aSize = end - aStart; |
|
1891 |
|
1892 // We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height() |
|
1893 // can't return a value greater than nscoord_MAX. If aSize is greater than |
|
1894 // nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect |
|
1895 // centered: |
|
1896 if (aSize > nscoord_MAX) { |
|
1897 float excess = aSize - nscoord_MAX; |
|
1898 excess /= 2; |
|
1899 aStart += excess; |
|
1900 aSize = nscoord_MAX; |
|
1901 } |
|
1902 } |
|
1903 |
|
1904 /** |
|
1905 * Given a gfxFloat, constrains its value to be between nscoord_MIN and nscoord_MAX. |
|
1906 * |
|
1907 * @param aVal The value to constrain (in/out) |
|
1908 */ |
|
1909 static void ConstrainToCoordValues(gfxFloat& aVal) |
|
1910 { |
|
1911 if (aVal <= nscoord_MIN) |
|
1912 aVal = nscoord_MIN; |
|
1913 else if (aVal >= nscoord_MAX) |
|
1914 aVal = nscoord_MAX; |
|
1915 } |
|
1916 |
|
1917 static void ConstrainToCoordValues(gfxFloat& aStart, gfxFloat& aSize) |
|
1918 { |
|
1919 gfxFloat max = aStart + aSize; |
|
1920 |
|
1921 // Clamp the end points to within nscoord range |
|
1922 ConstrainToCoordValues(aStart); |
|
1923 ConstrainToCoordValues(max); |
|
1924 |
|
1925 aSize = max - aStart; |
|
1926 // If the width if still greater than the max nscoord, then bring both |
|
1927 // endpoints in by the same amount until it fits. |
|
1928 if (aSize > nscoord_MAX) { |
|
1929 gfxFloat excess = aSize - nscoord_MAX; |
|
1930 excess /= 2; |
|
1931 |
|
1932 aStart += excess; |
|
1933 aSize = nscoord_MAX; |
|
1934 } else if (aSize < nscoord_MIN) { |
|
1935 gfxFloat excess = aSize - nscoord_MIN; |
|
1936 excess /= 2; |
|
1937 |
|
1938 aStart -= excess; |
|
1939 aSize = nscoord_MIN; |
|
1940 } |
|
1941 } |
|
1942 |
|
1943 nsRect |
|
1944 nsLayoutUtils::RoundGfxRectToAppRect(const Rect &aRect, float aFactor) |
|
1945 { |
|
1946 /* Get a new Rect whose units are app units by scaling by the specified factor. */ |
|
1947 Rect scaledRect = aRect; |
|
1948 scaledRect.ScaleRoundOut(aFactor); |
|
1949 |
|
1950 /* We now need to constrain our results to the max and min values for coords. */ |
|
1951 ConstrainToCoordValues(scaledRect.x, scaledRect.width); |
|
1952 ConstrainToCoordValues(scaledRect.y, scaledRect.height); |
|
1953 |
|
1954 /* Now typecast everything back. This is guaranteed to be safe. */ |
|
1955 return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()), |
|
1956 nscoord(scaledRect.Width()), nscoord(scaledRect.Height())); |
|
1957 } |
|
1958 |
|
1959 nsRect |
|
1960 nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor) |
|
1961 { |
|
1962 /* Get a new gfxRect whose units are app units by scaling by the specified factor. */ |
|
1963 gfxRect scaledRect = aRect; |
|
1964 scaledRect.ScaleRoundOut(aFactor); |
|
1965 |
|
1966 /* We now need to constrain our results to the max and min values for coords. */ |
|
1967 ConstrainToCoordValues(scaledRect.x, scaledRect.width); |
|
1968 ConstrainToCoordValues(scaledRect.y, scaledRect.height); |
|
1969 |
|
1970 /* Now typecast everything back. This is guaranteed to be safe. */ |
|
1971 return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()), |
|
1972 nscoord(scaledRect.Width()), nscoord(scaledRect.Height())); |
|
1973 } |
|
1974 |
|
1975 |
|
1976 nsRegion |
|
1977 nsLayoutUtils::RoundedRectIntersectRect(const nsRect& aRoundedRect, |
|
1978 const nscoord aRadii[8], |
|
1979 const nsRect& aContainedRect) |
|
1980 { |
|
1981 // rectFullHeight and rectFullWidth together will approximately contain |
|
1982 // the total area of the frame minus the rounded corners. |
|
1983 nsRect rectFullHeight = aRoundedRect; |
|
1984 nscoord xDiff = std::max(aRadii[NS_CORNER_TOP_LEFT_X], aRadii[NS_CORNER_BOTTOM_LEFT_X]); |
|
1985 rectFullHeight.x += xDiff; |
|
1986 rectFullHeight.width -= std::max(aRadii[NS_CORNER_TOP_RIGHT_X], |
|
1987 aRadii[NS_CORNER_BOTTOM_RIGHT_X]) + xDiff; |
|
1988 nsRect r1; |
|
1989 r1.IntersectRect(rectFullHeight, aContainedRect); |
|
1990 |
|
1991 nsRect rectFullWidth = aRoundedRect; |
|
1992 nscoord yDiff = std::max(aRadii[NS_CORNER_TOP_LEFT_Y], aRadii[NS_CORNER_TOP_RIGHT_Y]); |
|
1993 rectFullWidth.y += yDiff; |
|
1994 rectFullWidth.height -= std::max(aRadii[NS_CORNER_BOTTOM_LEFT_Y], |
|
1995 aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) + yDiff; |
|
1996 nsRect r2; |
|
1997 r2.IntersectRect(rectFullWidth, aContainedRect); |
|
1998 |
|
1999 nsRegion result; |
|
2000 result.Or(r1, r2); |
|
2001 return result; |
|
2002 } |
|
2003 |
|
2004 // Helper for RoundedRectIntersectsRect. |
|
2005 static bool |
|
2006 CheckCorner(nscoord aXOffset, nscoord aYOffset, |
|
2007 nscoord aXRadius, nscoord aYRadius) |
|
2008 { |
|
2009 NS_ABORT_IF_FALSE(aXOffset > 0 && aYOffset > 0, |
|
2010 "must not pass nonpositives to CheckCorner"); |
|
2011 NS_ABORT_IF_FALSE(aXRadius >= 0 && aYRadius >= 0, |
|
2012 "must not pass negatives to CheckCorner"); |
|
2013 |
|
2014 // Avoid floating point math unless we're either (1) within the |
|
2015 // quarter-ellipse area at the rounded corner or (2) outside the |
|
2016 // rounding. |
|
2017 if (aXOffset >= aXRadius || aYOffset >= aYRadius) |
|
2018 return true; |
|
2019 |
|
2020 // Convert coordinates to a unit circle with (0,0) as the center of |
|
2021 // curvature, and see if we're inside the circle or outside. |
|
2022 float scaledX = float(aXRadius - aXOffset) / float(aXRadius); |
|
2023 float scaledY = float(aYRadius - aYOffset) / float(aYRadius); |
|
2024 return scaledX * scaledX + scaledY * scaledY < 1.0f; |
|
2025 } |
|
2026 |
|
2027 bool |
|
2028 nsLayoutUtils::RoundedRectIntersectsRect(const nsRect& aRoundedRect, |
|
2029 const nscoord aRadii[8], |
|
2030 const nsRect& aTestRect) |
|
2031 { |
|
2032 if (!aTestRect.Intersects(aRoundedRect)) |
|
2033 return false; |
|
2034 |
|
2035 // distances from this edge of aRoundedRect to opposite edge of aTestRect, |
|
2036 // which we know are positive due to the Intersects check above. |
|
2037 nsMargin insets; |
|
2038 insets.top = aTestRect.YMost() - aRoundedRect.y; |
|
2039 insets.right = aRoundedRect.XMost() - aTestRect.x; |
|
2040 insets.bottom = aRoundedRect.YMost() - aTestRect.y; |
|
2041 insets.left = aTestRect.XMost() - aRoundedRect.x; |
|
2042 |
|
2043 // Check whether the bottom-right corner of aTestRect is inside the |
|
2044 // top left corner of aBounds when rounded by aRadii, etc. If any |
|
2045 // corner is not, then fail; otherwise succeed. |
|
2046 return CheckCorner(insets.left, insets.top, |
|
2047 aRadii[NS_CORNER_TOP_LEFT_X], |
|
2048 aRadii[NS_CORNER_TOP_LEFT_Y]) && |
|
2049 CheckCorner(insets.right, insets.top, |
|
2050 aRadii[NS_CORNER_TOP_RIGHT_X], |
|
2051 aRadii[NS_CORNER_TOP_RIGHT_Y]) && |
|
2052 CheckCorner(insets.right, insets.bottom, |
|
2053 aRadii[NS_CORNER_BOTTOM_RIGHT_X], |
|
2054 aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) && |
|
2055 CheckCorner(insets.left, insets.bottom, |
|
2056 aRadii[NS_CORNER_BOTTOM_LEFT_X], |
|
2057 aRadii[NS_CORNER_BOTTOM_LEFT_Y]); |
|
2058 } |
|
2059 |
|
2060 nsRect |
|
2061 nsLayoutUtils::MatrixTransformRectOut(const nsRect &aBounds, |
|
2062 const gfx3DMatrix &aMatrix, float aFactor) |
|
2063 { |
|
2064 nsRect outside = aBounds; |
|
2065 outside.ScaleRoundOut(1/aFactor); |
|
2066 gfxRect image = aMatrix.TransformBounds(gfxRect(outside.x, |
|
2067 outside.y, |
|
2068 outside.width, |
|
2069 outside.height)); |
|
2070 return RoundGfxRectToAppRect(image, aFactor); |
|
2071 } |
|
2072 |
|
2073 nsRect |
|
2074 nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds, |
|
2075 const gfx3DMatrix &aMatrix, float aFactor) |
|
2076 { |
|
2077 gfxRect image = aMatrix.TransformBounds(gfxRect(NSAppUnitsToDoublePixels(aBounds.x, aFactor), |
|
2078 NSAppUnitsToDoublePixels(aBounds.y, aFactor), |
|
2079 NSAppUnitsToDoublePixels(aBounds.width, aFactor), |
|
2080 NSAppUnitsToDoublePixels(aBounds.height, aFactor))); |
|
2081 |
|
2082 return RoundGfxRectToAppRect(image, aFactor); |
|
2083 } |
|
2084 |
|
2085 nsPoint |
|
2086 nsLayoutUtils::MatrixTransformPoint(const nsPoint &aPoint, |
|
2087 const gfx3DMatrix &aMatrix, float aFactor) |
|
2088 { |
|
2089 gfxPoint image = aMatrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor), |
|
2090 NSAppUnitsToFloatPixels(aPoint.y, aFactor))); |
|
2091 return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor), |
|
2092 NSFloatPixelsToAppUnits(float(image.y), aFactor)); |
|
2093 } |
|
2094 |
|
2095 gfx3DMatrix |
|
2096 nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame, const nsIFrame *aAncestor) |
|
2097 { |
|
2098 nsIFrame* parent; |
|
2099 gfx3DMatrix ctm; |
|
2100 if (aFrame == aAncestor) { |
|
2101 return ctm; |
|
2102 } |
|
2103 ctm = aFrame->GetTransformMatrix(aAncestor, &parent); |
|
2104 while (parent && parent != aAncestor) { |
|
2105 if (!parent->Preserves3DChildren()) { |
|
2106 ctm.ProjectTo2D(); |
|
2107 } |
|
2108 ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent); |
|
2109 } |
|
2110 return ctm; |
|
2111 } |
|
2112 |
|
2113 static nsIFrame* |
|
2114 FindNearestCommonAncestorFrame(nsIFrame* aFrame1, nsIFrame* aFrame2) |
|
2115 { |
|
2116 nsAutoTArray<nsIFrame*,100> ancestors1; |
|
2117 nsAutoTArray<nsIFrame*,100> ancestors2; |
|
2118 nsIFrame* commonAncestor = nullptr; |
|
2119 if (aFrame1->PresContext() == aFrame2->PresContext()) { |
|
2120 commonAncestor = aFrame1->PresContext()->PresShell()->GetRootFrame(); |
|
2121 } |
|
2122 for (nsIFrame* f = aFrame1; f != commonAncestor; |
|
2123 f = nsLayoutUtils::GetCrossDocParentFrame(f)) { |
|
2124 ancestors1.AppendElement(f); |
|
2125 } |
|
2126 for (nsIFrame* f = aFrame2; f != commonAncestor; |
|
2127 f = nsLayoutUtils::GetCrossDocParentFrame(f)) { |
|
2128 ancestors2.AppendElement(f); |
|
2129 } |
|
2130 uint32_t minLengths = std::min(ancestors1.Length(), ancestors2.Length()); |
|
2131 for (uint32_t i = 1; i <= minLengths; ++i) { |
|
2132 if (ancestors1[ancestors1.Length() - i] == ancestors2[ancestors2.Length() - i]) { |
|
2133 commonAncestor = ancestors1[ancestors1.Length() - i]; |
|
2134 } else { |
|
2135 break; |
|
2136 } |
|
2137 } |
|
2138 return commonAncestor; |
|
2139 } |
|
2140 |
|
2141 nsLayoutUtils::TransformResult |
|
2142 nsLayoutUtils::TransformPoints(nsIFrame* aFromFrame, nsIFrame* aToFrame, |
|
2143 uint32_t aPointCount, CSSPoint* aPoints) |
|
2144 { |
|
2145 nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame); |
|
2146 if (!nearestCommonAncestor) { |
|
2147 return NO_COMMON_ANCESTOR; |
|
2148 } |
|
2149 gfx3DMatrix downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor); |
|
2150 if (downToDest.IsSingular()) { |
|
2151 return NONINVERTIBLE_TRANSFORM; |
|
2152 } |
|
2153 downToDest.Invert(); |
|
2154 gfx3DMatrix upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor); |
|
2155 CSSToLayoutDeviceScale devPixelsPerCSSPixelFromFrame( |
|
2156 double(nsPresContext::AppUnitsPerCSSPixel())/ |
|
2157 aFromFrame->PresContext()->AppUnitsPerDevPixel()); |
|
2158 CSSToLayoutDeviceScale devPixelsPerCSSPixelToFrame( |
|
2159 double(nsPresContext::AppUnitsPerCSSPixel())/ |
|
2160 aToFrame->PresContext()->AppUnitsPerDevPixel()); |
|
2161 for (uint32_t i = 0; i < aPointCount; ++i) { |
|
2162 LayoutDevicePoint devPixels = aPoints[i] * devPixelsPerCSSPixelFromFrame; |
|
2163 gfxPoint toDevPixels = downToDest.ProjectPoint( |
|
2164 upToAncestor.ProjectPoint(gfxPoint(devPixels.x, devPixels.y))); |
|
2165 // Divide here so that when the devPixelsPerCSSPixels are the same, we get the correct |
|
2166 // answer instead of some inaccuracy multiplying a number by its reciprocal. |
|
2167 aPoints[i] = LayoutDevicePoint(toDevPixels.x, toDevPixels.y) / |
|
2168 devPixelsPerCSSPixelToFrame; |
|
2169 } |
|
2170 return TRANSFORM_SUCCEEDED; |
|
2171 } |
|
2172 |
|
2173 bool |
|
2174 nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame, |
|
2175 gfx3DMatrix* aTransform) |
|
2176 { |
|
2177 // FIXME/bug 796690: we can sometimes compute a transform in these |
|
2178 // cases, it just increases complexity considerably. Punt for now. |
|
2179 if (aFrame->Preserves3DChildren() || aFrame->HasTransformGetter()) { |
|
2180 return false; |
|
2181 } |
|
2182 |
|
2183 nsIFrame* root = nsLayoutUtils::GetDisplayRootFrame(aFrame); |
|
2184 if (root->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE)) { |
|
2185 // Content may have been invalidated, so we can't reliably compute |
|
2186 // the "layer transform" in general. |
|
2187 return false; |
|
2188 } |
|
2189 // If the caller doesn't care about the value, early-return to skip |
|
2190 // overhead below. |
|
2191 if (!aTransform) { |
|
2192 return true; |
|
2193 } |
|
2194 |
|
2195 nsDisplayListBuilder builder(root, nsDisplayListBuilder::OTHER, |
|
2196 false/*don't build caret*/); |
|
2197 nsDisplayList list; |
|
2198 nsDisplayTransform* item = |
|
2199 new (&builder) nsDisplayTransform(&builder, aFrame, &list); |
|
2200 |
|
2201 *aTransform = |
|
2202 item->GetTransform(); |
|
2203 item->~nsDisplayTransform(); |
|
2204 |
|
2205 return true; |
|
2206 } |
|
2207 |
|
2208 static bool |
|
2209 TransformGfxPointFromAncestor(nsIFrame *aFrame, |
|
2210 const gfxPoint &aPoint, |
|
2211 nsIFrame *aAncestor, |
|
2212 gfxPoint* aOut) |
|
2213 { |
|
2214 gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor); |
|
2215 |
|
2216 float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
2217 nsRect childBounds = aFrame->GetVisualOverflowRectRelativeToSelf(); |
|
2218 gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), |
|
2219 NSAppUnitsToFloatPixels(childBounds.y, factor), |
|
2220 NSAppUnitsToFloatPixels(childBounds.width, factor), |
|
2221 NSAppUnitsToFloatPixels(childBounds.height, factor)); |
|
2222 return ctm.UntransformPoint(aPoint, childGfxBounds, aOut); |
|
2223 } |
|
2224 |
|
2225 static gfxRect |
|
2226 TransformGfxRectToAncestor(nsIFrame *aFrame, |
|
2227 const gfxRect &aRect, |
|
2228 const nsIFrame *aAncestor, |
|
2229 bool* aPreservesAxisAlignedRectangles = nullptr) |
|
2230 { |
|
2231 gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor); |
|
2232 if (aPreservesAxisAlignedRectangles) { |
|
2233 gfxMatrix matrix2d; |
|
2234 *aPreservesAxisAlignedRectangles = |
|
2235 ctm.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles(); |
|
2236 } |
|
2237 return ctm.TransformBounds(aRect); |
|
2238 } |
|
2239 |
|
2240 static SVGTextFrame* |
|
2241 GetContainingSVGTextFrame(nsIFrame* aFrame) |
|
2242 { |
|
2243 if (!aFrame->IsSVGText()) { |
|
2244 return nullptr; |
|
2245 } |
|
2246 |
|
2247 return static_cast<SVGTextFrame*> |
|
2248 (nsLayoutUtils::GetClosestFrameOfType(aFrame->GetParent(), |
|
2249 nsGkAtoms::svgTextFrame)); |
|
2250 } |
|
2251 |
|
2252 nsPoint |
|
2253 nsLayoutUtils::TransformAncestorPointToFrame(nsIFrame* aFrame, |
|
2254 const nsPoint& aPoint, |
|
2255 nsIFrame* aAncestor) |
|
2256 { |
|
2257 SVGTextFrame* text = GetContainingSVGTextFrame(aFrame); |
|
2258 |
|
2259 float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
2260 gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor), |
|
2261 NSAppUnitsToFloatPixels(aPoint.y, factor)); |
|
2262 |
|
2263 if (text) { |
|
2264 if (!TransformGfxPointFromAncestor(text, result, aAncestor, &result)) { |
|
2265 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
2266 } |
|
2267 result = text->TransformFramePointToTextChild(result, aFrame); |
|
2268 } else { |
|
2269 if (!TransformGfxPointFromAncestor(aFrame, result, nullptr, &result)) { |
|
2270 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
2271 } |
|
2272 } |
|
2273 |
|
2274 return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor), |
|
2275 NSFloatPixelsToAppUnits(float(result.y), factor)); |
|
2276 } |
|
2277 |
|
2278 nsRect |
|
2279 nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame, |
|
2280 const nsRect& aRect, |
|
2281 const nsIFrame* aAncestor, |
|
2282 bool* aPreservesAxisAlignedRectangles /* = nullptr */) |
|
2283 { |
|
2284 SVGTextFrame* text = GetContainingSVGTextFrame(aFrame); |
|
2285 |
|
2286 float srcAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
2287 gfxRect result; |
|
2288 |
|
2289 if (text) { |
|
2290 result = text->TransformFrameRectFromTextChild(aRect, aFrame); |
|
2291 result = TransformGfxRectToAncestor(text, result, aAncestor); |
|
2292 // TransformFrameRectFromTextChild could involve any kind of transform, we |
|
2293 // could drill down into it to get an answer out of it but we don't yet. |
|
2294 if (aPreservesAxisAlignedRectangles) |
|
2295 *aPreservesAxisAlignedRectangles = false; |
|
2296 } else { |
|
2297 result = gfxRect(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel), |
|
2298 NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel), |
|
2299 NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel), |
|
2300 NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel)); |
|
2301 result = TransformGfxRectToAncestor(aFrame, result, aAncestor, aPreservesAxisAlignedRectangles); |
|
2302 } |
|
2303 |
|
2304 float destAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel(); |
|
2305 return nsRect(NSFloatPixelsToAppUnits(float(result.x), destAppUnitsPerDevPixel), |
|
2306 NSFloatPixelsToAppUnits(float(result.y), destAppUnitsPerDevPixel), |
|
2307 NSFloatPixelsToAppUnits(float(result.width), destAppUnitsPerDevPixel), |
|
2308 NSFloatPixelsToAppUnits(float(result.height), destAppUnitsPerDevPixel)); |
|
2309 } |
|
2310 |
|
2311 static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) { |
|
2312 nsIntPoint offset(0, 0); |
|
2313 nsIWidget* parent = aWidget->GetParent(); |
|
2314 while (parent) { |
|
2315 nsIntRect bounds; |
|
2316 aWidget->GetBounds(bounds); |
|
2317 offset += bounds.TopLeft(); |
|
2318 aWidget = parent; |
|
2319 parent = aWidget->GetParent(); |
|
2320 } |
|
2321 aRootWidget = aWidget; |
|
2322 return offset; |
|
2323 } |
|
2324 |
|
2325 nsPoint |
|
2326 nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext, |
|
2327 nsIWidget* aWidget, nsIntPoint aPt, |
|
2328 nsView* aView) |
|
2329 { |
|
2330 nsPoint viewOffset; |
|
2331 nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset); |
|
2332 if (!viewWidget) { |
|
2333 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
|
2334 } |
|
2335 |
|
2336 nsIWidget* fromRoot; |
|
2337 nsIntPoint fromOffset = GetWidgetOffset(aWidget, fromRoot); |
|
2338 nsIWidget* toRoot; |
|
2339 nsIntPoint toOffset = GetWidgetOffset(viewWidget, toRoot); |
|
2340 |
|
2341 nsIntPoint widgetPoint; |
|
2342 if (fromRoot == toRoot) { |
|
2343 widgetPoint = aPt + fromOffset - toOffset; |
|
2344 } else { |
|
2345 nsIntPoint screenPoint = aWidget->WidgetToScreenOffset(); |
|
2346 widgetPoint = aPt + screenPoint - viewWidget->WidgetToScreenOffset(); |
|
2347 } |
|
2348 |
|
2349 nsPoint widgetAppUnits(aPresContext->DevPixelsToAppUnits(widgetPoint.x), |
|
2350 aPresContext->DevPixelsToAppUnits(widgetPoint.y)); |
|
2351 return widgetAppUnits - viewOffset; |
|
2352 } |
|
2353 |
|
2354 // Combine aNewBreakType with aOrigBreakType, but limit the break types |
|
2355 // to NS_STYLE_CLEAR_LEFT, RIGHT, BOTH. |
|
2356 uint8_t |
|
2357 nsLayoutUtils::CombineBreakType(uint8_t aOrigBreakType, |
|
2358 uint8_t aNewBreakType) |
|
2359 { |
|
2360 uint8_t breakType = aOrigBreakType; |
|
2361 switch(breakType) { |
|
2362 case NS_STYLE_CLEAR_LEFT: |
|
2363 if (NS_STYLE_CLEAR_RIGHT == aNewBreakType || |
|
2364 NS_STYLE_CLEAR_BOTH == aNewBreakType) { |
|
2365 breakType = NS_STYLE_CLEAR_BOTH; |
|
2366 } |
|
2367 break; |
|
2368 case NS_STYLE_CLEAR_RIGHT: |
|
2369 if (NS_STYLE_CLEAR_LEFT == aNewBreakType || |
|
2370 NS_STYLE_CLEAR_BOTH == aNewBreakType) { |
|
2371 breakType = NS_STYLE_CLEAR_BOTH; |
|
2372 } |
|
2373 break; |
|
2374 case NS_STYLE_CLEAR_NONE: |
|
2375 if (NS_STYLE_CLEAR_LEFT == aNewBreakType || |
|
2376 NS_STYLE_CLEAR_RIGHT == aNewBreakType || |
|
2377 NS_STYLE_CLEAR_BOTH == aNewBreakType) { |
|
2378 breakType = aNewBreakType; |
|
2379 } |
|
2380 } |
|
2381 return breakType; |
|
2382 } |
|
2383 |
|
2384 #ifdef MOZ_DUMP_PAINTING |
|
2385 #include <stdio.h> |
|
2386 |
|
2387 static bool gDumpEventList = false; |
|
2388 int gPaintCount = 0; |
|
2389 #endif |
|
2390 |
|
2391 nsresult |
|
2392 nsLayoutUtils::GetRemoteContentIds(nsIFrame* aFrame, |
|
2393 const nsRect& aTarget, |
|
2394 nsTArray<ViewID> &aOutIDs, |
|
2395 bool aIgnoreRootScrollFrame) |
|
2396 { |
|
2397 nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::EVENT_DELIVERY, |
|
2398 false); |
|
2399 nsDisplayList list; |
|
2400 |
|
2401 if (aIgnoreRootScrollFrame) { |
|
2402 nsIFrame* rootScrollFrame = |
|
2403 aFrame->PresContext()->PresShell()->GetRootScrollFrame(); |
|
2404 if (rootScrollFrame) { |
|
2405 builder.SetIgnoreScrollFrame(rootScrollFrame); |
|
2406 } |
|
2407 } |
|
2408 |
|
2409 builder.EnterPresShell(aFrame, aTarget); |
|
2410 aFrame->BuildDisplayListForStackingContext(&builder, aTarget, &list); |
|
2411 builder.LeavePresShell(aFrame, aTarget); |
|
2412 |
|
2413 nsAutoTArray<nsIFrame*,8> outFrames; |
|
2414 nsDisplayItem::HitTestState hitTestState(&aOutIDs); |
|
2415 list.HitTest(&builder, aTarget, &hitTestState, &outFrames); |
|
2416 list.DeleteAll(); |
|
2417 |
|
2418 return NS_OK; |
|
2419 } |
|
2420 |
|
2421 nsIFrame* |
|
2422 nsLayoutUtils::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt, uint32_t aFlags) |
|
2423 { |
|
2424 PROFILER_LABEL("nsLayoutUtils", "GetFrameForPoint"); |
|
2425 nsresult rv; |
|
2426 nsAutoTArray<nsIFrame*,8> outFrames; |
|
2427 rv = GetFramesForArea(aFrame, nsRect(aPt, nsSize(1, 1)), outFrames, aFlags); |
|
2428 NS_ENSURE_SUCCESS(rv, nullptr); |
|
2429 return outFrames.Length() ? outFrames.ElementAt(0) : nullptr; |
|
2430 } |
|
2431 |
|
2432 nsresult |
|
2433 nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect, |
|
2434 nsTArray<nsIFrame*> &aOutFrames, |
|
2435 uint32_t aFlags) |
|
2436 { |
|
2437 PROFILER_LABEL("nsLayoutUtils","GetFramesForArea"); |
|
2438 nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::EVENT_DELIVERY, |
|
2439 false); |
|
2440 nsDisplayList list; |
|
2441 nsRect target(aRect); |
|
2442 |
|
2443 if (aFlags & IGNORE_PAINT_SUPPRESSION) { |
|
2444 builder.IgnorePaintSuppression(); |
|
2445 } |
|
2446 |
|
2447 if (aFlags & IGNORE_ROOT_SCROLL_FRAME) { |
|
2448 nsIFrame* rootScrollFrame = |
|
2449 aFrame->PresContext()->PresShell()->GetRootScrollFrame(); |
|
2450 if (rootScrollFrame) { |
|
2451 builder.SetIgnoreScrollFrame(rootScrollFrame); |
|
2452 } |
|
2453 } |
|
2454 if (aFlags & IGNORE_CROSS_DOC) { |
|
2455 builder.SetDescendIntoSubdocuments(false); |
|
2456 } |
|
2457 |
|
2458 builder.EnterPresShell(aFrame, target); |
|
2459 aFrame->BuildDisplayListForStackingContext(&builder, target, &list); |
|
2460 builder.LeavePresShell(aFrame, target); |
|
2461 |
|
2462 #ifdef MOZ_DUMP_PAINTING |
|
2463 if (gDumpEventList) { |
|
2464 fprintf_stderr(stderr, "Event handling --- (%d,%d):\n", aRect.x, aRect.y); |
|
2465 nsFrame::PrintDisplayList(&builder, list); |
|
2466 } |
|
2467 #endif |
|
2468 |
|
2469 nsDisplayItem::HitTestState hitTestState; |
|
2470 list.HitTest(&builder, target, &hitTestState, &aOutFrames); |
|
2471 list.DeleteAll(); |
|
2472 return NS_OK; |
|
2473 } |
|
2474 |
|
2475 // This function is only used on B2G, and some compilers complain about |
|
2476 // unused static functions, so we need to #ifdef it. |
|
2477 #ifdef MOZ_WIDGET_GONK |
|
2478 // aScrollFrame and aScrollFrameAsScrollable must be non-nullptr |
|
2479 static FrameMetrics |
|
2480 CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame, |
|
2481 nsIScrollableFrame* aScrollFrameAsScrollable) { |
|
2482 // Calculate the metrics necessary for calculating the displayport. |
|
2483 // This code has a lot in common with the code in RecordFrameMetrics(); |
|
2484 // we may want to refactor this at some point. |
|
2485 FrameMetrics metrics; |
|
2486 nsPresContext* presContext = aScrollFrame->PresContext(); |
|
2487 nsIPresShell* presShell = presContext->PresShell(); |
|
2488 CSSToLayoutDeviceScale deviceScale(float(nsPresContext::AppUnitsPerCSSPixel()) |
|
2489 / presContext->AppUnitsPerDevPixel()); |
|
2490 ParentLayerToLayerScale resolution(presShell->GetResolution().width); |
|
2491 LayoutDeviceToLayerScale cumulativeResolution(presShell->GetCumulativeResolution().width); |
|
2492 |
|
2493 metrics.mDevPixelsPerCSSPixel = deviceScale; |
|
2494 metrics.mResolution = resolution; |
|
2495 metrics.mCumulativeResolution = cumulativeResolution; |
|
2496 metrics.SetZoom(deviceScale * cumulativeResolution * LayerToScreenScale(1)); |
|
2497 |
|
2498 // Only the size of the composition bounds is relevant to the |
|
2499 // displayport calculation, not its origin. |
|
2500 nsSize compositionSize = nsLayoutUtils::CalculateCompositionSizeForFrame(aScrollFrame); |
|
2501 metrics.mCompositionBounds |
|
2502 = RoundedToInt(LayoutDeviceRect::FromAppUnits(nsRect(nsPoint(0, 0), compositionSize), |
|
2503 presContext->AppUnitsPerDevPixel()) |
|
2504 * (cumulativeResolution / resolution)); |
|
2505 |
|
2506 // This function is used for setting a display port for subframes, so |
|
2507 // aScrollFrame will not be the root content document's root scroll frame. |
|
2508 metrics.SetRootCompositionSize( |
|
2509 nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame, false, metrics)); |
|
2510 |
|
2511 metrics.SetScrollOffset(CSSPoint::FromAppUnits( |
|
2512 aScrollFrameAsScrollable->GetScrollPosition())); |
|
2513 |
|
2514 metrics.mScrollableRect = CSSRect::FromAppUnits( |
|
2515 nsLayoutUtils::CalculateScrollableRectForFrame(aScrollFrameAsScrollable, nullptr)); |
|
2516 |
|
2517 return metrics; |
|
2518 } |
|
2519 #endif |
|
2520 |
|
2521 bool |
|
2522 nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder, |
|
2523 nsIFrame* aScrollFrame, |
|
2524 nsRect aDisplayPortBase, |
|
2525 nsRect* aOutDisplayport) { |
|
2526 nsIContent* content = aScrollFrame->GetContent(); |
|
2527 nsIScrollableFrame* scrollableFrame = do_QueryFrame(aScrollFrame); |
|
2528 if (!content || !scrollableFrame) { |
|
2529 return false; |
|
2530 } |
|
2531 |
|
2532 // Set the base rect. Note that this will not influence 'haveDisplayPort', |
|
2533 // which is based on either the whole rect or margins being set, but it |
|
2534 // will affect what is returned in 'aOutDisplayPort' if margins are set. |
|
2535 SetDisplayPortBase(content, aDisplayPortBase); |
|
2536 |
|
2537 bool haveDisplayPort = GetDisplayPort(content, aOutDisplayport); |
|
2538 |
|
2539 #ifdef MOZ_WIDGET_GONK |
|
2540 // On B2G, we perform an optimization where we ensure that at least one |
|
2541 // async-scrollable frame (i.e. one that WantsAsyncScroll()) has a displayport. |
|
2542 // If that's not the case yet, and we are async-scrollable, we will get a |
|
2543 // displayport. |
|
2544 // Note: we only do this in processes where we do subframe scrolling to |
|
2545 // begin with (i.e., not in the parent process on B2G). |
|
2546 if (WantSubAPZC() && |
|
2547 !aBuilder.HaveScrollableDisplayPort() && |
|
2548 scrollableFrame->WantAsyncScroll()) { |
|
2549 |
|
2550 // If we don't already have a displayport, calculate and set one. |
|
2551 if (!haveDisplayPort) { |
|
2552 FrameMetrics metrics = CalculateFrameMetricsForDisplayPort(aScrollFrame, scrollableFrame); |
|
2553 LayerMargin displayportMargins = AsyncPanZoomController::CalculatePendingDisplayPort( |
|
2554 metrics, ScreenPoint(0.0f, 0.0f), 0.0); |
|
2555 nsIPresShell* presShell = aScrollFrame->PresContext()->GetPresShell(); |
|
2556 gfx::IntSize alignment = gfxPrefs::LayersTilesEnabled() |
|
2557 ? gfx::IntSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()) : |
|
2558 gfx::IntSize(0, 0); |
|
2559 nsLayoutUtils::SetDisplayPortMargins( |
|
2560 content, presShell, displayportMargins, alignment.width, |
|
2561 alignment.height, 0, nsLayoutUtils::RepaintMode::DoNotRepaint); |
|
2562 haveDisplayPort = GetDisplayPort(content, aOutDisplayport); |
|
2563 NS_ASSERTION(haveDisplayPort, "should have a displayport after having just set it"); |
|
2564 } |
|
2565 |
|
2566 // Record that the we now have a scrollable display port. |
|
2567 aBuilder.SetHaveScrollableDisplayPort(); |
|
2568 } |
|
2569 #endif |
|
2570 |
|
2571 return haveDisplayPort; |
|
2572 } |
|
2573 |
|
2574 nsresult |
|
2575 nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFrame, |
|
2576 const nsRegion& aDirtyRegion, nscolor aBackstop, |
|
2577 uint32_t aFlags) |
|
2578 { |
|
2579 PROFILER_LABEL("nsLayoutUtils","PaintFrame"); |
|
2580 if (aFlags & PAINT_WIDGET_LAYERS) { |
|
2581 nsView* view = aFrame->GetView(); |
|
2582 if (!(view && view->GetWidget() && GetDisplayRootFrame(aFrame) == aFrame)) { |
|
2583 aFlags &= ~PAINT_WIDGET_LAYERS; |
|
2584 NS_ASSERTION(aRenderingContext, "need a rendering context"); |
|
2585 } |
|
2586 } |
|
2587 |
|
2588 nsPresContext* presContext = aFrame->PresContext(); |
|
2589 nsIPresShell* presShell = presContext->PresShell(); |
|
2590 nsRootPresContext* rootPresContext = presContext->GetRootPresContext(); |
|
2591 if (!rootPresContext) { |
|
2592 return NS_OK; |
|
2593 } |
|
2594 |
|
2595 nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::PAINTING, |
|
2596 !(aFlags & PAINT_HIDE_CARET)); |
|
2597 |
|
2598 nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); |
|
2599 bool usingDisplayPort = false; |
|
2600 nsRect displayport; |
|
2601 if (rootScrollFrame && !aFrame->GetParent()) { |
|
2602 nsRect displayportBase( |
|
2603 nsPoint(0,0), |
|
2604 nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame)); |
|
2605 usingDisplayPort = nsLayoutUtils::GetOrMaybeCreateDisplayPort( |
|
2606 builder, rootScrollFrame, displayportBase, &displayport); |
|
2607 } |
|
2608 |
|
2609 nsRegion visibleRegion; |
|
2610 if (aFlags & PAINT_WIDGET_LAYERS) { |
|
2611 // This layer tree will be reused, so we'll need to calculate it |
|
2612 // for the whole "visible" area of the window |
|
2613 // |
|
2614 // |ignoreViewportScrolling| and |usingDisplayPort| are persistent |
|
2615 // document-rendering state. We rely on PresShell to flush |
|
2616 // retained layers as needed when that persistent state changes. |
|
2617 if (!usingDisplayPort) { |
|
2618 visibleRegion = aFrame->GetVisualOverflowRectRelativeToSelf(); |
|
2619 } else { |
|
2620 visibleRegion = displayport; |
|
2621 } |
|
2622 } else { |
|
2623 visibleRegion = aDirtyRegion; |
|
2624 } |
|
2625 |
|
2626 // If we're going to display something different from what we'd normally |
|
2627 // paint in a window then we will flush out any retained layer trees before |
|
2628 // *and after* we draw. |
|
2629 bool willFlushRetainedLayers = (aFlags & PAINT_HIDE_CARET) != 0; |
|
2630 |
|
2631 nsDisplayList list; |
|
2632 if (aFlags & PAINT_IN_TRANSFORM) { |
|
2633 builder.SetInTransform(true); |
|
2634 } |
|
2635 if (aFlags & PAINT_SYNC_DECODE_IMAGES) { |
|
2636 builder.SetSyncDecodeImages(true); |
|
2637 } |
|
2638 if (aFlags & (PAINT_WIDGET_LAYERS | PAINT_TO_WINDOW)) { |
|
2639 builder.SetPaintingToWindow(true); |
|
2640 } |
|
2641 if (aFlags & PAINT_IGNORE_SUPPRESSION) { |
|
2642 builder.IgnorePaintSuppression(); |
|
2643 } |
|
2644 // Windowed plugins aren't allowed in popups |
|
2645 if ((aFlags & PAINT_WIDGET_LAYERS) && |
|
2646 !willFlushRetainedLayers && |
|
2647 !(aFlags & PAINT_DOCUMENT_RELATIVE) && |
|
2648 rootPresContext->NeedToComputePluginGeometryUpdates()) { |
|
2649 builder.SetWillComputePluginGeometry(true); |
|
2650 } |
|
2651 nsRect canvasArea(nsPoint(0, 0), aFrame->GetSize()); |
|
2652 |
|
2653 bool ignoreViewportScrolling = |
|
2654 aFrame->GetParent() ? false : presShell->IgnoringViewportScrolling(); |
|
2655 if (ignoreViewportScrolling && rootScrollFrame) { |
|
2656 nsIScrollableFrame* rootScrollableFrame = |
|
2657 presShell->GetRootScrollFrameAsScrollable(); |
|
2658 if (aFlags & PAINT_DOCUMENT_RELATIVE) { |
|
2659 // Make visibleRegion and aRenderingContext relative to the |
|
2660 // scrolled frame instead of the root frame. |
|
2661 nsPoint pos = rootScrollableFrame->GetScrollPosition(); |
|
2662 visibleRegion.MoveBy(-pos); |
|
2663 if (aRenderingContext) { |
|
2664 aRenderingContext->Translate(pos); |
|
2665 } |
|
2666 } |
|
2667 builder.SetIgnoreScrollFrame(rootScrollFrame); |
|
2668 |
|
2669 nsCanvasFrame* canvasFrame = |
|
2670 do_QueryFrame(rootScrollableFrame->GetScrolledFrame()); |
|
2671 if (canvasFrame) { |
|
2672 // Use UnionRect here to ensure that areas where the scrollbars |
|
2673 // were are still filled with the background color. |
|
2674 canvasArea.UnionRect(canvasArea, |
|
2675 canvasFrame->CanvasArea() + builder.ToReferenceFrame(canvasFrame)); |
|
2676 } |
|
2677 } |
|
2678 |
|
2679 nsRect dirtyRect = visibleRegion.GetBounds(); |
|
2680 builder.EnterPresShell(aFrame, dirtyRect); |
|
2681 { |
|
2682 PROFILER_LABEL("nsLayoutUtils","PaintFrame::BuildDisplayList"); |
|
2683 aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list); |
|
2684 } |
|
2685 const bool paintAllContinuations = aFlags & PAINT_ALL_CONTINUATIONS; |
|
2686 NS_ASSERTION(!paintAllContinuations || !aFrame->GetPrevContinuation(), |
|
2687 "If painting all continuations, the frame must be " |
|
2688 "first-continuation"); |
|
2689 |
|
2690 nsIAtom* frameType = aFrame->GetType(); |
|
2691 |
|
2692 if (paintAllContinuations) { |
|
2693 nsIFrame* currentFrame = aFrame; |
|
2694 while ((currentFrame = currentFrame->GetNextContinuation()) != nullptr) { |
|
2695 PROFILER_LABEL("nsLayoutUtils","PaintFrame::ContinuationsBuildDisplayList"); |
|
2696 nsRect frameDirty = dirtyRect - builder.ToReferenceFrame(currentFrame); |
|
2697 currentFrame->BuildDisplayListForStackingContext(&builder, |
|
2698 frameDirty, &list); |
|
2699 } |
|
2700 } |
|
2701 |
|
2702 // For the viewport frame in print preview/page layout we want to paint |
|
2703 // the grey background behind the page, not the canvas color. |
|
2704 if (frameType == nsGkAtoms::viewportFrame && |
|
2705 nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) { |
|
2706 nsRect bounds = nsRect(builder.ToReferenceFrame(aFrame), |
|
2707 aFrame->GetSize()); |
|
2708 presShell->AddPrintPreviewBackgroundItem(builder, list, aFrame, bounds); |
|
2709 } else if (frameType != nsGkAtoms::pageFrame) { |
|
2710 // For printing, this function is first called on an nsPageFrame, which |
|
2711 // creates a display list with a PageContent item. The PageContent item's |
|
2712 // paint function calls this function on the nsPageFrame's child which is |
|
2713 // an nsPageContentFrame. We only want to add the canvas background color |
|
2714 // item once, for the nsPageContentFrame. |
|
2715 |
|
2716 // Add the canvas background color to the bottom of the list. This |
|
2717 // happens after we've built the list so that AddCanvasBackgroundColorItem |
|
2718 // can monkey with the contents if necessary. |
|
2719 canvasArea.IntersectRect(canvasArea, visibleRegion.GetBounds()); |
|
2720 presShell->AddCanvasBackgroundColorItem( |
|
2721 builder, list, aFrame, canvasArea, aBackstop); |
|
2722 |
|
2723 // If the passed in backstop color makes us draw something different from |
|
2724 // normal, we need to flush layers. |
|
2725 if ((aFlags & PAINT_WIDGET_LAYERS) && !willFlushRetainedLayers) { |
|
2726 nsView* view = aFrame->GetView(); |
|
2727 if (view) { |
|
2728 nscolor backstop = presShell->ComputeBackstopColor(view); |
|
2729 // The PresShell's canvas background color doesn't get updated until |
|
2730 // EnterPresShell, so this check has to be done after that. |
|
2731 nscolor canvasColor = presShell->GetCanvasBackground(); |
|
2732 if (NS_ComposeColors(aBackstop, canvasColor) != |
|
2733 NS_ComposeColors(backstop, canvasColor)) { |
|
2734 willFlushRetainedLayers = true; |
|
2735 } |
|
2736 } |
|
2737 } |
|
2738 } |
|
2739 |
|
2740 builder.LeavePresShell(aFrame, dirtyRect); |
|
2741 |
|
2742 if (builder.GetHadToIgnorePaintSuppression()) { |
|
2743 willFlushRetainedLayers = true; |
|
2744 } |
|
2745 |
|
2746 #ifdef MOZ_DUMP_PAINTING |
|
2747 FILE* savedDumpFile = gfxUtils::sDumpPaintFile; |
|
2748 if (gfxUtils::sDumpPaintList || gfxUtils::sDumpPainting) { |
|
2749 if (gfxUtils::sDumpPaintingToFile) { |
|
2750 nsCString string("dump-"); |
|
2751 string.AppendInt(gPaintCount); |
|
2752 string.Append(".html"); |
|
2753 gfxUtils::sDumpPaintFile = fopen(string.BeginReading(), "w"); |
|
2754 } else { |
|
2755 gfxUtils::sDumpPaintFile = stderr; |
|
2756 } |
|
2757 if (gfxUtils::sDumpPaintingToFile) { |
|
2758 fprintf_stderr(gfxUtils::sDumpPaintFile, "<html><head><script>var array = {}; function ViewImage(index) { window.location = array[index]; }</script></head><body>"); |
|
2759 } |
|
2760 fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- before optimization (dirty %d,%d,%d,%d):\n", |
|
2761 dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); |
|
2762 nsFrame::PrintDisplayList(&builder, list, gfxUtils::sDumpPaintFile, gfxUtils::sDumpPaintingToFile); |
|
2763 if (gfxUtils::sDumpPaintingToFile) { |
|
2764 fprintf_stderr(gfxUtils::sDumpPaintFile, "<script>"); |
|
2765 } |
|
2766 } |
|
2767 #endif |
|
2768 |
|
2769 list.ComputeVisibilityForRoot(&builder, &visibleRegion, |
|
2770 usingDisplayPort ? rootScrollFrame : nullptr); |
|
2771 |
|
2772 uint32_t flags = nsDisplayList::PAINT_DEFAULT; |
|
2773 if (aFlags & PAINT_WIDGET_LAYERS) { |
|
2774 flags |= nsDisplayList::PAINT_USE_WIDGET_LAYERS; |
|
2775 if (willFlushRetainedLayers) { |
|
2776 // The caller wanted to paint from retained layers, but set up |
|
2777 // the paint in such a way that we can't use them. We're going |
|
2778 // to display something different from what we'd normally paint |
|
2779 // in a window, so make sure we flush out any retained layer |
|
2780 // trees before *and after* we draw. Callers should be fixed to |
|
2781 // not do this. |
|
2782 NS_WARNING("Flushing retained layers!"); |
|
2783 flags |= nsDisplayList::PAINT_FLUSH_LAYERS; |
|
2784 } else if (!(aFlags & PAINT_DOCUMENT_RELATIVE)) { |
|
2785 nsIWidget *widget = aFrame->GetNearestWidget(); |
|
2786 if (widget) { |
|
2787 builder.SetFinalTransparentRegion(visibleRegion); |
|
2788 // If we're finished building display list items for painting of the outermost |
|
2789 // pres shell, notify the widget about any toolbars we've encountered. |
|
2790 widget->UpdateThemeGeometries(builder.GetThemeGeometries()); |
|
2791 } |
|
2792 } |
|
2793 } |
|
2794 if (aFlags & PAINT_EXISTING_TRANSACTION) { |
|
2795 flags |= nsDisplayList::PAINT_EXISTING_TRANSACTION; |
|
2796 } |
|
2797 if (aFlags & PAINT_NO_COMPOSITE) { |
|
2798 flags |= nsDisplayList::PAINT_NO_COMPOSITE; |
|
2799 } |
|
2800 if (aFlags & PAINT_COMPRESSED) { |
|
2801 flags |= nsDisplayList::PAINT_COMPRESSED; |
|
2802 } |
|
2803 |
|
2804 list.PaintRoot(&builder, aRenderingContext, flags); |
|
2805 |
|
2806 #ifdef MOZ_DUMP_PAINTING |
|
2807 if (gfxUtils::sDumpPaintList || gfxUtils::sDumpPainting) { |
|
2808 if (gfxUtils::sDumpPaintingToFile) { |
|
2809 fprintf_stderr(gfxUtils::sDumpPaintFile, "</script>"); |
|
2810 } |
|
2811 fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- after optimization:\n"); |
|
2812 nsFrame::PrintDisplayList(&builder, list, gfxUtils::sDumpPaintFile, gfxUtils::sDumpPaintingToFile); |
|
2813 |
|
2814 fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- retained layer tree:\n"); |
|
2815 nsIWidget* widget = aFrame->GetNearestWidget(); |
|
2816 if (widget) { |
|
2817 nsRefPtr<LayerManager> layerManager = widget->GetLayerManager(); |
|
2818 if (layerManager) { |
|
2819 FrameLayerBuilder::DumpRetainedLayerTree(layerManager, gfxUtils::sDumpPaintFile, |
|
2820 gfxUtils::sDumpPaintingToFile); |
|
2821 } |
|
2822 } |
|
2823 if (gfxUtils::sDumpPaintingToFile) { |
|
2824 fprintf(gfxUtils::sDumpPaintFile, "</body></html>"); |
|
2825 fclose(gfxUtils::sDumpPaintFile); |
|
2826 } |
|
2827 gfxUtils::sDumpPaintFile = savedDumpFile; |
|
2828 gPaintCount++; |
|
2829 } |
|
2830 #endif |
|
2831 |
|
2832 // Update the widget's opaque region information. This sets |
|
2833 // glass boundaries on Windows. Also set up plugin clip regions and bounds. |
|
2834 if ((aFlags & PAINT_WIDGET_LAYERS) && |
|
2835 !willFlushRetainedLayers && |
|
2836 !(aFlags & PAINT_DOCUMENT_RELATIVE)) { |
|
2837 nsIWidget *widget = aFrame->GetNearestWidget(); |
|
2838 if (widget) { |
|
2839 nsRegion excludedRegion = builder.GetExcludedGlassRegion(); |
|
2840 excludedRegion.Sub(excludedRegion, visibleRegion); |
|
2841 nsIntRegion windowRegion(excludedRegion.ToNearestPixels(presContext->AppUnitsPerDevPixel())); |
|
2842 widget->UpdateOpaqueRegion(windowRegion); |
|
2843 } |
|
2844 } |
|
2845 |
|
2846 if (builder.WillComputePluginGeometry()) { |
|
2847 nsRefPtr<LayerManager> layerManager; |
|
2848 nsIWidget* widget = aFrame->GetNearestWidget(); |
|
2849 if (widget) { |
|
2850 layerManager = widget->GetLayerManager(); |
|
2851 } |
|
2852 |
|
2853 rootPresContext->ComputePluginGeometryUpdates(aFrame, &builder, &list); |
|
2854 |
|
2855 // We're not going to get a WillPaintWindow event here if we didn't do |
|
2856 // widget invalidation, so just apply the plugin geometry update here instead. |
|
2857 // We could instead have the compositor send back an equivalent to WillPaintWindow, |
|
2858 // but it should be close enough to now not to matter. |
|
2859 if (layerManager && !layerManager->NeedsWidgetInvalidation()) { |
|
2860 rootPresContext->ApplyPluginGeometryUpdates(); |
|
2861 } |
|
2862 |
|
2863 // We told the compositor thread not to composite when it received the transaction because |
|
2864 // we wanted to update plugins first. Schedule the composite now. |
|
2865 if (layerManager) { |
|
2866 layerManager->Composite(); |
|
2867 } |
|
2868 } |
|
2869 |
|
2870 |
|
2871 // Flush the list so we don't trigger the IsEmpty-on-destruction assertion |
|
2872 list.DeleteAll(); |
|
2873 return NS_OK; |
|
2874 } |
|
2875 |
|
2876 /** |
|
2877 * Uses a binary search for find where the cursor falls in the line of text |
|
2878 * It also keeps track of the part of the string that has already been measured |
|
2879 * so it doesn't have to keep measuring the same text over and over |
|
2880 * |
|
2881 * @param "aBaseWidth" contains the width in twips of the portion |
|
2882 * of the text that has already been measured, and aBaseInx contains |
|
2883 * the index of the text that has already been measured. |
|
2884 * |
|
2885 * @param aTextWidth returns the (in twips) the length of the text that falls |
|
2886 * before the cursor aIndex contains the index of the text where the cursor falls |
|
2887 */ |
|
2888 bool |
|
2889 nsLayoutUtils::BinarySearchForPosition(nsRenderingContext* aRendContext, |
|
2890 const char16_t* aText, |
|
2891 int32_t aBaseWidth, |
|
2892 int32_t aBaseInx, |
|
2893 int32_t aStartInx, |
|
2894 int32_t aEndInx, |
|
2895 int32_t aCursorPos, |
|
2896 int32_t& aIndex, |
|
2897 int32_t& aTextWidth) |
|
2898 { |
|
2899 int32_t range = aEndInx - aStartInx; |
|
2900 if ((range == 1) || (range == 2 && NS_IS_HIGH_SURROGATE(aText[aStartInx]))) { |
|
2901 aIndex = aStartInx + aBaseInx; |
|
2902 aTextWidth = aRendContext->GetWidth(aText, aIndex); |
|
2903 return true; |
|
2904 } |
|
2905 |
|
2906 int32_t inx = aStartInx + (range / 2); |
|
2907 |
|
2908 // Make sure we don't leave a dangling low surrogate |
|
2909 if (NS_IS_HIGH_SURROGATE(aText[inx-1])) |
|
2910 inx++; |
|
2911 |
|
2912 int32_t textWidth = aRendContext->GetWidth(aText, inx); |
|
2913 |
|
2914 int32_t fullWidth = aBaseWidth + textWidth; |
|
2915 if (fullWidth == aCursorPos) { |
|
2916 aTextWidth = textWidth; |
|
2917 aIndex = inx; |
|
2918 return true; |
|
2919 } else if (aCursorPos < fullWidth) { |
|
2920 aTextWidth = aBaseWidth; |
|
2921 if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, aStartInx, inx, aCursorPos, aIndex, aTextWidth)) { |
|
2922 return true; |
|
2923 } |
|
2924 } else { |
|
2925 aTextWidth = fullWidth; |
|
2926 if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, inx, aEndInx, aCursorPos, aIndex, aTextWidth)) { |
|
2927 return true; |
|
2928 } |
|
2929 } |
|
2930 return false; |
|
2931 } |
|
2932 |
|
2933 static void |
|
2934 AddBoxesForFrame(nsIFrame* aFrame, |
|
2935 nsLayoutUtils::BoxCallback* aCallback) |
|
2936 { |
|
2937 nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo(); |
|
2938 |
|
2939 if (pseudoType == nsCSSAnonBoxes::tableOuter) { |
|
2940 AddBoxesForFrame(aFrame->GetFirstPrincipalChild(), aCallback); |
|
2941 nsIFrame* kid = aFrame->GetFirstChild(nsIFrame::kCaptionList); |
|
2942 if (kid) { |
|
2943 AddBoxesForFrame(kid, aCallback); |
|
2944 } |
|
2945 } else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock || |
|
2946 pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock || |
|
2947 pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock || |
|
2948 pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) { |
|
2949 for (nsIFrame* kid = aFrame->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { |
|
2950 AddBoxesForFrame(kid, aCallback); |
|
2951 } |
|
2952 } else { |
|
2953 aCallback->AddBox(aFrame); |
|
2954 } |
|
2955 } |
|
2956 |
|
2957 void |
|
2958 nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback) |
|
2959 { |
|
2960 while (aFrame) { |
|
2961 AddBoxesForFrame(aFrame, aCallback); |
|
2962 aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame); |
|
2963 } |
|
2964 } |
|
2965 |
|
2966 nsIFrame* |
|
2967 nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame* aFrame) |
|
2968 { |
|
2969 while (aFrame) { |
|
2970 nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo(); |
|
2971 |
|
2972 if (pseudoType == nsCSSAnonBoxes::tableOuter) { |
|
2973 nsIFrame* f = GetFirstNonAnonymousFrame(aFrame->GetFirstPrincipalChild()); |
|
2974 if (f) { |
|
2975 return f; |
|
2976 } |
|
2977 nsIFrame* kid = aFrame->GetFirstChild(nsIFrame::kCaptionList); |
|
2978 if (kid) { |
|
2979 f = GetFirstNonAnonymousFrame(kid); |
|
2980 if (f) { |
|
2981 return f; |
|
2982 } |
|
2983 } |
|
2984 } else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock || |
|
2985 pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock || |
|
2986 pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock || |
|
2987 pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) { |
|
2988 for (nsIFrame* kid = aFrame->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { |
|
2989 nsIFrame* f = GetFirstNonAnonymousFrame(kid); |
|
2990 if (f) { |
|
2991 return f; |
|
2992 } |
|
2993 } |
|
2994 } else { |
|
2995 return aFrame; |
|
2996 } |
|
2997 |
|
2998 aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame); |
|
2999 } |
|
3000 return nullptr; |
|
3001 } |
|
3002 |
|
3003 struct BoxToRect : public nsLayoutUtils::BoxCallback { |
|
3004 nsIFrame* mRelativeTo; |
|
3005 nsLayoutUtils::RectCallback* mCallback; |
|
3006 uint32_t mFlags; |
|
3007 |
|
3008 BoxToRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback, |
|
3009 uint32_t aFlags) |
|
3010 : mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags) {} |
|
3011 |
|
3012 virtual void AddBox(nsIFrame* aFrame) MOZ_OVERRIDE { |
|
3013 nsRect r; |
|
3014 nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r); |
|
3015 if (!outer) { |
|
3016 outer = aFrame; |
|
3017 switch (mFlags & nsLayoutUtils::RECTS_WHICH_BOX_MASK) { |
|
3018 case nsLayoutUtils::RECTS_USE_CONTENT_BOX: |
|
3019 r = aFrame->GetContentRectRelativeToSelf(); |
|
3020 break; |
|
3021 case nsLayoutUtils::RECTS_USE_PADDING_BOX: |
|
3022 r = aFrame->GetPaddingRectRelativeToSelf(); |
|
3023 break; |
|
3024 case nsLayoutUtils::RECTS_USE_MARGIN_BOX: |
|
3025 r = aFrame->GetMarginRectRelativeToSelf(); |
|
3026 break; |
|
3027 default: // Use the border box |
|
3028 r = aFrame->GetRectRelativeToSelf(); |
|
3029 } |
|
3030 } |
|
3031 if (mFlags & nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS) { |
|
3032 r = nsLayoutUtils::TransformFrameRectToAncestor(outer, r, mRelativeTo); |
|
3033 } else { |
|
3034 r += outer->GetOffsetTo(mRelativeTo); |
|
3035 } |
|
3036 mCallback->AddRect(r); |
|
3037 } |
|
3038 }; |
|
3039 |
|
3040 void |
|
3041 nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo, |
|
3042 RectCallback* aCallback, uint32_t aFlags) |
|
3043 { |
|
3044 BoxToRect converter(aRelativeTo, aCallback, aFlags); |
|
3045 GetAllInFlowBoxes(aFrame, &converter); |
|
3046 } |
|
3047 |
|
3048 nsLayoutUtils::RectAccumulator::RectAccumulator() : mSeenFirstRect(false) {} |
|
3049 |
|
3050 void nsLayoutUtils::RectAccumulator::AddRect(const nsRect& aRect) { |
|
3051 mResultRect.UnionRect(mResultRect, aRect); |
|
3052 if (!mSeenFirstRect) { |
|
3053 mSeenFirstRect = true; |
|
3054 mFirstRect = aRect; |
|
3055 } |
|
3056 } |
|
3057 |
|
3058 nsLayoutUtils::RectListBuilder::RectListBuilder(DOMRectList* aList) |
|
3059 : mRectList(aList) |
|
3060 { |
|
3061 } |
|
3062 |
|
3063 void nsLayoutUtils::RectListBuilder::AddRect(const nsRect& aRect) { |
|
3064 nsRefPtr<DOMRect> rect = new DOMRect(mRectList); |
|
3065 |
|
3066 rect->SetLayoutRect(aRect); |
|
3067 mRectList->Append(rect); |
|
3068 } |
|
3069 |
|
3070 nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame) |
|
3071 { |
|
3072 return aFrame->PresContext()->PresShell()->GetRootFrame(); |
|
3073 } |
|
3074 |
|
3075 nsRect |
|
3076 nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo, |
|
3077 uint32_t aFlags) { |
|
3078 RectAccumulator accumulator; |
|
3079 GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags); |
|
3080 return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect |
|
3081 : accumulator.mResultRect; |
|
3082 } |
|
3083 |
|
3084 nsRect |
|
3085 nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect, |
|
3086 nsIFrame* aFrame, |
|
3087 uint32_t aFlags) |
|
3088 { |
|
3089 const nsStyleText* textStyle = aFrame->StyleText(); |
|
3090 if (!textStyle->HasTextShadow()) |
|
3091 return aTextAndDecorationsRect; |
|
3092 |
|
3093 nsRect resultRect = aTextAndDecorationsRect; |
|
3094 int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
3095 for (uint32_t i = 0; i < textStyle->mTextShadow->Length(); ++i) { |
|
3096 nsCSSShadowItem* shadow = textStyle->mTextShadow->ShadowAt(i); |
|
3097 nsMargin blur = nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D); |
|
3098 if ((aFlags & EXCLUDE_BLUR_SHADOWS) && blur != nsMargin(0, 0, 0, 0)) |
|
3099 continue; |
|
3100 |
|
3101 nsRect tmpRect(aTextAndDecorationsRect); |
|
3102 |
|
3103 tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset)); |
|
3104 tmpRect.Inflate(blur); |
|
3105 |
|
3106 resultRect.UnionRect(resultRect, tmpRect); |
|
3107 } |
|
3108 return resultRect; |
|
3109 } |
|
3110 |
|
3111 nsresult |
|
3112 nsLayoutUtils::GetFontMetricsForFrame(const nsIFrame* aFrame, |
|
3113 nsFontMetrics** aFontMetrics, |
|
3114 float aInflation) |
|
3115 { |
|
3116 return nsLayoutUtils::GetFontMetricsForStyleContext(aFrame->StyleContext(), |
|
3117 aFontMetrics, |
|
3118 aInflation); |
|
3119 } |
|
3120 |
|
3121 nsresult |
|
3122 nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext, |
|
3123 nsFontMetrics** aFontMetrics, |
|
3124 float aInflation) |
|
3125 { |
|
3126 // pass the user font set object into the device context to pass along to CreateFontGroup |
|
3127 nsPresContext* pc = aStyleContext->PresContext(); |
|
3128 gfxUserFontSet* fs = pc->GetUserFontSet(); |
|
3129 gfxTextPerfMetrics* tp = pc->GetTextPerfMetrics(); |
|
3130 |
|
3131 nsFont font = aStyleContext->StyleFont()->mFont; |
|
3132 // We need to not run font.size through floats when it's large since |
|
3133 // doing so would be lossy. Fortunately, in such cases, aInflation is |
|
3134 // guaranteed to be 1.0f. |
|
3135 if (aInflation != 1.0f) { |
|
3136 font.size = NSToCoordRound(font.size * aInflation); |
|
3137 } |
|
3138 return pc->DeviceContext()->GetMetricsFor( |
|
3139 font, aStyleContext->StyleFont()->mLanguage, |
|
3140 fs, tp, *aFontMetrics); |
|
3141 } |
|
3142 |
|
3143 nsIFrame* |
|
3144 nsLayoutUtils::FindChildContainingDescendant(nsIFrame* aParent, nsIFrame* aDescendantFrame) |
|
3145 { |
|
3146 nsIFrame* result = aDescendantFrame; |
|
3147 |
|
3148 while (result) { |
|
3149 nsIFrame* parent = result->GetParent(); |
|
3150 if (parent == aParent) { |
|
3151 break; |
|
3152 } |
|
3153 |
|
3154 // The frame is not an immediate child of aParent so walk up another level |
|
3155 result = parent; |
|
3156 } |
|
3157 |
|
3158 return result; |
|
3159 } |
|
3160 |
|
3161 nsBlockFrame* |
|
3162 nsLayoutUtils::GetAsBlock(nsIFrame* aFrame) |
|
3163 { |
|
3164 nsBlockFrame* block = do_QueryFrame(aFrame); |
|
3165 return block; |
|
3166 } |
|
3167 |
|
3168 nsBlockFrame* |
|
3169 nsLayoutUtils::FindNearestBlockAncestor(nsIFrame* aFrame) |
|
3170 { |
|
3171 nsIFrame* nextAncestor; |
|
3172 for (nextAncestor = aFrame->GetParent(); nextAncestor; |
|
3173 nextAncestor = nextAncestor->GetParent()) { |
|
3174 nsBlockFrame* block = GetAsBlock(nextAncestor); |
|
3175 if (block) |
|
3176 return block; |
|
3177 } |
|
3178 return nullptr; |
|
3179 } |
|
3180 |
|
3181 nsIFrame* |
|
3182 nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame* aFrame) |
|
3183 { |
|
3184 if (!(aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT)) |
|
3185 return aFrame; |
|
3186 |
|
3187 nsIFrame* f = aFrame; |
|
3188 do { |
|
3189 f = GetParentOrPlaceholderFor(f); |
|
3190 } while (f->GetStateBits() & NS_FRAME_GENERATED_CONTENT); |
|
3191 return f; |
|
3192 } |
|
3193 |
|
3194 nsIFrame* |
|
3195 nsLayoutUtils::GetParentOrPlaceholderFor(nsIFrame* aFrame) |
|
3196 { |
|
3197 if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) |
|
3198 && !aFrame->GetPrevInFlow()) { |
|
3199 return aFrame->PresContext()->PresShell()->FrameManager()-> |
|
3200 GetPlaceholderFrameFor(aFrame); |
|
3201 } |
|
3202 return aFrame->GetParent(); |
|
3203 } |
|
3204 |
|
3205 nsIFrame* |
|
3206 nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(nsIFrame* aFrame) |
|
3207 { |
|
3208 nsIFrame* f = GetParentOrPlaceholderFor(aFrame); |
|
3209 if (f) |
|
3210 return f; |
|
3211 return GetCrossDocParentFrame(aFrame); |
|
3212 } |
|
3213 |
|
3214 nsIFrame* |
|
3215 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(nsIFrame *aFrame) |
|
3216 { |
|
3217 nsIFrame *result = aFrame->GetNextContinuation(); |
|
3218 if (result) |
|
3219 return result; |
|
3220 |
|
3221 if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0) { |
|
3222 // We only store the ib-split sibling annotation with the first |
|
3223 // frame in the continuation chain. Walk back to find that frame now. |
|
3224 aFrame = aFrame->FirstContinuation(); |
|
3225 |
|
3226 void* value = aFrame->Properties().Get(nsIFrame::IBSplitSibling()); |
|
3227 return static_cast<nsIFrame*>(value); |
|
3228 } |
|
3229 |
|
3230 return nullptr; |
|
3231 } |
|
3232 |
|
3233 nsIFrame* |
|
3234 nsLayoutUtils::FirstContinuationOrIBSplitSibling(nsIFrame *aFrame) |
|
3235 { |
|
3236 nsIFrame *result = aFrame->FirstContinuation(); |
|
3237 if (result->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) { |
|
3238 while (true) { |
|
3239 nsIFrame *f = static_cast<nsIFrame*> |
|
3240 (result->Properties().Get(nsIFrame::IBSplitPrevSibling())); |
|
3241 if (!f) |
|
3242 break; |
|
3243 result = f; |
|
3244 } |
|
3245 } |
|
3246 |
|
3247 return result; |
|
3248 } |
|
3249 |
|
3250 bool |
|
3251 nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(nsIFrame *aFrame) |
|
3252 { |
|
3253 if (aFrame->GetPrevContinuation()) { |
|
3254 return false; |
|
3255 } |
|
3256 if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) && |
|
3257 aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling())) { |
|
3258 return false; |
|
3259 } |
|
3260 |
|
3261 return true; |
|
3262 } |
|
3263 |
|
3264 bool |
|
3265 nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame) |
|
3266 { |
|
3267 if (!aFrame) |
|
3268 return false; |
|
3269 |
|
3270 nsIFrame* rootScrollFrame = |
|
3271 aFrame->PresContext()->PresShell()->GetRootScrollFrame(); |
|
3272 if (!rootScrollFrame) |
|
3273 return false; |
|
3274 |
|
3275 nsIScrollableFrame* rootScrollableFrame = do_QueryFrame(rootScrollFrame); |
|
3276 NS_ASSERTION(rootScrollableFrame, "The root scorollable frame is null"); |
|
3277 |
|
3278 if (!IsProperAncestorFrame(rootScrollFrame, aFrame)) |
|
3279 return false; |
|
3280 |
|
3281 nsIFrame* rootScrolledFrame = rootScrollableFrame->GetScrolledFrame(); |
|
3282 return !(rootScrolledFrame == aFrame || |
|
3283 IsProperAncestorFrame(rootScrolledFrame, aFrame)); |
|
3284 } |
|
3285 |
|
3286 static nscoord AddPercents(nsLayoutUtils::IntrinsicWidthType aType, |
|
3287 nscoord aCurrent, float aPercent) |
|
3288 { |
|
3289 nscoord result = aCurrent; |
|
3290 if (aPercent > 0.0f && aType == nsLayoutUtils::PREF_WIDTH) { |
|
3291 // XXX Should we also consider percentages for min widths, up to a |
|
3292 // limit? |
|
3293 if (aPercent >= 1.0f) |
|
3294 result = nscoord_MAX; |
|
3295 else |
|
3296 result = NSToCoordRound(float(result) / (1.0f - aPercent)); |
|
3297 } |
|
3298 return result; |
|
3299 } |
|
3300 |
|
3301 // Use only for widths/heights (or their min/max), since it clamps |
|
3302 // negative calc() results to 0. |
|
3303 static bool GetAbsoluteCoord(const nsStyleCoord& aStyle, nscoord& aResult) |
|
3304 { |
|
3305 if (aStyle.IsCalcUnit()) { |
|
3306 if (aStyle.CalcHasPercent()) { |
|
3307 return false; |
|
3308 } |
|
3309 // If it has no percents, we can pass 0 for the percentage basis. |
|
3310 aResult = nsRuleNode::ComputeComputedCalc(aStyle, 0); |
|
3311 if (aResult < 0) |
|
3312 aResult = 0; |
|
3313 return true; |
|
3314 } |
|
3315 |
|
3316 if (eStyleUnit_Coord != aStyle.GetUnit()) |
|
3317 return false; |
|
3318 |
|
3319 aResult = aStyle.GetCoordValue(); |
|
3320 NS_ASSERTION(aResult >= 0, "negative widths not allowed"); |
|
3321 return true; |
|
3322 } |
|
3323 |
|
3324 // Only call on style coords for which GetAbsoluteCoord returned false. |
|
3325 static bool |
|
3326 GetPercentHeight(const nsStyleCoord& aStyle, |
|
3327 nsIFrame* aFrame, |
|
3328 nscoord& aResult) |
|
3329 { |
|
3330 if (eStyleUnit_Percent != aStyle.GetUnit() && |
|
3331 !aStyle.IsCalcUnit()) |
|
3332 return false; |
|
3333 |
|
3334 MOZ_ASSERT(!aStyle.IsCalcUnit() || aStyle.CalcHasPercent(), |
|
3335 "GetAbsoluteCoord should have handled this"); |
|
3336 |
|
3337 nsIFrame *f = aFrame->GetContainingBlock(); |
|
3338 if (!f) { |
|
3339 NS_NOTREACHED("top of frame tree not a containing block"); |
|
3340 return false; |
|
3341 } |
|
3342 |
|
3343 // During reflow, nsHTMLScrollFrame::ReflowScrolledFrame uses |
|
3344 // SetComputedHeight on the reflow state for its child to propagate its |
|
3345 // computed height to the scrolled content. So here we skip to the scroll |
|
3346 // frame that contains this scrolled content in order to get the same |
|
3347 // behavior as layout when computing percentage heights. |
|
3348 if (f->StyleContext()->GetPseudo() == nsCSSAnonBoxes::scrolledContent) { |
|
3349 f = f->GetParent(); |
|
3350 } |
|
3351 |
|
3352 const nsStylePosition *pos = f->StylePosition(); |
|
3353 nscoord h; |
|
3354 if (!GetAbsoluteCoord(pos->mHeight, h) && |
|
3355 !GetPercentHeight(pos->mHeight, f, h)) { |
|
3356 NS_ASSERTION(pos->mHeight.GetUnit() == eStyleUnit_Auto || |
|
3357 pos->mHeight.HasPercent(), |
|
3358 "unknown height unit"); |
|
3359 nsIAtom* fType = f->GetType(); |
|
3360 if (fType != nsGkAtoms::viewportFrame && fType != nsGkAtoms::canvasFrame && |
|
3361 fType != nsGkAtoms::pageContentFrame) { |
|
3362 // There's no basis for the percentage height, so it acts like auto. |
|
3363 // Should we consider a max-height < min-height pair a basis for |
|
3364 // percentage heights? The spec is somewhat unclear, and not doing |
|
3365 // so is simpler and avoids troubling discontinuities in behavior, |
|
3366 // so I'll choose not to. -LDB |
|
3367 return false; |
|
3368 } |
|
3369 |
|
3370 NS_ASSERTION(pos->mHeight.GetUnit() == eStyleUnit_Auto, |
|
3371 "Unexpected height unit for viewport or canvas or page-content"); |
|
3372 // For the viewport, canvas, and page-content kids, the percentage |
|
3373 // basis is just the parent height. |
|
3374 h = f->GetSize().height; |
|
3375 if (h == NS_UNCONSTRAINEDSIZE) { |
|
3376 // We don't have a percentage basis after all |
|
3377 return false; |
|
3378 } |
|
3379 } |
|
3380 |
|
3381 nscoord maxh; |
|
3382 if (GetAbsoluteCoord(pos->mMaxHeight, maxh) || |
|
3383 GetPercentHeight(pos->mMaxHeight, f, maxh)) { |
|
3384 if (maxh < h) |
|
3385 h = maxh; |
|
3386 } else { |
|
3387 NS_ASSERTION(pos->mMaxHeight.GetUnit() == eStyleUnit_None || |
|
3388 pos->mMaxHeight.HasPercent(), |
|
3389 "unknown max-height unit"); |
|
3390 } |
|
3391 |
|
3392 nscoord minh; |
|
3393 if (GetAbsoluteCoord(pos->mMinHeight, minh) || |
|
3394 GetPercentHeight(pos->mMinHeight, f, minh)) { |
|
3395 if (minh > h) |
|
3396 h = minh; |
|
3397 } else { |
|
3398 NS_ASSERTION(pos->mMinHeight.HasPercent(), |
|
3399 "unknown min-height unit"); |
|
3400 } |
|
3401 |
|
3402 if (aStyle.IsCalcUnit()) { |
|
3403 aResult = std::max(nsRuleNode::ComputeComputedCalc(aStyle, h), 0); |
|
3404 return true; |
|
3405 } |
|
3406 |
|
3407 aResult = NSToCoordRound(aStyle.GetPercentValue() * h); |
|
3408 return true; |
|
3409 } |
|
3410 |
|
3411 // Handles only -moz-max-content and -moz-min-content, and |
|
3412 // -moz-fit-content for min-width and max-width, since the others |
|
3413 // (-moz-fit-content for width, and -moz-available) have no effect on |
|
3414 // intrinsic widths. |
|
3415 enum eWidthProperty { PROP_WIDTH, PROP_MAX_WIDTH, PROP_MIN_WIDTH }; |
|
3416 static bool |
|
3417 GetIntrinsicCoord(const nsStyleCoord& aStyle, |
|
3418 nsRenderingContext* aRenderingContext, |
|
3419 nsIFrame* aFrame, |
|
3420 eWidthProperty aProperty, |
|
3421 nscoord& aResult) |
|
3422 { |
|
3423 NS_PRECONDITION(aProperty == PROP_WIDTH || aProperty == PROP_MAX_WIDTH || |
|
3424 aProperty == PROP_MIN_WIDTH, "unexpected property"); |
|
3425 if (aStyle.GetUnit() != eStyleUnit_Enumerated) |
|
3426 return false; |
|
3427 int32_t val = aStyle.GetIntValue(); |
|
3428 NS_ASSERTION(val == NS_STYLE_WIDTH_MAX_CONTENT || |
|
3429 val == NS_STYLE_WIDTH_MIN_CONTENT || |
|
3430 val == NS_STYLE_WIDTH_FIT_CONTENT || |
|
3431 val == NS_STYLE_WIDTH_AVAILABLE, |
|
3432 "unexpected enumerated value for width property"); |
|
3433 if (val == NS_STYLE_WIDTH_AVAILABLE) |
|
3434 return false; |
|
3435 if (val == NS_STYLE_WIDTH_FIT_CONTENT) { |
|
3436 if (aProperty == PROP_WIDTH) |
|
3437 return false; // handle like 'width: auto' |
|
3438 if (aProperty == PROP_MAX_WIDTH) |
|
3439 // constrain large 'width' values down to -moz-max-content |
|
3440 val = NS_STYLE_WIDTH_MAX_CONTENT; |
|
3441 else |
|
3442 // constrain small 'width' or 'max-width' values up to -moz-min-content |
|
3443 val = NS_STYLE_WIDTH_MIN_CONTENT; |
|
3444 } |
|
3445 |
|
3446 NS_ASSERTION(val == NS_STYLE_WIDTH_MAX_CONTENT || |
|
3447 val == NS_STYLE_WIDTH_MIN_CONTENT, |
|
3448 "should have reduced everything remaining to one of these"); |
|
3449 |
|
3450 // If aFrame is a container for font size inflation, then shrink |
|
3451 // wrapping inside of it should not apply font size inflation. |
|
3452 AutoMaybeDisableFontInflation an(aFrame); |
|
3453 |
|
3454 if (val == NS_STYLE_WIDTH_MAX_CONTENT) |
|
3455 aResult = aFrame->GetPrefWidth(aRenderingContext); |
|
3456 else |
|
3457 aResult = aFrame->GetMinWidth(aRenderingContext); |
|
3458 return true; |
|
3459 } |
|
3460 |
|
3461 #undef DEBUG_INTRINSIC_WIDTH |
|
3462 |
|
3463 #ifdef DEBUG_INTRINSIC_WIDTH |
|
3464 static int32_t gNoiseIndent = 0; |
|
3465 #endif |
|
3466 |
|
3467 /* static */ nscoord |
|
3468 nsLayoutUtils::IntrinsicForContainer(nsRenderingContext *aRenderingContext, |
|
3469 nsIFrame *aFrame, |
|
3470 IntrinsicWidthType aType, |
|
3471 uint32_t aFlags) |
|
3472 { |
|
3473 NS_PRECONDITION(aFrame, "null frame"); |
|
3474 NS_PRECONDITION(aType == MIN_WIDTH || aType == PREF_WIDTH, "bad type"); |
|
3475 |
|
3476 #ifdef DEBUG_INTRINSIC_WIDTH |
|
3477 nsFrame::IndentBy(stderr, gNoiseIndent); |
|
3478 static_cast<nsFrame*>(aFrame)->ListTag(stderr); |
|
3479 printf_stderr(" %s intrinsic width for container:\n", |
|
3480 aType == MIN_WIDTH ? "min" : "pref"); |
|
3481 #endif |
|
3482 |
|
3483 // If aFrame is a container for font size inflation, then shrink |
|
3484 // wrapping inside of it should not apply font size inflation. |
|
3485 AutoMaybeDisableFontInflation an(aFrame); |
|
3486 |
|
3487 nsIFrame::IntrinsicWidthOffsetData offsets = |
|
3488 aFrame->IntrinsicWidthOffsets(aRenderingContext); |
|
3489 |
|
3490 const nsStylePosition *stylePos = aFrame->StylePosition(); |
|
3491 uint8_t boxSizing = stylePos->mBoxSizing; |
|
3492 const nsStyleCoord &styleWidth = stylePos->mWidth; |
|
3493 const nsStyleCoord &styleMinWidth = stylePos->mMinWidth; |
|
3494 const nsStyleCoord &styleMaxWidth = stylePos->mMaxWidth; |
|
3495 |
|
3496 // We build up two values starting with the content box, and then |
|
3497 // adding padding, border and margin. The result is normally |
|
3498 // |result|. Then, when we handle 'width', 'min-width', and |
|
3499 // 'max-width', we use the results we've been building in |min| as a |
|
3500 // minimum, overriding 'min-width'. This ensures two things: |
|
3501 // * that we don't let a value of 'box-sizing' specifying a width |
|
3502 // smaller than the padding/border inside the box-sizing box give |
|
3503 // a content width less than zero |
|
3504 // * that we prevent tables from becoming smaller than their |
|
3505 // intrinsic minimum width |
|
3506 nscoord result = 0, min = 0; |
|
3507 |
|
3508 nscoord maxw; |
|
3509 bool haveFixedMaxWidth = GetAbsoluteCoord(styleMaxWidth, maxw); |
|
3510 nscoord minw; |
|
3511 bool haveFixedMinWidth = GetAbsoluteCoord(styleMinWidth, minw); |
|
3512 |
|
3513 // If we have a specified width (or a specified 'min-width' greater |
|
3514 // than the specified 'max-width', which works out to the same thing), |
|
3515 // don't even bother getting the frame's intrinsic width, because in |
|
3516 // this case GetAbsoluteCoord(styleWidth, w) will always succeed, so |
|
3517 // we'll never need the intrinsic dimensions. |
|
3518 if (styleWidth.GetUnit() == eStyleUnit_Enumerated && |
|
3519 (styleWidth.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT || |
|
3520 styleWidth.GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT)) { |
|
3521 // -moz-fit-content and -moz-available enumerated widths compute intrinsic |
|
3522 // widths just like auto. |
|
3523 // For -moz-max-content and -moz-min-content, we handle them like |
|
3524 // specified widths, but ignore box-sizing. |
|
3525 boxSizing = NS_STYLE_BOX_SIZING_CONTENT; |
|
3526 } else if (!styleWidth.ConvertsToLength() && |
|
3527 !(haveFixedMinWidth && haveFixedMaxWidth && maxw <= minw)) { |
|
3528 #ifdef DEBUG_INTRINSIC_WIDTH |
|
3529 ++gNoiseIndent; |
|
3530 #endif |
|
3531 if (aType == MIN_WIDTH) |
|
3532 result = aFrame->GetMinWidth(aRenderingContext); |
|
3533 else |
|
3534 result = aFrame->GetPrefWidth(aRenderingContext); |
|
3535 #ifdef DEBUG_INTRINSIC_WIDTH |
|
3536 --gNoiseIndent; |
|
3537 nsFrame::IndentBy(stderr, gNoiseIndent); |
|
3538 static_cast<nsFrame*>(aFrame)->ListTag(stderr); |
|
3539 printf_stderr(" %s intrinsic width from frame is %d.\n", |
|
3540 aType == MIN_WIDTH ? "min" : "pref", result); |
|
3541 #endif |
|
3542 |
|
3543 // Handle elements with an intrinsic ratio (or size) and a specified |
|
3544 // height, min-height, or max-height. |
|
3545 const nsStyleCoord &styleHeight = stylePos->mHeight; |
|
3546 const nsStyleCoord &styleMinHeight = stylePos->mMinHeight; |
|
3547 const nsStyleCoord &styleMaxHeight = stylePos->mMaxHeight; |
|
3548 if (styleHeight.GetUnit() != eStyleUnit_Auto || |
|
3549 !(styleMinHeight.GetUnit() == eStyleUnit_Coord && |
|
3550 styleMinHeight.GetCoordValue() == 0) || |
|
3551 styleMaxHeight.GetUnit() != eStyleUnit_None) { |
|
3552 |
|
3553 nsSize ratio = aFrame->GetIntrinsicRatio(); |
|
3554 |
|
3555 if (ratio.height != 0) { |
|
3556 nscoord heightTakenByBoxSizing = 0; |
|
3557 switch (boxSizing) { |
|
3558 case NS_STYLE_BOX_SIZING_BORDER: { |
|
3559 const nsStyleBorder* styleBorder = aFrame->StyleBorder(); |
|
3560 heightTakenByBoxSizing += |
|
3561 styleBorder->GetComputedBorder().TopBottom(); |
|
3562 // fall through |
|
3563 } |
|
3564 case NS_STYLE_BOX_SIZING_PADDING: { |
|
3565 if (!(aFlags & IGNORE_PADDING)) { |
|
3566 const nsStylePadding* stylePadding = aFrame->StylePadding(); |
|
3567 nscoord pad; |
|
3568 if (GetAbsoluteCoord(stylePadding->mPadding.GetTop(), pad) || |
|
3569 GetPercentHeight(stylePadding->mPadding.GetTop(), aFrame, pad)) { |
|
3570 heightTakenByBoxSizing += pad; |
|
3571 } |
|
3572 if (GetAbsoluteCoord(stylePadding->mPadding.GetBottom(), pad) || |
|
3573 GetPercentHeight(stylePadding->mPadding.GetBottom(), aFrame, pad)) { |
|
3574 heightTakenByBoxSizing += pad; |
|
3575 } |
|
3576 } |
|
3577 // fall through |
|
3578 } |
|
3579 case NS_STYLE_BOX_SIZING_CONTENT: |
|
3580 default: |
|
3581 break; |
|
3582 } |
|
3583 |
|
3584 nscoord h; |
|
3585 if (GetAbsoluteCoord(styleHeight, h) || |
|
3586 GetPercentHeight(styleHeight, aFrame, h)) { |
|
3587 h = std::max(0, h - heightTakenByBoxSizing); |
|
3588 result = |
|
3589 NSToCoordRound(h * (float(ratio.width) / float(ratio.height))); |
|
3590 } |
|
3591 |
|
3592 if (GetAbsoluteCoord(styleMaxHeight, h) || |
|
3593 GetPercentHeight(styleMaxHeight, aFrame, h)) { |
|
3594 h = std::max(0, h - heightTakenByBoxSizing); |
|
3595 nscoord maxWidth = |
|
3596 NSToCoordRound(h * (float(ratio.width) / float(ratio.height))); |
|
3597 if (maxWidth < result) |
|
3598 result = maxWidth; |
|
3599 } |
|
3600 |
|
3601 if (GetAbsoluteCoord(styleMinHeight, h) || |
|
3602 GetPercentHeight(styleMinHeight, aFrame, h)) { |
|
3603 h = std::max(0, h - heightTakenByBoxSizing); |
|
3604 nscoord minWidth = |
|
3605 NSToCoordRound(h * (float(ratio.width) / float(ratio.height))); |
|
3606 if (minWidth > result) |
|
3607 result = minWidth; |
|
3608 } |
|
3609 } |
|
3610 } |
|
3611 } |
|
3612 |
|
3613 if (aFrame->GetType() == nsGkAtoms::tableFrame) { |
|
3614 // Tables can't shrink smaller than their intrinsic minimum width, |
|
3615 // no matter what. |
|
3616 min = aFrame->GetMinWidth(aRenderingContext); |
|
3617 } |
|
3618 |
|
3619 // We also need to track what has been added on outside of the box |
|
3620 // (controlled by 'box-sizing') where 'width', 'min-width' and |
|
3621 // 'max-width' are applied. We have to account for these properties |
|
3622 // after getting all the offsets (margin, border, padding) because |
|
3623 // percentages do not operate linearly. |
|
3624 // Doing this is ok because although percentages aren't handled |
|
3625 // linearly, they are handled monotonically. |
|
3626 nscoord coordOutsideWidth = 0; |
|
3627 float pctOutsideWidth = 0; |
|
3628 float pctTotal = 0.0f; |
|
3629 |
|
3630 if (!(aFlags & IGNORE_PADDING)) { |
|
3631 coordOutsideWidth += offsets.hPadding; |
|
3632 pctOutsideWidth += offsets.hPctPadding; |
|
3633 |
|
3634 if (boxSizing == NS_STYLE_BOX_SIZING_PADDING) { |
|
3635 min += coordOutsideWidth; |
|
3636 result = NSCoordSaturatingAdd(result, coordOutsideWidth); |
|
3637 pctTotal += pctOutsideWidth; |
|
3638 |
|
3639 coordOutsideWidth = 0; |
|
3640 pctOutsideWidth = 0.0f; |
|
3641 } |
|
3642 } |
|
3643 |
|
3644 coordOutsideWidth += offsets.hBorder; |
|
3645 |
|
3646 if (boxSizing == NS_STYLE_BOX_SIZING_BORDER) { |
|
3647 min += coordOutsideWidth; |
|
3648 result = NSCoordSaturatingAdd(result, coordOutsideWidth); |
|
3649 pctTotal += pctOutsideWidth; |
|
3650 |
|
3651 coordOutsideWidth = 0; |
|
3652 pctOutsideWidth = 0.0f; |
|
3653 } |
|
3654 |
|
3655 coordOutsideWidth += offsets.hMargin; |
|
3656 pctOutsideWidth += offsets.hPctMargin; |
|
3657 |
|
3658 min += coordOutsideWidth; |
|
3659 result = NSCoordSaturatingAdd(result, coordOutsideWidth); |
|
3660 pctTotal += pctOutsideWidth; |
|
3661 |
|
3662 nscoord w; |
|
3663 if (GetAbsoluteCoord(styleWidth, w) || |
|
3664 GetIntrinsicCoord(styleWidth, aRenderingContext, aFrame, |
|
3665 PROP_WIDTH, w)) { |
|
3666 result = AddPercents(aType, w + coordOutsideWidth, pctOutsideWidth); |
|
3667 } |
|
3668 else if (aType == MIN_WIDTH && |
|
3669 // The only cases of coord-percent-calc() units that |
|
3670 // GetAbsoluteCoord didn't handle are percent and calc()s |
|
3671 // containing percent. |
|
3672 styleWidth.IsCoordPercentCalcUnit() && |
|
3673 aFrame->IsFrameOfType(nsIFrame::eReplaced)) { |
|
3674 // A percentage width on replaced elements means they can shrink to 0. |
|
3675 result = 0; // let |min| handle padding/border/margin |
|
3676 } |
|
3677 else { |
|
3678 // NOTE: We could really do a lot better for percents and for some |
|
3679 // cases of calc() containing percent (certainly including any where |
|
3680 // the coefficient on the percent is positive and there are no max() |
|
3681 // expressions). However, doing better for percents wouldn't be |
|
3682 // backwards compatible. |
|
3683 result = AddPercents(aType, result, pctTotal); |
|
3684 } |
|
3685 |
|
3686 if (haveFixedMaxWidth || |
|
3687 GetIntrinsicCoord(styleMaxWidth, aRenderingContext, aFrame, |
|
3688 PROP_MAX_WIDTH, maxw)) { |
|
3689 maxw = AddPercents(aType, maxw + coordOutsideWidth, pctOutsideWidth); |
|
3690 if (result > maxw) |
|
3691 result = maxw; |
|
3692 } |
|
3693 |
|
3694 if (haveFixedMinWidth || |
|
3695 GetIntrinsicCoord(styleMinWidth, aRenderingContext, aFrame, |
|
3696 PROP_MIN_WIDTH, minw)) { |
|
3697 minw = AddPercents(aType, minw + coordOutsideWidth, pctOutsideWidth); |
|
3698 if (result < minw) |
|
3699 result = minw; |
|
3700 } |
|
3701 |
|
3702 min = AddPercents(aType, min, pctTotal); |
|
3703 if (result < min) |
|
3704 result = min; |
|
3705 |
|
3706 const nsStyleDisplay *disp = aFrame->StyleDisplay(); |
|
3707 if (aFrame->IsThemed(disp)) { |
|
3708 nsIntSize size(0, 0); |
|
3709 bool canOverride = true; |
|
3710 nsPresContext *presContext = aFrame->PresContext(); |
|
3711 presContext->GetTheme()-> |
|
3712 GetMinimumWidgetSize(aRenderingContext, aFrame, disp->mAppearance, |
|
3713 &size, &canOverride); |
|
3714 |
|
3715 nscoord themeWidth = presContext->DevPixelsToAppUnits(size.width); |
|
3716 |
|
3717 // GMWS() returns a border-box width |
|
3718 themeWidth += offsets.hMargin; |
|
3719 themeWidth = AddPercents(aType, themeWidth, offsets.hPctMargin); |
|
3720 |
|
3721 if (themeWidth > result || !canOverride) |
|
3722 result = themeWidth; |
|
3723 } |
|
3724 |
|
3725 #ifdef DEBUG_INTRINSIC_WIDTH |
|
3726 nsFrame::IndentBy(stderr, gNoiseIndent); |
|
3727 static_cast<nsFrame*>(aFrame)->ListTag(stderr); |
|
3728 printf_stderr(" %s intrinsic width for container is %d twips.\n", |
|
3729 aType == MIN_WIDTH ? "min" : "pref", result); |
|
3730 #endif |
|
3731 |
|
3732 return result; |
|
3733 } |
|
3734 |
|
3735 /* static */ nscoord |
|
3736 nsLayoutUtils::ComputeCBDependentValue(nscoord aPercentBasis, |
|
3737 const nsStyleCoord& aCoord) |
|
3738 { |
|
3739 NS_WARN_IF_FALSE(aPercentBasis != NS_UNCONSTRAINEDSIZE, |
|
3740 "have unconstrained width or height; this should only " |
|
3741 "result from very large sizes, not attempts at intrinsic " |
|
3742 "size calculation"); |
|
3743 |
|
3744 if (aCoord.IsCoordPercentCalcUnit()) { |
|
3745 return nsRuleNode::ComputeCoordPercentCalc(aCoord, aPercentBasis); |
|
3746 } |
|
3747 NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None || |
|
3748 aCoord.GetUnit() == eStyleUnit_Auto, |
|
3749 "unexpected width value"); |
|
3750 return 0; |
|
3751 } |
|
3752 |
|
3753 /* static */ nscoord |
|
3754 nsLayoutUtils::ComputeWidthValue( |
|
3755 nsRenderingContext* aRenderingContext, |
|
3756 nsIFrame* aFrame, |
|
3757 nscoord aContainingBlockWidth, |
|
3758 nscoord aContentEdgeToBoxSizing, |
|
3759 nscoord aBoxSizingToMarginEdge, |
|
3760 const nsStyleCoord& aCoord) |
|
3761 { |
|
3762 NS_PRECONDITION(aFrame, "non-null frame expected"); |
|
3763 NS_PRECONDITION(aRenderingContext, "non-null rendering context expected"); |
|
3764 NS_WARN_IF_FALSE(aContainingBlockWidth != NS_UNCONSTRAINEDSIZE, |
|
3765 "have unconstrained width; this should only result from " |
|
3766 "very large sizes, not attempts at intrinsic width " |
|
3767 "calculation"); |
|
3768 NS_PRECONDITION(aContainingBlockWidth >= 0, |
|
3769 "width less than zero"); |
|
3770 |
|
3771 nscoord result; |
|
3772 if (aCoord.IsCoordPercentCalcUnit()) { |
|
3773 result = nsRuleNode::ComputeCoordPercentCalc(aCoord, |
|
3774 aContainingBlockWidth); |
|
3775 // The result of a calc() expression might be less than 0; we |
|
3776 // should clamp at runtime (below). (Percentages and coords that |
|
3777 // are less than 0 have already been dropped by the parser.) |
|
3778 result -= aContentEdgeToBoxSizing; |
|
3779 } else { |
|
3780 MOZ_ASSERT(eStyleUnit_Enumerated == aCoord.GetUnit()); |
|
3781 // If aFrame is a container for font size inflation, then shrink |
|
3782 // wrapping inside of it should not apply font size inflation. |
|
3783 AutoMaybeDisableFontInflation an(aFrame); |
|
3784 |
|
3785 int32_t val = aCoord.GetIntValue(); |
|
3786 switch (val) { |
|
3787 case NS_STYLE_WIDTH_MAX_CONTENT: |
|
3788 result = aFrame->GetPrefWidth(aRenderingContext); |
|
3789 NS_ASSERTION(result >= 0, "width less than zero"); |
|
3790 break; |
|
3791 case NS_STYLE_WIDTH_MIN_CONTENT: |
|
3792 result = aFrame->GetMinWidth(aRenderingContext); |
|
3793 NS_ASSERTION(result >= 0, "width less than zero"); |
|
3794 break; |
|
3795 case NS_STYLE_WIDTH_FIT_CONTENT: |
|
3796 { |
|
3797 nscoord pref = aFrame->GetPrefWidth(aRenderingContext), |
|
3798 min = aFrame->GetMinWidth(aRenderingContext), |
|
3799 fill = aContainingBlockWidth - |
|
3800 (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing); |
|
3801 result = std::max(min, std::min(pref, fill)); |
|
3802 NS_ASSERTION(result >= 0, "width less than zero"); |
|
3803 } |
|
3804 break; |
|
3805 case NS_STYLE_WIDTH_AVAILABLE: |
|
3806 result = aContainingBlockWidth - |
|
3807 (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing); |
|
3808 } |
|
3809 } |
|
3810 |
|
3811 return std::max(0, result); |
|
3812 } |
|
3813 |
|
3814 /* static */ nscoord |
|
3815 nsLayoutUtils::ComputeHeightDependentValue( |
|
3816 nscoord aContainingBlockHeight, |
|
3817 const nsStyleCoord& aCoord) |
|
3818 { |
|
3819 // XXXldb Some callers explicitly check aContainingBlockHeight |
|
3820 // against NS_AUTOHEIGHT *and* unit against eStyleUnit_Percent or |
|
3821 // calc()s containing percents before calling this function. |
|
3822 // However, it would be much more likely to catch problems without |
|
3823 // the unit conditions. |
|
3824 // XXXldb Many callers pass a non-'auto' containing block height when |
|
3825 // according to CSS2.1 they should be passing 'auto'. |
|
3826 NS_PRECONDITION(NS_AUTOHEIGHT != aContainingBlockHeight || |
|
3827 !aCoord.HasPercent(), |
|
3828 "unexpected containing block height"); |
|
3829 |
|
3830 if (aCoord.IsCoordPercentCalcUnit()) { |
|
3831 return nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockHeight); |
|
3832 } |
|
3833 |
|
3834 NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None || |
|
3835 aCoord.GetUnit() == eStyleUnit_Auto, |
|
3836 "unexpected height value"); |
|
3837 return 0; |
|
3838 } |
|
3839 |
|
3840 /* static */ void |
|
3841 nsLayoutUtils::MarkDescendantsDirty(nsIFrame *aSubtreeRoot) |
|
3842 { |
|
3843 nsAutoTArray<nsIFrame*, 4> subtrees; |
|
3844 subtrees.AppendElement(aSubtreeRoot); |
|
3845 |
|
3846 // dirty descendants, iterating over subtrees that may include |
|
3847 // additional subtrees associated with placeholders |
|
3848 do { |
|
3849 nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1); |
|
3850 subtrees.RemoveElementAt(subtrees.Length() - 1); |
|
3851 |
|
3852 // Mark all descendants dirty (using an nsTArray stack rather than |
|
3853 // recursion). |
|
3854 // Note that nsHTMLReflowState::InitResizeFlags has some similar |
|
3855 // code; see comments there for how and why it differs. |
|
3856 nsAutoTArray<nsIFrame*, 32> stack; |
|
3857 stack.AppendElement(subtreeRoot); |
|
3858 |
|
3859 do { |
|
3860 nsIFrame *f = stack.ElementAt(stack.Length() - 1); |
|
3861 stack.RemoveElementAt(stack.Length() - 1); |
|
3862 |
|
3863 f->MarkIntrinsicWidthsDirty(); |
|
3864 |
|
3865 if (f->GetType() == nsGkAtoms::placeholderFrame) { |
|
3866 nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f); |
|
3867 if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) { |
|
3868 // We have another distinct subtree we need to mark. |
|
3869 subtrees.AppendElement(oof); |
|
3870 } |
|
3871 } |
|
3872 |
|
3873 nsIFrame::ChildListIterator lists(f); |
|
3874 for (; !lists.IsDone(); lists.Next()) { |
|
3875 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
3876 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
3877 nsIFrame* kid = childFrames.get(); |
|
3878 stack.AppendElement(kid); |
|
3879 } |
|
3880 } |
|
3881 } while (stack.Length() != 0); |
|
3882 } while (subtrees.Length() != 0); |
|
3883 } |
|
3884 |
|
3885 #define MULDIV(a,b,c) (nscoord(int64_t(a) * int64_t(b) / int64_t(c))) |
|
3886 |
|
3887 /* static */ nsSize |
|
3888 nsLayoutUtils::ComputeSizeWithIntrinsicDimensions( |
|
3889 nsRenderingContext* aRenderingContext, nsIFrame* aFrame, |
|
3890 const IntrinsicSize& aIntrinsicSize, |
|
3891 nsSize aIntrinsicRatio, nsSize aCBSize, |
|
3892 nsSize aMargin, nsSize aBorder, nsSize aPadding) |
|
3893 { |
|
3894 const nsStylePosition* stylePos = aFrame->StylePosition(); |
|
3895 |
|
3896 // If we're a flex item, we'll compute our size a bit differently. |
|
3897 const nsStyleCoord* widthStyleCoord = &(stylePos->mWidth); |
|
3898 const nsStyleCoord* heightStyleCoord = &(stylePos->mHeight); |
|
3899 |
|
3900 bool isFlexItem = aFrame->IsFlexItem(); |
|
3901 bool isHorizontalFlexItem = false; |
|
3902 |
|
3903 if (isFlexItem) { |
|
3904 // Flex items use their "flex-basis" property in place of their main-size |
|
3905 // property (e.g. "width") for sizing purposes, *unless* they have |
|
3906 // "flex-basis:auto", in which case they use their main-size property after |
|
3907 // all. |
|
3908 uint32_t flexDirection = |
|
3909 aFrame->GetParent()->StylePosition()->mFlexDirection; |
|
3910 isHorizontalFlexItem = |
|
3911 flexDirection == NS_STYLE_FLEX_DIRECTION_ROW || |
|
3912 flexDirection == NS_STYLE_FLEX_DIRECTION_ROW_REVERSE; |
|
3913 |
|
3914 // NOTE: The logic here should match the similar chunk for determining |
|
3915 // widthStyleCoord and heightStyleCoord in nsFrame::ComputeSize(). |
|
3916 const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis); |
|
3917 if (flexBasis->GetUnit() != eStyleUnit_Auto) { |
|
3918 if (isHorizontalFlexItem) { |
|
3919 widthStyleCoord = flexBasis; |
|
3920 } else { |
|
3921 // One caveat for vertical flex items: We don't support enumerated |
|
3922 // values (e.g. "max-content") for height properties yet. So, if our |
|
3923 // computed flex-basis is an enumerated value, we'll just behave as if |
|
3924 // it were "auto", which means "use the main-size property after all" |
|
3925 // (which is "height", in this case). |
|
3926 // NOTE: Once we support intrinsic sizing keywords for "height", |
|
3927 // we should remove this check. |
|
3928 if (flexBasis->GetUnit() != eStyleUnit_Enumerated) { |
|
3929 heightStyleCoord = flexBasis; |
|
3930 } |
|
3931 } |
|
3932 } |
|
3933 } |
|
3934 |
|
3935 // Handle intrinsic sizes and their interaction with |
|
3936 // {min-,max-,}{width,height} according to the rules in |
|
3937 // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths |
|
3938 |
|
3939 // Note: throughout the following section of the function, I avoid |
|
3940 // a * (b / c) because of its reduced accuracy relative to a * b / c |
|
3941 // or (a * b) / c (which are equivalent). |
|
3942 |
|
3943 const bool isAutoWidth = widthStyleCoord->GetUnit() == eStyleUnit_Auto; |
|
3944 const bool isAutoHeight = IsAutoHeight(*heightStyleCoord, aCBSize.height); |
|
3945 |
|
3946 nsSize boxSizingAdjust(0,0); |
|
3947 switch (stylePos->mBoxSizing) { |
|
3948 case NS_STYLE_BOX_SIZING_BORDER: |
|
3949 boxSizingAdjust += aBorder; |
|
3950 // fall through |
|
3951 case NS_STYLE_BOX_SIZING_PADDING: |
|
3952 boxSizingAdjust += aPadding; |
|
3953 } |
|
3954 nscoord boxSizingToMarginEdgeWidth = |
|
3955 aMargin.width + aBorder.width + aPadding.width - boxSizingAdjust.width; |
|
3956 |
|
3957 nscoord width, minWidth, maxWidth, height, minHeight, maxHeight; |
|
3958 |
|
3959 if (!isAutoWidth) { |
|
3960 width = nsLayoutUtils::ComputeWidthValue(aRenderingContext, |
|
3961 aFrame, aCBSize.width, boxSizingAdjust.width, |
|
3962 boxSizingToMarginEdgeWidth, *widthStyleCoord); |
|
3963 } |
|
3964 |
|
3965 if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None && |
|
3966 !(isFlexItem && isHorizontalFlexItem)) { |
|
3967 maxWidth = nsLayoutUtils::ComputeWidthValue(aRenderingContext, |
|
3968 aFrame, aCBSize.width, boxSizingAdjust.width, |
|
3969 boxSizingToMarginEdgeWidth, stylePos->mMaxWidth); |
|
3970 } else { |
|
3971 // NOTE: Flex items ignore their min & max sizing properties in their |
|
3972 // flex container's main-axis. (Those properties get applied later in |
|
3973 // the flexbox algorithm.) |
|
3974 maxWidth = nscoord_MAX; |
|
3975 } |
|
3976 |
|
3977 if (!(isFlexItem && isHorizontalFlexItem)) { |
|
3978 minWidth = nsLayoutUtils::ComputeWidthValue(aRenderingContext, |
|
3979 aFrame, aCBSize.width, boxSizingAdjust.width, |
|
3980 boxSizingToMarginEdgeWidth, stylePos->mMinWidth); |
|
3981 } else { |
|
3982 // NOTE: Flex items ignore their min & max sizing properties in their |
|
3983 // flex container's main-axis. (Those properties get applied later in |
|
3984 // the flexbox algorithm.) |
|
3985 minWidth = 0; |
|
3986 } |
|
3987 |
|
3988 if (!isAutoHeight) { |
|
3989 height = nsLayoutUtils::ComputeHeightValue(aCBSize.height, |
|
3990 boxSizingAdjust.height, |
|
3991 *heightStyleCoord); |
|
3992 } |
|
3993 |
|
3994 if (!IsAutoHeight(stylePos->mMaxHeight, aCBSize.height) && |
|
3995 !(isFlexItem && !isHorizontalFlexItem)) { |
|
3996 maxHeight = nsLayoutUtils::ComputeHeightValue(aCBSize.height, |
|
3997 boxSizingAdjust.height, |
|
3998 stylePos->mMaxHeight); |
|
3999 } else { |
|
4000 maxHeight = nscoord_MAX; |
|
4001 } |
|
4002 |
|
4003 if (!IsAutoHeight(stylePos->mMinHeight, aCBSize.height) && |
|
4004 !(isFlexItem && !isHorizontalFlexItem)) { |
|
4005 minHeight = nsLayoutUtils::ComputeHeightValue(aCBSize.height, |
|
4006 boxSizingAdjust.height, |
|
4007 stylePos->mMinHeight); |
|
4008 } else { |
|
4009 minHeight = 0; |
|
4010 } |
|
4011 |
|
4012 // Resolve percentage intrinsic width/height as necessary: |
|
4013 |
|
4014 NS_ASSERTION(aCBSize.width != NS_UNCONSTRAINEDSIZE, |
|
4015 "Our containing block must not have unconstrained width!"); |
|
4016 |
|
4017 bool hasIntrinsicWidth, hasIntrinsicHeight; |
|
4018 nscoord intrinsicWidth, intrinsicHeight; |
|
4019 |
|
4020 if (aIntrinsicSize.width.GetUnit() == eStyleUnit_Coord) { |
|
4021 hasIntrinsicWidth = true; |
|
4022 intrinsicWidth = aIntrinsicSize.width.GetCoordValue(); |
|
4023 if (intrinsicWidth < 0) |
|
4024 intrinsicWidth = 0; |
|
4025 } else { |
|
4026 NS_ASSERTION(aIntrinsicSize.width.GetUnit() == eStyleUnit_None, |
|
4027 "unexpected unit"); |
|
4028 hasIntrinsicWidth = false; |
|
4029 intrinsicWidth = 0; |
|
4030 } |
|
4031 |
|
4032 if (aIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) { |
|
4033 hasIntrinsicHeight = true; |
|
4034 intrinsicHeight = aIntrinsicSize.height.GetCoordValue(); |
|
4035 if (intrinsicHeight < 0) |
|
4036 intrinsicHeight = 0; |
|
4037 } else { |
|
4038 NS_ASSERTION(aIntrinsicSize.height.GetUnit() == eStyleUnit_None, |
|
4039 "unexpected unit"); |
|
4040 hasIntrinsicHeight = false; |
|
4041 intrinsicHeight = 0; |
|
4042 } |
|
4043 |
|
4044 NS_ASSERTION(aIntrinsicRatio.width >= 0 && aIntrinsicRatio.height >= 0, |
|
4045 "Intrinsic ratio has a negative component!"); |
|
4046 |
|
4047 // Now calculate the used values for width and height: |
|
4048 |
|
4049 if (isAutoWidth) { |
|
4050 if (isAutoHeight) { |
|
4051 |
|
4052 // 'auto' width, 'auto' height |
|
4053 |
|
4054 // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2: |
|
4055 |
|
4056 nscoord tentWidth, tentHeight; |
|
4057 |
|
4058 if (hasIntrinsicWidth) { |
|
4059 tentWidth = intrinsicWidth; |
|
4060 } else if (hasIntrinsicHeight && aIntrinsicRatio.height > 0) { |
|
4061 tentWidth = MULDIV(intrinsicHeight, aIntrinsicRatio.width, aIntrinsicRatio.height); |
|
4062 } else if (aIntrinsicRatio.width > 0) { |
|
4063 tentWidth = aCBSize.width - boxSizingToMarginEdgeWidth; // XXX scrollbar? |
|
4064 if (tentWidth < 0) tentWidth = 0; |
|
4065 } else { |
|
4066 tentWidth = nsPresContext::CSSPixelsToAppUnits(300); |
|
4067 } |
|
4068 |
|
4069 if (hasIntrinsicHeight) { |
|
4070 tentHeight = intrinsicHeight; |
|
4071 } else if (aIntrinsicRatio.width > 0) { |
|
4072 tentHeight = MULDIV(tentWidth, aIntrinsicRatio.height, aIntrinsicRatio.width); |
|
4073 } else { |
|
4074 tentHeight = nsPresContext::CSSPixelsToAppUnits(150); |
|
4075 } |
|
4076 |
|
4077 return ComputeAutoSizeWithIntrinsicDimensions(minWidth, minHeight, |
|
4078 maxWidth, maxHeight, |
|
4079 tentWidth, tentHeight); |
|
4080 } else { |
|
4081 |
|
4082 // 'auto' width, non-'auto' height |
|
4083 height = NS_CSS_MINMAX(height, minHeight, maxHeight); |
|
4084 if (aIntrinsicRatio.height > 0) { |
|
4085 width = MULDIV(height, aIntrinsicRatio.width, aIntrinsicRatio.height); |
|
4086 } else if (hasIntrinsicWidth) { |
|
4087 width = intrinsicWidth; |
|
4088 } else { |
|
4089 width = nsPresContext::CSSPixelsToAppUnits(300); |
|
4090 } |
|
4091 width = NS_CSS_MINMAX(width, minWidth, maxWidth); |
|
4092 |
|
4093 } |
|
4094 } else { |
|
4095 if (isAutoHeight) { |
|
4096 |
|
4097 // non-'auto' width, 'auto' height |
|
4098 width = NS_CSS_MINMAX(width, minWidth, maxWidth); |
|
4099 if (aIntrinsicRatio.width > 0) { |
|
4100 height = MULDIV(width, aIntrinsicRatio.height, aIntrinsicRatio.width); |
|
4101 } else if (hasIntrinsicHeight) { |
|
4102 height = intrinsicHeight; |
|
4103 } else { |
|
4104 height = nsPresContext::CSSPixelsToAppUnits(150); |
|
4105 } |
|
4106 height = NS_CSS_MINMAX(height, minHeight, maxHeight); |
|
4107 |
|
4108 } else { |
|
4109 |
|
4110 // non-'auto' width, non-'auto' height |
|
4111 width = NS_CSS_MINMAX(width, minWidth, maxWidth); |
|
4112 height = NS_CSS_MINMAX(height, minHeight, maxHeight); |
|
4113 |
|
4114 } |
|
4115 } |
|
4116 |
|
4117 return nsSize(width, height); |
|
4118 } |
|
4119 |
|
4120 nsSize |
|
4121 nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(nscoord minWidth, nscoord minHeight, |
|
4122 nscoord maxWidth, nscoord maxHeight, |
|
4123 nscoord tentWidth, nscoord tentHeight) |
|
4124 { |
|
4125 // Now apply min/max-width/height - CSS 2.1 sections 10.4 and 10.7: |
|
4126 |
|
4127 if (minWidth > maxWidth) |
|
4128 maxWidth = minWidth; |
|
4129 if (minHeight > maxHeight) |
|
4130 maxHeight = minHeight; |
|
4131 |
|
4132 nscoord heightAtMaxWidth, heightAtMinWidth, |
|
4133 widthAtMaxHeight, widthAtMinHeight; |
|
4134 |
|
4135 if (tentWidth > 0) { |
|
4136 heightAtMaxWidth = MULDIV(maxWidth, tentHeight, tentWidth); |
|
4137 if (heightAtMaxWidth < minHeight) |
|
4138 heightAtMaxWidth = minHeight; |
|
4139 heightAtMinWidth = MULDIV(minWidth, tentHeight, tentWidth); |
|
4140 if (heightAtMinWidth > maxHeight) |
|
4141 heightAtMinWidth = maxHeight; |
|
4142 } else { |
|
4143 heightAtMaxWidth = heightAtMinWidth = NS_CSS_MINMAX(tentHeight, minHeight, maxHeight); |
|
4144 } |
|
4145 |
|
4146 if (tentHeight > 0) { |
|
4147 widthAtMaxHeight = MULDIV(maxHeight, tentWidth, tentHeight); |
|
4148 if (widthAtMaxHeight < minWidth) |
|
4149 widthAtMaxHeight = minWidth; |
|
4150 widthAtMinHeight = MULDIV(minHeight, tentWidth, tentHeight); |
|
4151 if (widthAtMinHeight > maxWidth) |
|
4152 widthAtMinHeight = maxWidth; |
|
4153 } else { |
|
4154 widthAtMaxHeight = widthAtMinHeight = NS_CSS_MINMAX(tentWidth, minWidth, maxWidth); |
|
4155 } |
|
4156 |
|
4157 // The table at http://www.w3.org/TR/CSS21/visudet.html#min-max-widths : |
|
4158 |
|
4159 nscoord width, height; |
|
4160 |
|
4161 if (tentWidth > maxWidth) { |
|
4162 if (tentHeight > maxHeight) { |
|
4163 if (int64_t(maxWidth) * int64_t(tentHeight) <= |
|
4164 int64_t(maxHeight) * int64_t(tentWidth)) { |
|
4165 width = maxWidth; |
|
4166 height = heightAtMaxWidth; |
|
4167 } else { |
|
4168 width = widthAtMaxHeight; |
|
4169 height = maxHeight; |
|
4170 } |
|
4171 } else { |
|
4172 // This also covers "(w > max-width) and (h < min-height)" since in |
|
4173 // that case (max-width/w < 1), and with (h < min-height): |
|
4174 // max(max-width * h/w, min-height) == min-height |
|
4175 width = maxWidth; |
|
4176 height = heightAtMaxWidth; |
|
4177 } |
|
4178 } else if (tentWidth < minWidth) { |
|
4179 if (tentHeight < minHeight) { |
|
4180 if (int64_t(minWidth) * int64_t(tentHeight) <= |
|
4181 int64_t(minHeight) * int64_t(tentWidth)) { |
|
4182 width = widthAtMinHeight; |
|
4183 height = minHeight; |
|
4184 } else { |
|
4185 width = minWidth; |
|
4186 height = heightAtMinWidth; |
|
4187 } |
|
4188 } else { |
|
4189 // This also covers "(w < min-width) and (h > max-height)" since in |
|
4190 // that case (min-width/w > 1), and with (h > max-height): |
|
4191 // min(min-width * h/w, max-height) == max-height |
|
4192 width = minWidth; |
|
4193 height = heightAtMinWidth; |
|
4194 } |
|
4195 } else { |
|
4196 if (tentHeight > maxHeight) { |
|
4197 width = widthAtMaxHeight; |
|
4198 height = maxHeight; |
|
4199 } else if (tentHeight < minHeight) { |
|
4200 width = widthAtMinHeight; |
|
4201 height = minHeight; |
|
4202 } else { |
|
4203 width = tentWidth; |
|
4204 height = tentHeight; |
|
4205 } |
|
4206 } |
|
4207 |
|
4208 return nsSize(width, height); |
|
4209 } |
|
4210 |
|
4211 /* static */ nscoord |
|
4212 nsLayoutUtils::MinWidthFromInline(nsIFrame* aFrame, |
|
4213 nsRenderingContext* aRenderingContext) |
|
4214 { |
|
4215 NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(), |
|
4216 "should not be container for font size inflation"); |
|
4217 |
|
4218 nsIFrame::InlineMinWidthData data; |
|
4219 DISPLAY_MIN_WIDTH(aFrame, data.prevLines); |
|
4220 aFrame->AddInlineMinWidth(aRenderingContext, &data); |
|
4221 data.ForceBreak(aRenderingContext); |
|
4222 return data.prevLines; |
|
4223 } |
|
4224 |
|
4225 /* static */ nscoord |
|
4226 nsLayoutUtils::PrefWidthFromInline(nsIFrame* aFrame, |
|
4227 nsRenderingContext* aRenderingContext) |
|
4228 { |
|
4229 NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(), |
|
4230 "should not be container for font size inflation"); |
|
4231 |
|
4232 nsIFrame::InlinePrefWidthData data; |
|
4233 DISPLAY_PREF_WIDTH(aFrame, data.prevLines); |
|
4234 aFrame->AddInlinePrefWidth(aRenderingContext, &data); |
|
4235 data.ForceBreak(aRenderingContext); |
|
4236 return data.prevLines; |
|
4237 } |
|
4238 |
|
4239 static nscolor |
|
4240 DarkenColor(nscolor aColor) |
|
4241 { |
|
4242 uint16_t hue, sat, value; |
|
4243 uint8_t alpha; |
|
4244 |
|
4245 // convert the RBG to HSV so we can get the lightness (which is the v) |
|
4246 NS_RGB2HSV(aColor, hue, sat, value, alpha); |
|
4247 |
|
4248 // The goal here is to send white to black while letting colored |
|
4249 // stuff stay colored... So we adopt the following approach. |
|
4250 // Something with sat = 0 should end up with value = 0. Something |
|
4251 // with a high sat can end up with a high value and it's ok.... At |
|
4252 // the same time, we don't want to make things lighter. Do |
|
4253 // something simple, since it seems to work. |
|
4254 if (value > sat) { |
|
4255 value = sat; |
|
4256 // convert this color back into the RGB color space. |
|
4257 NS_HSV2RGB(aColor, hue, sat, value, alpha); |
|
4258 } |
|
4259 return aColor; |
|
4260 } |
|
4261 |
|
4262 // Check whether we should darken text/decoration colors. We need to do this if |
|
4263 // background images and colors are being suppressed, because that means |
|
4264 // light text will not be visible against the (presumed light-colored) background. |
|
4265 static bool |
|
4266 ShouldDarkenColors(nsPresContext* aPresContext) |
|
4267 { |
|
4268 return !aPresContext->GetBackgroundColorDraw() && |
|
4269 !aPresContext->GetBackgroundImageDraw(); |
|
4270 } |
|
4271 |
|
4272 nscolor |
|
4273 nsLayoutUtils::GetColor(nsIFrame* aFrame, nsCSSProperty aProperty) |
|
4274 { |
|
4275 if (aProperty == eCSSProperty_color) |
|
4276 { |
|
4277 nscolor nativeColor = NS_RGB(0, 0, 0); |
|
4278 if (GetNativeTextColor(aFrame, nativeColor)) |
|
4279 return nativeColor; |
|
4280 } |
|
4281 |
|
4282 nscolor color = aFrame->GetVisitedDependentColor(aProperty); |
|
4283 if (ShouldDarkenColors(aFrame->PresContext())) { |
|
4284 color = DarkenColor(color); |
|
4285 } |
|
4286 |
|
4287 return color; |
|
4288 } |
|
4289 |
|
4290 bool |
|
4291 nsLayoutUtils::GetNativeTextColor(nsIFrame* aFrame, nscolor& aColor) |
|
4292 { |
|
4293 nsPresContext *presContext = aFrame->PresContext(); |
|
4294 if (!presContext->IsChrome()) { |
|
4295 // If native appearance was used to draw the background of the containing |
|
4296 // frame, return a contrasting native foreground color instead of the |
|
4297 // color from the element's style. This avoids a problem where black |
|
4298 // text was displayed on a black background when a Windows theme such as |
|
4299 // "High Contrast Black" was used. The background is drawn inside |
|
4300 // nsNativeThemeWin::ClassicDrawWidgetBackground(). |
|
4301 // |
|
4302 // Because both the background color and this foreground color are used |
|
4303 // directly without exposing the colors via CSS computed styles, the |
|
4304 // native colors are not leaked to content. |
|
4305 nsIFrame* bgFrame = |
|
4306 nsCSSRendering::FindNonTransparentBackgroundFrame(aFrame); |
|
4307 if (bgFrame) { |
|
4308 const nsStyleDisplay* displayData = bgFrame->StyleDisplay(); |
|
4309 uint8_t widgetType = displayData->mAppearance; |
|
4310 nsITheme *theme = presContext->GetTheme(); |
|
4311 if (theme && widgetType && theme->ThemeSupportsWidget(presContext, |
|
4312 bgFrame, |
|
4313 widgetType)) { |
|
4314 bool isDisabled = false; |
|
4315 nsIContent* frameContent = bgFrame->GetContent(); |
|
4316 if (frameContent && frameContent->IsElement()) { |
|
4317 EventStates es = frameContent->AsElement()->State(); |
|
4318 isDisabled = es.HasState(NS_EVENT_STATE_DISABLED); |
|
4319 } |
|
4320 |
|
4321 if (NS_SUCCEEDED(LookAndFeel::GetColorForNativeAppearance(widgetType, |
|
4322 isDisabled, &aColor))) { |
|
4323 return true; |
|
4324 } |
|
4325 } |
|
4326 } |
|
4327 } |
|
4328 |
|
4329 return false; |
|
4330 } |
|
4331 |
|
4332 gfxFloat |
|
4333 nsLayoutUtils::GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext, |
|
4334 nscoord aY, nscoord aAscent) |
|
4335 { |
|
4336 gfxFloat appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
4337 gfxFloat baseline = gfxFloat(aY) + aAscent; |
|
4338 gfxRect putativeRect(0, baseline/appUnitsPerDevUnit, 1, 1); |
|
4339 if (!aContext->UserToDevicePixelSnapped(putativeRect, true)) |
|
4340 return baseline; |
|
4341 return aContext->DeviceToUser(putativeRect.TopLeft()).y * appUnitsPerDevUnit; |
|
4342 } |
|
4343 |
|
4344 void |
|
4345 nsLayoutUtils::DrawString(const nsIFrame* aFrame, |
|
4346 nsRenderingContext* aContext, |
|
4347 const char16_t* aString, |
|
4348 int32_t aLength, |
|
4349 nsPoint aPoint, |
|
4350 nsStyleContext* aStyleContext) |
|
4351 { |
|
4352 nsresult rv = NS_ERROR_FAILURE; |
|
4353 nsPresContext* presContext = aFrame->PresContext(); |
|
4354 if (presContext->BidiEnabled()) { |
|
4355 nsBidiLevel level = |
|
4356 nsBidiPresUtils::BidiLevelFromStyle(aStyleContext ? |
|
4357 aStyleContext : aFrame->StyleContext()); |
|
4358 rv = nsBidiPresUtils::RenderText(aString, aLength, level, |
|
4359 presContext, *aContext, *aContext, |
|
4360 aPoint.x, aPoint.y); |
|
4361 } |
|
4362 if (NS_FAILED(rv)) |
|
4363 { |
|
4364 aContext->SetTextRunRTL(false); |
|
4365 aContext->DrawString(aString, aLength, aPoint.x, aPoint.y); |
|
4366 } |
|
4367 } |
|
4368 |
|
4369 nscoord |
|
4370 nsLayoutUtils::GetStringWidth(const nsIFrame* aFrame, |
|
4371 nsRenderingContext* aContext, |
|
4372 const char16_t* aString, |
|
4373 int32_t aLength) |
|
4374 { |
|
4375 nsPresContext* presContext = aFrame->PresContext(); |
|
4376 if (presContext->BidiEnabled()) { |
|
4377 nsBidiLevel level = |
|
4378 nsBidiPresUtils::BidiLevelFromStyle(aFrame->StyleContext()); |
|
4379 return nsBidiPresUtils::MeasureTextWidth(aString, aLength, |
|
4380 level, presContext, *aContext); |
|
4381 } |
|
4382 aContext->SetTextRunRTL(false); |
|
4383 return aContext->GetWidth(aString, aLength); |
|
4384 } |
|
4385 |
|
4386 /* static */ void |
|
4387 nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame, |
|
4388 nsRenderingContext* aContext, |
|
4389 const nsRect& aTextRect, |
|
4390 const nsRect& aDirtyRect, |
|
4391 const nscolor& aForegroundColor, |
|
4392 TextShadowCallback aCallback, |
|
4393 void* aCallbackData) |
|
4394 { |
|
4395 const nsStyleText* textStyle = aFrame->StyleText(); |
|
4396 if (!textStyle->HasTextShadow()) |
|
4397 return; |
|
4398 |
|
4399 // Text shadow happens with the last value being painted at the back, |
|
4400 // ie. it is painted first. |
|
4401 gfxContext* aDestCtx = aContext->ThebesContext(); |
|
4402 for (uint32_t i = textStyle->mTextShadow->Length(); i > 0; --i) { |
|
4403 nsCSSShadowItem* shadowDetails = textStyle->mTextShadow->ShadowAt(i - 1); |
|
4404 nsPoint shadowOffset(shadowDetails->mXOffset, |
|
4405 shadowDetails->mYOffset); |
|
4406 nscoord blurRadius = std::max(shadowDetails->mRadius, 0); |
|
4407 |
|
4408 nsRect shadowRect(aTextRect); |
|
4409 shadowRect.MoveBy(shadowOffset); |
|
4410 |
|
4411 nsPresContext* presCtx = aFrame->PresContext(); |
|
4412 nsContextBoxBlur contextBoxBlur; |
|
4413 gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius, |
|
4414 presCtx->AppUnitsPerDevPixel(), |
|
4415 aDestCtx, aDirtyRect, nullptr); |
|
4416 if (!shadowContext) |
|
4417 continue; |
|
4418 |
|
4419 nscolor shadowColor; |
|
4420 if (shadowDetails->mHasColor) |
|
4421 shadowColor = shadowDetails->mColor; |
|
4422 else |
|
4423 shadowColor = aForegroundColor; |
|
4424 |
|
4425 // Conjure an nsRenderingContext from a gfxContext for drawing the text |
|
4426 // to blur. |
|
4427 nsRefPtr<nsRenderingContext> renderingContext = new nsRenderingContext(); |
|
4428 renderingContext->Init(presCtx->DeviceContext(), shadowContext); |
|
4429 |
|
4430 aDestCtx->Save(); |
|
4431 aDestCtx->NewPath(); |
|
4432 aDestCtx->SetColor(gfxRGBA(shadowColor)); |
|
4433 |
|
4434 // The callback will draw whatever we want to blur as a shadow. |
|
4435 aCallback(renderingContext, shadowOffset, shadowColor, aCallbackData); |
|
4436 |
|
4437 contextBoxBlur.DoPaint(); |
|
4438 aDestCtx->Restore(); |
|
4439 } |
|
4440 } |
|
4441 |
|
4442 /* static */ nscoord |
|
4443 nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics* aFontMetrics, |
|
4444 nscoord aLineHeight) |
|
4445 { |
|
4446 nscoord fontAscent = aFontMetrics->MaxAscent(); |
|
4447 nscoord fontHeight = aFontMetrics->MaxHeight(); |
|
4448 |
|
4449 nscoord leading = aLineHeight - fontHeight; |
|
4450 return fontAscent + leading/2; |
|
4451 } |
|
4452 |
|
4453 |
|
4454 /* static */ bool |
|
4455 nsLayoutUtils::GetFirstLineBaseline(const nsIFrame* aFrame, nscoord* aResult) |
|
4456 { |
|
4457 LinePosition position; |
|
4458 if (!GetFirstLinePosition(aFrame, &position)) |
|
4459 return false; |
|
4460 *aResult = position.mBaseline; |
|
4461 return true; |
|
4462 } |
|
4463 |
|
4464 /* static */ bool |
|
4465 nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame, |
|
4466 LinePosition* aResult) |
|
4467 { |
|
4468 const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame)); |
|
4469 if (!block) { |
|
4470 // For the first-line baseline we also have to check for a table, and if |
|
4471 // so, use the baseline of its first row. |
|
4472 nsIAtom* fType = aFrame->GetType(); |
|
4473 if (fType == nsGkAtoms::tableOuterFrame) { |
|
4474 aResult->mTop = 0; |
|
4475 aResult->mBaseline = aFrame->GetBaseline(); |
|
4476 // This is what we want for the list bullet caller; not sure if |
|
4477 // other future callers will want the same. |
|
4478 aResult->mBottom = aFrame->GetSize().height; |
|
4479 return true; |
|
4480 } |
|
4481 |
|
4482 // For first-line baselines, we have to consider scroll frames. |
|
4483 if (fType == nsGkAtoms::scrollFrame) { |
|
4484 nsIScrollableFrame *sFrame = do_QueryFrame(const_cast<nsIFrame*>(aFrame)); |
|
4485 if (!sFrame) { |
|
4486 NS_NOTREACHED("not scroll frame"); |
|
4487 } |
|
4488 LinePosition kidPosition; |
|
4489 if (GetFirstLinePosition(sFrame->GetScrolledFrame(), &kidPosition)) { |
|
4490 // Consider only the border and padding that contributes to the |
|
4491 // kid's position, not the scrolling, so we get the initial |
|
4492 // position. |
|
4493 *aResult = kidPosition + aFrame->GetUsedBorderAndPadding().top; |
|
4494 return true; |
|
4495 } |
|
4496 return false; |
|
4497 } |
|
4498 |
|
4499 if (fType == nsGkAtoms::fieldSetFrame) { |
|
4500 LinePosition kidPosition; |
|
4501 nsIFrame* kid = aFrame->GetFirstPrincipalChild(); |
|
4502 // kid might be a legend frame here, but that's ok. |
|
4503 if (GetFirstLinePosition(kid, &kidPosition)) { |
|
4504 *aResult = kidPosition + kid->GetNormalPosition().y; |
|
4505 return true; |
|
4506 } |
|
4507 return false; |
|
4508 } |
|
4509 |
|
4510 // No baseline. |
|
4511 return false; |
|
4512 } |
|
4513 |
|
4514 for (nsBlockFrame::const_line_iterator line = block->begin_lines(), |
|
4515 line_end = block->end_lines(); |
|
4516 line != line_end; ++line) { |
|
4517 if (line->IsBlock()) { |
|
4518 nsIFrame *kid = line->mFirstChild; |
|
4519 LinePosition kidPosition; |
|
4520 if (GetFirstLinePosition(kid, &kidPosition)) { |
|
4521 *aResult = kidPosition + kid->GetNormalPosition().y; |
|
4522 return true; |
|
4523 } |
|
4524 } else { |
|
4525 // XXX Is this the right test? We have some bogus empty lines |
|
4526 // floating around, but IsEmpty is perhaps too weak. |
|
4527 if (line->BSize() != 0 || !line->IsEmpty()) { |
|
4528 nscoord top = line->BStart(); |
|
4529 aResult->mTop = top; |
|
4530 aResult->mBaseline = top + line->GetAscent(); |
|
4531 aResult->mBottom = top + line->BSize(); |
|
4532 return true; |
|
4533 } |
|
4534 } |
|
4535 } |
|
4536 return false; |
|
4537 } |
|
4538 |
|
4539 /* static */ bool |
|
4540 nsLayoutUtils::GetLastLineBaseline(const nsIFrame* aFrame, nscoord* aResult) |
|
4541 { |
|
4542 const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame)); |
|
4543 if (!block) |
|
4544 // No baseline. (We intentionally don't descend into scroll frames.) |
|
4545 return false; |
|
4546 |
|
4547 for (nsBlockFrame::const_reverse_line_iterator line = block->rbegin_lines(), |
|
4548 line_end = block->rend_lines(); |
|
4549 line != line_end; ++line) { |
|
4550 if (line->IsBlock()) { |
|
4551 nsIFrame *kid = line->mFirstChild; |
|
4552 nscoord kidBaseline; |
|
4553 if (GetLastLineBaseline(kid, &kidBaseline)) { |
|
4554 // Ignore relative positioning for baseline calculations |
|
4555 *aResult = kidBaseline + kid->GetNormalPosition().y; |
|
4556 return true; |
|
4557 } else if (kid->GetType() == nsGkAtoms::scrollFrame) { |
|
4558 // Use the bottom of the scroll frame. |
|
4559 // XXX CSS2.1 really doesn't say what to do here. |
|
4560 *aResult = kid->GetNormalPosition().y + kid->GetRect().height; |
|
4561 return true; |
|
4562 } |
|
4563 } else { |
|
4564 // XXX Is this the right test? We have some bogus empty lines |
|
4565 // floating around, but IsEmpty is perhaps too weak. |
|
4566 if (line->BSize() != 0 || !line->IsEmpty()) { |
|
4567 *aResult = line->BStart() + line->GetAscent(); |
|
4568 return true; |
|
4569 } |
|
4570 } |
|
4571 } |
|
4572 return false; |
|
4573 } |
|
4574 |
|
4575 static nscoord |
|
4576 CalculateBlockContentBottom(nsBlockFrame* aFrame) |
|
4577 { |
|
4578 NS_PRECONDITION(aFrame, "null ptr"); |
|
4579 |
|
4580 nscoord contentBottom = 0; |
|
4581 |
|
4582 for (nsBlockFrame::line_iterator line = aFrame->begin_lines(), |
|
4583 line_end = aFrame->end_lines(); |
|
4584 line != line_end; ++line) { |
|
4585 if (line->IsBlock()) { |
|
4586 nsIFrame* child = line->mFirstChild; |
|
4587 nscoord offset = child->GetNormalPosition().y; |
|
4588 contentBottom = std::max(contentBottom, |
|
4589 nsLayoutUtils::CalculateContentBottom(child) + offset); |
|
4590 } |
|
4591 else { |
|
4592 contentBottom = std::max(contentBottom, line->BEnd()); |
|
4593 } |
|
4594 } |
|
4595 return contentBottom; |
|
4596 } |
|
4597 |
|
4598 /* static */ nscoord |
|
4599 nsLayoutUtils::CalculateContentBottom(nsIFrame* aFrame) |
|
4600 { |
|
4601 NS_PRECONDITION(aFrame, "null ptr"); |
|
4602 |
|
4603 nscoord contentBottom = aFrame->GetRect().height; |
|
4604 |
|
4605 // We want scrollable overflow rather than visual because this |
|
4606 // calculation is intended to affect layout. |
|
4607 if (aFrame->GetScrollableOverflowRect().height > contentBottom) { |
|
4608 nsIFrame::ChildListIDs skip(nsIFrame::kOverflowList | |
|
4609 nsIFrame::kExcessOverflowContainersList | |
|
4610 nsIFrame::kOverflowOutOfFlowList); |
|
4611 nsBlockFrame* blockFrame = GetAsBlock(aFrame); |
|
4612 if (blockFrame) { |
|
4613 contentBottom = |
|
4614 std::max(contentBottom, CalculateBlockContentBottom(blockFrame)); |
|
4615 skip |= nsIFrame::kPrincipalList; |
|
4616 } |
|
4617 nsIFrame::ChildListIterator lists(aFrame); |
|
4618 for (; !lists.IsDone(); lists.Next()) { |
|
4619 if (!skip.Contains(lists.CurrentID())) { |
|
4620 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
4621 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
4622 nsIFrame* child = childFrames.get(); |
|
4623 nscoord offset = child->GetNormalPosition().y; |
|
4624 contentBottom = std::max(contentBottom, |
|
4625 CalculateContentBottom(child) + offset); |
|
4626 } |
|
4627 } |
|
4628 } |
|
4629 } |
|
4630 return contentBottom; |
|
4631 } |
|
4632 |
|
4633 /* static */ nsIFrame* |
|
4634 nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame) |
|
4635 { |
|
4636 nsIFrame* layer; |
|
4637 for (layer = aFrame; layer; layer = layer->GetParent()) { |
|
4638 if (layer->IsPositioned() || |
|
4639 (layer->GetParent() && |
|
4640 layer->GetParent()->GetType() == nsGkAtoms::scrollFrame)) |
|
4641 break; |
|
4642 } |
|
4643 if (layer) |
|
4644 return layer; |
|
4645 return aFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame(); |
|
4646 } |
|
4647 |
|
4648 GraphicsFilter |
|
4649 nsLayoutUtils::GetGraphicsFilterForFrame(nsIFrame* aForFrame) |
|
4650 { |
|
4651 GraphicsFilter defaultFilter = GraphicsFilter::FILTER_GOOD; |
|
4652 nsStyleContext *sc; |
|
4653 if (nsCSSRendering::IsCanvasFrame(aForFrame)) { |
|
4654 nsCSSRendering::FindBackground(aForFrame, &sc); |
|
4655 } else { |
|
4656 sc = aForFrame->StyleContext(); |
|
4657 } |
|
4658 |
|
4659 switch (sc->StyleSVG()->mImageRendering) { |
|
4660 case NS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED: |
|
4661 return GraphicsFilter::FILTER_FAST; |
|
4662 case NS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY: |
|
4663 return GraphicsFilter::FILTER_BEST; |
|
4664 case NS_STYLE_IMAGE_RENDERING_CRISPEDGES: |
|
4665 return GraphicsFilter::FILTER_NEAREST; |
|
4666 default: |
|
4667 return defaultFilter; |
|
4668 } |
|
4669 } |
|
4670 |
|
4671 /** |
|
4672 * Given an image being drawn into an appunit coordinate system, and |
|
4673 * a point in that coordinate system, map the point back into image |
|
4674 * pixel space. |
|
4675 * @param aSize the size of the image, in pixels |
|
4676 * @param aDest the rectangle that the image is being mapped into |
|
4677 * @param aPt a point in the same coordinate system as the rectangle |
|
4678 */ |
|
4679 static gfxPoint |
|
4680 MapToFloatImagePixels(const gfxSize& aSize, |
|
4681 const gfxRect& aDest, const gfxPoint& aPt) |
|
4682 { |
|
4683 return gfxPoint(((aPt.x - aDest.X())*aSize.width)/aDest.Width(), |
|
4684 ((aPt.y - aDest.Y())*aSize.height)/aDest.Height()); |
|
4685 } |
|
4686 |
|
4687 /** |
|
4688 * Given an image being drawn into an pixel-based coordinate system, and |
|
4689 * a point in image space, map the point into the pixel-based coordinate |
|
4690 * system. |
|
4691 * @param aSize the size of the image, in pixels |
|
4692 * @param aDest the rectangle that the image is being mapped into |
|
4693 * @param aPt a point in image space |
|
4694 */ |
|
4695 static gfxPoint |
|
4696 MapToFloatUserPixels(const gfxSize& aSize, |
|
4697 const gfxRect& aDest, const gfxPoint& aPt) |
|
4698 { |
|
4699 return gfxPoint(aPt.x*aDest.Width()/aSize.width + aDest.X(), |
|
4700 aPt.y*aDest.Height()/aSize.height + aDest.Y()); |
|
4701 } |
|
4702 |
|
4703 /* static */ gfxRect |
|
4704 nsLayoutUtils::RectToGfxRect(const nsRect& aRect, int32_t aAppUnitsPerDevPixel) |
|
4705 { |
|
4706 return gfxRect(gfxFloat(aRect.x) / aAppUnitsPerDevPixel, |
|
4707 gfxFloat(aRect.y) / aAppUnitsPerDevPixel, |
|
4708 gfxFloat(aRect.width) / aAppUnitsPerDevPixel, |
|
4709 gfxFloat(aRect.height) / aAppUnitsPerDevPixel); |
|
4710 } |
|
4711 |
|
4712 struct SnappedImageDrawingParameters { |
|
4713 // A transform from either device space or user space (depending on mResetCTM) |
|
4714 // to image space |
|
4715 gfxMatrix mUserSpaceToImageSpace; |
|
4716 // A device-space, pixel-aligned rectangle to fill |
|
4717 gfxRect mFillRect; |
|
4718 // A pixel rectangle in tiled image space outside of which gfx should not |
|
4719 // sample (using EXTEND_PAD as necessary) |
|
4720 nsIntRect mSubimage; |
|
4721 // Whether there's anything to draw at all |
|
4722 bool mShouldDraw; |
|
4723 // true iff the CTM of the rendering context needs to be reset to the |
|
4724 // identity matrix before drawing |
|
4725 bool mResetCTM; |
|
4726 |
|
4727 SnappedImageDrawingParameters() |
|
4728 : mShouldDraw(false) |
|
4729 , mResetCTM(false) |
|
4730 {} |
|
4731 |
|
4732 SnappedImageDrawingParameters(const gfxMatrix& aUserSpaceToImageSpace, |
|
4733 const gfxRect& aFillRect, |
|
4734 const nsIntRect& aSubimage, |
|
4735 bool aResetCTM) |
|
4736 : mUserSpaceToImageSpace(aUserSpaceToImageSpace) |
|
4737 , mFillRect(aFillRect) |
|
4738 , mSubimage(aSubimage) |
|
4739 , mShouldDraw(true) |
|
4740 , mResetCTM(aResetCTM) |
|
4741 {} |
|
4742 }; |
|
4743 |
|
4744 /** |
|
4745 * Given a set of input parameters, compute certain output parameters |
|
4746 * for drawing an image with the image snapping algorithm. |
|
4747 * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering |
|
4748 * |
|
4749 * @see nsLayoutUtils::DrawImage() for the descriptions of input parameters |
|
4750 */ |
|
4751 static SnappedImageDrawingParameters |
|
4752 ComputeSnappedImageDrawingParameters(gfxContext* aCtx, |
|
4753 int32_t aAppUnitsPerDevPixel, |
|
4754 const nsRect aDest, |
|
4755 const nsRect aFill, |
|
4756 const nsPoint aAnchor, |
|
4757 const nsRect aDirty, |
|
4758 const nsIntSize aImageSize) |
|
4759 |
|
4760 { |
|
4761 if (aDest.IsEmpty() || aFill.IsEmpty() || !aImageSize.width || !aImageSize.height) |
|
4762 return SnappedImageDrawingParameters(); |
|
4763 |
|
4764 gfxRect devPixelDest = |
|
4765 nsLayoutUtils::RectToGfxRect(aDest, aAppUnitsPerDevPixel); |
|
4766 gfxRect devPixelFill = |
|
4767 nsLayoutUtils::RectToGfxRect(aFill, aAppUnitsPerDevPixel); |
|
4768 gfxRect devPixelDirty = |
|
4769 nsLayoutUtils::RectToGfxRect(aDirty, aAppUnitsPerDevPixel); |
|
4770 |
|
4771 gfxMatrix currentMatrix = aCtx->CurrentMatrix(); |
|
4772 gfxRect fill = devPixelFill; |
|
4773 bool didSnap; |
|
4774 // Snap even if we have a scale in the context. But don't snap if |
|
4775 // we have something that's not translation+scale, or if the scale flips in |
|
4776 // the X or Y direction, because snapped image drawing can't handle that yet. |
|
4777 if (!currentMatrix.HasNonAxisAlignedTransform() && |
|
4778 currentMatrix.xx > 0.0 && currentMatrix.yy > 0.0 && |
|
4779 aCtx->UserToDevicePixelSnapped(fill, true)) { |
|
4780 didSnap = true; |
|
4781 if (fill.IsEmpty()) { |
|
4782 return SnappedImageDrawingParameters(); |
|
4783 } |
|
4784 } else { |
|
4785 didSnap = false; |
|
4786 fill = devPixelFill; |
|
4787 } |
|
4788 |
|
4789 gfxSize imageSize(aImageSize.width, aImageSize.height); |
|
4790 |
|
4791 // Compute the set of pixels that would be sampled by an ideal rendering |
|
4792 gfxPoint subimageTopLeft = |
|
4793 MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.TopLeft()); |
|
4794 gfxPoint subimageBottomRight = |
|
4795 MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.BottomRight()); |
|
4796 nsIntRect intSubimage; |
|
4797 intSubimage.MoveTo(NSToIntFloor(subimageTopLeft.x), |
|
4798 NSToIntFloor(subimageTopLeft.y)); |
|
4799 intSubimage.SizeTo(NSToIntCeil(subimageBottomRight.x) - intSubimage.x, |
|
4800 NSToIntCeil(subimageBottomRight.y) - intSubimage.y); |
|
4801 |
|
4802 // Compute the anchor point and compute final fill rect. |
|
4803 // This code assumes that pixel-based devices have one pixel per |
|
4804 // device unit! |
|
4805 gfxPoint anchorPoint(gfxFloat(aAnchor.x)/aAppUnitsPerDevPixel, |
|
4806 gfxFloat(aAnchor.y)/aAppUnitsPerDevPixel); |
|
4807 gfxPoint imageSpaceAnchorPoint = |
|
4808 MapToFloatImagePixels(imageSize, devPixelDest, anchorPoint); |
|
4809 |
|
4810 if (didSnap) { |
|
4811 imageSpaceAnchorPoint.Round(); |
|
4812 anchorPoint = imageSpaceAnchorPoint; |
|
4813 anchorPoint = MapToFloatUserPixels(imageSize, devPixelDest, anchorPoint); |
|
4814 anchorPoint = currentMatrix.Transform(anchorPoint); |
|
4815 anchorPoint.Round(); |
|
4816 |
|
4817 // This form of Transform is safe to call since non-axis-aligned |
|
4818 // transforms wouldn't be snapped. |
|
4819 devPixelDirty = currentMatrix.Transform(devPixelDirty); |
|
4820 } |
|
4821 |
|
4822 gfxFloat scaleX = imageSize.width*aAppUnitsPerDevPixel/aDest.width; |
|
4823 gfxFloat scaleY = imageSize.height*aAppUnitsPerDevPixel/aDest.height; |
|
4824 if (didSnap) { |
|
4825 // We'll reset aCTX to the identity matrix before drawing, so we need to |
|
4826 // adjust our scales to match. |
|
4827 scaleX /= currentMatrix.xx; |
|
4828 scaleY /= currentMatrix.yy; |
|
4829 } |
|
4830 gfxFloat translateX = imageSpaceAnchorPoint.x - anchorPoint.x*scaleX; |
|
4831 gfxFloat translateY = imageSpaceAnchorPoint.y - anchorPoint.y*scaleY; |
|
4832 gfxMatrix transform(scaleX, 0, 0, scaleY, translateX, translateY); |
|
4833 |
|
4834 gfxRect finalFillRect = fill; |
|
4835 // If the user-space-to-image-space transform is not a straight |
|
4836 // translation by integers, then filtering will occur, and |
|
4837 // restricting the fill rect to the dirty rect would change the values |
|
4838 // computed for edge pixels, which we can't allow. |
|
4839 // Also, if didSnap is false then rounding out 'devPixelDirty' might not |
|
4840 // produce pixel-aligned coordinates, which would also break the values |
|
4841 // computed for edge pixels. |
|
4842 if (didSnap && !transform.HasNonIntegerTranslation()) { |
|
4843 devPixelDirty.RoundOut(); |
|
4844 finalFillRect = fill.Intersect(devPixelDirty); |
|
4845 } |
|
4846 if (finalFillRect.IsEmpty()) |
|
4847 return SnappedImageDrawingParameters(); |
|
4848 |
|
4849 return SnappedImageDrawingParameters(transform, finalFillRect, intSubimage, |
|
4850 didSnap); |
|
4851 } |
|
4852 |
|
4853 |
|
4854 static nsresult |
|
4855 DrawImageInternal(nsRenderingContext* aRenderingContext, |
|
4856 imgIContainer* aImage, |
|
4857 GraphicsFilter aGraphicsFilter, |
|
4858 const nsRect& aDest, |
|
4859 const nsRect& aFill, |
|
4860 const nsPoint& aAnchor, |
|
4861 const nsRect& aDirty, |
|
4862 const nsIntSize& aImageSize, |
|
4863 const SVGImageContext* aSVGContext, |
|
4864 uint32_t aImageFlags) |
|
4865 { |
|
4866 if (aDest.Contains(aFill)) { |
|
4867 aImageFlags |= imgIContainer::FLAG_CLAMP; |
|
4868 } |
|
4869 int32_t appUnitsPerDevPixel = aRenderingContext->AppUnitsPerDevPixel(); |
|
4870 gfxContext* ctx = aRenderingContext->ThebesContext(); |
|
4871 |
|
4872 SnappedImageDrawingParameters drawingParams = |
|
4873 ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest, aFill, |
|
4874 aAnchor, aDirty, aImageSize); |
|
4875 |
|
4876 if (!drawingParams.mShouldDraw) |
|
4877 return NS_OK; |
|
4878 |
|
4879 gfxContextMatrixAutoSaveRestore saveMatrix(ctx); |
|
4880 if (drawingParams.mResetCTM) { |
|
4881 ctx->IdentityMatrix(); |
|
4882 } |
|
4883 |
|
4884 aImage->Draw(ctx, aGraphicsFilter, drawingParams.mUserSpaceToImageSpace, |
|
4885 drawingParams.mFillRect, drawingParams.mSubimage, aImageSize, |
|
4886 aSVGContext, imgIContainer::FRAME_CURRENT, aImageFlags); |
|
4887 return NS_OK; |
|
4888 } |
|
4889 |
|
4890 /* static */ void |
|
4891 nsLayoutUtils::DrawPixelSnapped(nsRenderingContext* aRenderingContext, |
|
4892 gfxDrawable* aDrawable, |
|
4893 GraphicsFilter aFilter, |
|
4894 const nsRect& aDest, |
|
4895 const nsRect& aFill, |
|
4896 const nsPoint& aAnchor, |
|
4897 const nsRect& aDirty) |
|
4898 { |
|
4899 int32_t appUnitsPerDevPixel = aRenderingContext->AppUnitsPerDevPixel(); |
|
4900 gfxContext* ctx = aRenderingContext->ThebesContext(); |
|
4901 gfxIntSize drawableSize = aDrawable->Size(); |
|
4902 nsIntSize imageSize(drawableSize.width, drawableSize.height); |
|
4903 |
|
4904 SnappedImageDrawingParameters drawingParams = |
|
4905 ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest, aFill, |
|
4906 aAnchor, aDirty, imageSize); |
|
4907 |
|
4908 if (!drawingParams.mShouldDraw) |
|
4909 return; |
|
4910 |
|
4911 gfxContextMatrixAutoSaveRestore saveMatrix(ctx); |
|
4912 if (drawingParams.mResetCTM) { |
|
4913 ctx->IdentityMatrix(); |
|
4914 } |
|
4915 |
|
4916 gfxRect sourceRect = |
|
4917 drawingParams.mUserSpaceToImageSpace.Transform(drawingParams.mFillRect); |
|
4918 gfxRect imageRect(0, 0, imageSize.width, imageSize.height); |
|
4919 gfxRect subimage(drawingParams.mSubimage.x, drawingParams.mSubimage.y, |
|
4920 drawingParams.mSubimage.width, drawingParams.mSubimage.height); |
|
4921 |
|
4922 NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(), |
|
4923 "We must be allowed to sample *some* source pixels!"); |
|
4924 |
|
4925 gfxUtils::DrawPixelSnapped(ctx, aDrawable, |
|
4926 drawingParams.mUserSpaceToImageSpace, subimage, |
|
4927 sourceRect, imageRect, drawingParams.mFillRect, |
|
4928 gfxImageFormat::ARGB32, aFilter); |
|
4929 } |
|
4930 |
|
4931 /* static */ nsresult |
|
4932 nsLayoutUtils::DrawSingleUnscaledImage(nsRenderingContext* aRenderingContext, |
|
4933 imgIContainer* aImage, |
|
4934 GraphicsFilter aGraphicsFilter, |
|
4935 const nsPoint& aDest, |
|
4936 const nsRect* aDirty, |
|
4937 uint32_t aImageFlags, |
|
4938 const nsRect* aSourceArea) |
|
4939 { |
|
4940 nsIntSize imageSize; |
|
4941 aImage->GetWidth(&imageSize.width); |
|
4942 aImage->GetHeight(&imageSize.height); |
|
4943 NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE); |
|
4944 |
|
4945 nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel(); |
|
4946 nsSize size(imageSize.width*appUnitsPerCSSPixel, |
|
4947 imageSize.height*appUnitsPerCSSPixel); |
|
4948 |
|
4949 nsRect source; |
|
4950 if (aSourceArea) { |
|
4951 source = *aSourceArea; |
|
4952 } else { |
|
4953 source.SizeTo(size); |
|
4954 } |
|
4955 |
|
4956 nsRect dest(aDest - source.TopLeft(), size); |
|
4957 nsRect fill(aDest, source.Size()); |
|
4958 // Ensure that only a single image tile is drawn. If aSourceArea extends |
|
4959 // outside the image bounds, we want to honor the aSourceArea-to-aDest |
|
4960 // translation but we don't want to actually tile the image. |
|
4961 fill.IntersectRect(fill, dest); |
|
4962 return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, |
|
4963 dest, fill, aDest, aDirty ? *aDirty : dest, |
|
4964 imageSize, nullptr, aImageFlags); |
|
4965 } |
|
4966 |
|
4967 /* static */ nsresult |
|
4968 nsLayoutUtils::DrawSingleImage(nsRenderingContext* aRenderingContext, |
|
4969 imgIContainer* aImage, |
|
4970 GraphicsFilter aGraphicsFilter, |
|
4971 const nsRect& aDest, |
|
4972 const nsRect& aDirty, |
|
4973 const SVGImageContext* aSVGContext, |
|
4974 uint32_t aImageFlags, |
|
4975 const nsRect* aSourceArea) |
|
4976 { |
|
4977 nsIntSize imageSize; |
|
4978 if (aImage->GetType() == imgIContainer::TYPE_VECTOR) { |
|
4979 // We choose a size for vector images that emulates a raster image which |
|
4980 // is perfectly sized for the destination rect: each pixel in the image |
|
4981 // maps exactly to a single pixel on-screen. |
|
4982 nscoord appUnitsPerDevPx = aRenderingContext->AppUnitsPerDevPixel(); |
|
4983 imageSize.width = NSAppUnitsToIntPixels(aDest.width, appUnitsPerDevPx); |
|
4984 imageSize.height = NSAppUnitsToIntPixels(aDest.height, appUnitsPerDevPx); |
|
4985 } else { |
|
4986 // Raster images have an intrinsic size, so we just use that. |
|
4987 aImage->GetWidth(&imageSize.width); |
|
4988 aImage->GetHeight(&imageSize.height); |
|
4989 } |
|
4990 NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE); |
|
4991 |
|
4992 nsRect source; |
|
4993 if (aSourceArea) { |
|
4994 source = *aSourceArea; |
|
4995 } else { |
|
4996 nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel(); |
|
4997 source.SizeTo(imageSize.width*appUnitsPerCSSPixel, |
|
4998 imageSize.height*appUnitsPerCSSPixel); |
|
4999 } |
|
5000 |
|
5001 nsRect dest = nsLayoutUtils::GetWholeImageDestination(imageSize, source, |
|
5002 aDest); |
|
5003 // Ensure that only a single image tile is drawn. If aSourceArea extends |
|
5004 // outside the image bounds, we want to honor the aSourceArea-to-aDest |
|
5005 // transform but we don't want to actually tile the image. |
|
5006 nsRect fill; |
|
5007 fill.IntersectRect(aDest, dest); |
|
5008 return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, dest, fill, |
|
5009 fill.TopLeft(), aDirty, imageSize, aSVGContext, aImageFlags); |
|
5010 } |
|
5011 |
|
5012 /* static */ void |
|
5013 nsLayoutUtils::ComputeSizeForDrawing(imgIContainer *aImage, |
|
5014 nsIntSize& aImageSize, /*outparam*/ |
|
5015 nsSize& aIntrinsicRatio, /*outparam*/ |
|
5016 bool& aGotWidth, /*outparam*/ |
|
5017 bool& aGotHeight /*outparam*/) |
|
5018 { |
|
5019 aGotWidth = NS_SUCCEEDED(aImage->GetWidth(&aImageSize.width)); |
|
5020 aGotHeight = NS_SUCCEEDED(aImage->GetHeight(&aImageSize.height)); |
|
5021 bool gotRatio = NS_SUCCEEDED(aImage->GetIntrinsicRatio(&aIntrinsicRatio)); |
|
5022 |
|
5023 if (!(aGotWidth && aGotHeight) && !gotRatio) { |
|
5024 // We hit an error (say, because the image failed to load or couldn't be |
|
5025 // decoded) and should return zero size. |
|
5026 aGotWidth = aGotHeight = true; |
|
5027 aImageSize = nsIntSize(0, 0); |
|
5028 aIntrinsicRatio = nsSize(0, 0); |
|
5029 } |
|
5030 } |
|
5031 |
|
5032 |
|
5033 /* static */ nsresult |
|
5034 nsLayoutUtils::DrawBackgroundImage(nsRenderingContext* aRenderingContext, |
|
5035 imgIContainer* aImage, |
|
5036 const nsIntSize& aImageSize, |
|
5037 GraphicsFilter aGraphicsFilter, |
|
5038 const nsRect& aDest, |
|
5039 const nsRect& aFill, |
|
5040 const nsPoint& aAnchor, |
|
5041 const nsRect& aDirty, |
|
5042 uint32_t aImageFlags) |
|
5043 { |
|
5044 PROFILER_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage"); |
|
5045 |
|
5046 if (UseBackgroundNearestFiltering()) { |
|
5047 aGraphicsFilter = GraphicsFilter::FILTER_NEAREST; |
|
5048 } |
|
5049 |
|
5050 return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, |
|
5051 aDest, aFill, aAnchor, aDirty, |
|
5052 aImageSize, nullptr, aImageFlags); |
|
5053 } |
|
5054 |
|
5055 /* static */ nsresult |
|
5056 nsLayoutUtils::DrawImage(nsRenderingContext* aRenderingContext, |
|
5057 imgIContainer* aImage, |
|
5058 GraphicsFilter aGraphicsFilter, |
|
5059 const nsRect& aDest, |
|
5060 const nsRect& aFill, |
|
5061 const nsPoint& aAnchor, |
|
5062 const nsRect& aDirty, |
|
5063 uint32_t aImageFlags) |
|
5064 { |
|
5065 nsIntSize imageSize; |
|
5066 nsSize imageRatio; |
|
5067 bool gotHeight, gotWidth; |
|
5068 ComputeSizeForDrawing(aImage, imageSize, imageRatio, gotWidth, gotHeight); |
|
5069 |
|
5070 // XXX Dimensionless images shouldn't fall back to filled-area size -- the |
|
5071 // caller should provide the image size, a la DrawBackgroundImage. |
|
5072 if (gotWidth != gotHeight) { |
|
5073 if (!gotWidth) { |
|
5074 if (imageRatio.height != 0) { |
|
5075 imageSize.width = |
|
5076 NSCoordSaturatingNonnegativeMultiply(imageSize.height, |
|
5077 float(imageRatio.width) / |
|
5078 float(imageRatio.height)); |
|
5079 gotWidth = true; |
|
5080 } |
|
5081 } else { |
|
5082 if (imageRatio.width != 0) { |
|
5083 imageSize.height = |
|
5084 NSCoordSaturatingNonnegativeMultiply(imageSize.width, |
|
5085 float(imageRatio.height) / |
|
5086 float(imageRatio.width)); |
|
5087 gotHeight = true; |
|
5088 } |
|
5089 } |
|
5090 } |
|
5091 |
|
5092 if (!gotWidth) { |
|
5093 imageSize.width = nsPresContext::AppUnitsToIntCSSPixels(aFill.width); |
|
5094 } |
|
5095 if (!gotHeight) { |
|
5096 imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aFill.height); |
|
5097 } |
|
5098 |
|
5099 return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, |
|
5100 aDest, aFill, aAnchor, aDirty, |
|
5101 imageSize, nullptr, aImageFlags); |
|
5102 } |
|
5103 |
|
5104 /* static */ nsRect |
|
5105 nsLayoutUtils::GetWholeImageDestination(const nsIntSize& aWholeImageSize, |
|
5106 const nsRect& aImageSourceArea, |
|
5107 const nsRect& aDestArea) |
|
5108 { |
|
5109 double scaleX = double(aDestArea.width)/aImageSourceArea.width; |
|
5110 double scaleY = double(aDestArea.height)/aImageSourceArea.height; |
|
5111 nscoord destOffsetX = NSToCoordRound(aImageSourceArea.x*scaleX); |
|
5112 nscoord destOffsetY = NSToCoordRound(aImageSourceArea.y*scaleY); |
|
5113 nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel(); |
|
5114 nscoord wholeSizeX = NSToCoordRound(aWholeImageSize.width*appUnitsPerCSSPixel*scaleX); |
|
5115 nscoord wholeSizeY = NSToCoordRound(aWholeImageSize.height*appUnitsPerCSSPixel*scaleY); |
|
5116 return nsRect(aDestArea.TopLeft() - nsPoint(destOffsetX, destOffsetY), |
|
5117 nsSize(wholeSizeX, wholeSizeY)); |
|
5118 } |
|
5119 |
|
5120 /* static */ already_AddRefed<imgIContainer> |
|
5121 nsLayoutUtils::OrientImage(imgIContainer* aContainer, |
|
5122 const nsStyleImageOrientation& aOrientation) |
|
5123 { |
|
5124 MOZ_ASSERT(aContainer, "Should have an image container"); |
|
5125 nsCOMPtr<imgIContainer> img(aContainer); |
|
5126 |
|
5127 if (aOrientation.IsFromImage()) { |
|
5128 img = ImageOps::Orient(img, img->GetOrientation()); |
|
5129 } else if (!aOrientation.IsDefault()) { |
|
5130 Angle angle = aOrientation.Angle(); |
|
5131 Flip flip = aOrientation.IsFlipped() ? Flip::Horizontal |
|
5132 : Flip::Unflipped; |
|
5133 img = ImageOps::Orient(img, Orientation(angle, flip)); |
|
5134 } |
|
5135 |
|
5136 return img.forget(); |
|
5137 } |
|
5138 |
|
5139 static bool NonZeroStyleCoord(const nsStyleCoord& aCoord) |
|
5140 { |
|
5141 if (aCoord.IsCoordPercentCalcUnit()) { |
|
5142 // Since negative results are clamped to 0, check > 0. |
|
5143 return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 || |
|
5144 nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0; |
|
5145 } |
|
5146 |
|
5147 return true; |
|
5148 } |
|
5149 |
|
5150 /* static */ bool |
|
5151 nsLayoutUtils::HasNonZeroCorner(const nsStyleCorners& aCorners) |
|
5152 { |
|
5153 NS_FOR_CSS_HALF_CORNERS(corner) { |
|
5154 if (NonZeroStyleCoord(aCorners.Get(corner))) |
|
5155 return true; |
|
5156 } |
|
5157 return false; |
|
5158 } |
|
5159 |
|
5160 // aCorner is a "full corner" value, i.e. NS_CORNER_TOP_LEFT etc |
|
5161 static bool IsCornerAdjacentToSide(uint8_t aCorner, css::Side aSide) |
|
5162 { |
|
5163 PR_STATIC_ASSERT((int)NS_SIDE_TOP == NS_CORNER_TOP_LEFT); |
|
5164 PR_STATIC_ASSERT((int)NS_SIDE_RIGHT == NS_CORNER_TOP_RIGHT); |
|
5165 PR_STATIC_ASSERT((int)NS_SIDE_BOTTOM == NS_CORNER_BOTTOM_RIGHT); |
|
5166 PR_STATIC_ASSERT((int)NS_SIDE_LEFT == NS_CORNER_BOTTOM_LEFT); |
|
5167 PR_STATIC_ASSERT((int)NS_SIDE_TOP == ((NS_CORNER_TOP_RIGHT - 1)&3)); |
|
5168 PR_STATIC_ASSERT((int)NS_SIDE_RIGHT == ((NS_CORNER_BOTTOM_RIGHT - 1)&3)); |
|
5169 PR_STATIC_ASSERT((int)NS_SIDE_BOTTOM == ((NS_CORNER_BOTTOM_LEFT - 1)&3)); |
|
5170 PR_STATIC_ASSERT((int)NS_SIDE_LEFT == ((NS_CORNER_TOP_LEFT - 1)&3)); |
|
5171 |
|
5172 return aSide == aCorner || aSide == ((aCorner - 1)&3); |
|
5173 } |
|
5174 |
|
5175 /* static */ bool |
|
5176 nsLayoutUtils::HasNonZeroCornerOnSide(const nsStyleCorners& aCorners, |
|
5177 css::Side aSide) |
|
5178 { |
|
5179 PR_STATIC_ASSERT(NS_CORNER_TOP_LEFT_X/2 == NS_CORNER_TOP_LEFT); |
|
5180 PR_STATIC_ASSERT(NS_CORNER_TOP_LEFT_Y/2 == NS_CORNER_TOP_LEFT); |
|
5181 PR_STATIC_ASSERT(NS_CORNER_TOP_RIGHT_X/2 == NS_CORNER_TOP_RIGHT); |
|
5182 PR_STATIC_ASSERT(NS_CORNER_TOP_RIGHT_Y/2 == NS_CORNER_TOP_RIGHT); |
|
5183 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_RIGHT_X/2 == NS_CORNER_BOTTOM_RIGHT); |
|
5184 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_RIGHT_Y/2 == NS_CORNER_BOTTOM_RIGHT); |
|
5185 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_LEFT_X/2 == NS_CORNER_BOTTOM_LEFT); |
|
5186 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_LEFT_Y/2 == NS_CORNER_BOTTOM_LEFT); |
|
5187 |
|
5188 NS_FOR_CSS_HALF_CORNERS(corner) { |
|
5189 // corner is a "half corner" value, so dividing by two gives us a |
|
5190 // "full corner" value. |
|
5191 if (NonZeroStyleCoord(aCorners.Get(corner)) && |
|
5192 IsCornerAdjacentToSide(corner/2, aSide)) |
|
5193 return true; |
|
5194 } |
|
5195 return false; |
|
5196 } |
|
5197 |
|
5198 /* static */ nsTransparencyMode |
|
5199 nsLayoutUtils::GetFrameTransparency(nsIFrame* aBackgroundFrame, |
|
5200 nsIFrame* aCSSRootFrame) { |
|
5201 if (aCSSRootFrame->StyleDisplay()->mOpacity < 1.0f) |
|
5202 return eTransparencyTransparent; |
|
5203 |
|
5204 if (HasNonZeroCorner(aCSSRootFrame->StyleBorder()->mBorderRadius)) |
|
5205 return eTransparencyTransparent; |
|
5206 |
|
5207 if (aCSSRootFrame->StyleDisplay()->mAppearance == NS_THEME_WIN_GLASS) |
|
5208 return eTransparencyGlass; |
|
5209 |
|
5210 if (aCSSRootFrame->StyleDisplay()->mAppearance == NS_THEME_WIN_BORDERLESS_GLASS) |
|
5211 return eTransparencyBorderlessGlass; |
|
5212 |
|
5213 nsITheme::Transparency transparency; |
|
5214 if (aCSSRootFrame->IsThemed(&transparency)) |
|
5215 return transparency == nsITheme::eTransparent |
|
5216 ? eTransparencyTransparent |
|
5217 : eTransparencyOpaque; |
|
5218 |
|
5219 // We need an uninitialized window to be treated as opaque because |
|
5220 // doing otherwise breaks window display effects on some platforms, |
|
5221 // specifically Vista. (bug 450322) |
|
5222 if (aBackgroundFrame->GetType() == nsGkAtoms::viewportFrame && |
|
5223 !aBackgroundFrame->GetFirstPrincipalChild()) { |
|
5224 return eTransparencyOpaque; |
|
5225 } |
|
5226 |
|
5227 nsStyleContext* bgSC; |
|
5228 if (!nsCSSRendering::FindBackground(aBackgroundFrame, &bgSC)) { |
|
5229 return eTransparencyTransparent; |
|
5230 } |
|
5231 const nsStyleBackground* bg = bgSC->StyleBackground(); |
|
5232 if (NS_GET_A(bg->mBackgroundColor) < 255 || |
|
5233 // bottom layer's clip is used for the color |
|
5234 bg->BottomLayer().mClip != NS_STYLE_BG_CLIP_BORDER) |
|
5235 return eTransparencyTransparent; |
|
5236 return eTransparencyOpaque; |
|
5237 } |
|
5238 |
|
5239 static bool IsPopupFrame(nsIFrame* aFrame) |
|
5240 { |
|
5241 // aFrame is a popup it's the list control frame dropdown for a combobox. |
|
5242 nsIAtom* frameType = aFrame->GetType(); |
|
5243 if (frameType == nsGkAtoms::listControlFrame) { |
|
5244 nsListControlFrame* lcf = static_cast<nsListControlFrame*>(aFrame); |
|
5245 return lcf->IsInDropDownMode(); |
|
5246 } |
|
5247 |
|
5248 // ... or if it's a XUL menupopup frame. |
|
5249 return frameType == nsGkAtoms::menuPopupFrame; |
|
5250 } |
|
5251 |
|
5252 /* static */ bool |
|
5253 nsLayoutUtils::IsPopup(nsIFrame* aFrame) |
|
5254 { |
|
5255 // Optimization: the frame can't possibly be a popup if it has no view. |
|
5256 if (!aFrame->HasView()) { |
|
5257 NS_ASSERTION(!IsPopupFrame(aFrame), "popup frame must have a view"); |
|
5258 return false; |
|
5259 } |
|
5260 return IsPopupFrame(aFrame); |
|
5261 } |
|
5262 |
|
5263 /* static */ nsIFrame* |
|
5264 nsLayoutUtils::GetDisplayRootFrame(nsIFrame* aFrame) |
|
5265 { |
|
5266 // We could use GetRootPresContext() here if the |
|
5267 // NS_FRAME_IN_POPUP frame bit is set. |
|
5268 nsIFrame* f = aFrame; |
|
5269 for (;;) { |
|
5270 if (!f->HasAnyStateBits(NS_FRAME_IN_POPUP)) { |
|
5271 f = f->PresContext()->FrameManager()->GetRootFrame(); |
|
5272 } else if (IsPopup(f)) { |
|
5273 return f; |
|
5274 } |
|
5275 nsIFrame* parent = GetCrossDocParentFrame(f); |
|
5276 if (!parent) |
|
5277 return f; |
|
5278 f = parent; |
|
5279 } |
|
5280 } |
|
5281 |
|
5282 /* static */ nsIFrame* |
|
5283 nsLayoutUtils::GetReferenceFrame(nsIFrame* aFrame) |
|
5284 { |
|
5285 nsIFrame *f = aFrame; |
|
5286 for (;;) { |
|
5287 if (f->IsTransformed() || IsPopup(f)) { |
|
5288 return f; |
|
5289 } |
|
5290 nsIFrame* parent = GetCrossDocParentFrame(f); |
|
5291 if (!parent) { |
|
5292 return f; |
|
5293 } |
|
5294 f = parent; |
|
5295 } |
|
5296 } |
|
5297 |
|
5298 /* static */ nsIFrame* |
|
5299 nsLayoutUtils::GetTransformRootFrame(nsIFrame* aFrame) |
|
5300 { |
|
5301 nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame); |
|
5302 while (parent && parent->Preserves3DChildren()) { |
|
5303 parent = nsLayoutUtils::GetCrossDocParentFrame(parent); |
|
5304 } |
|
5305 return parent; |
|
5306 } |
|
5307 |
|
5308 /* static */ uint32_t |
|
5309 nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext, |
|
5310 const nsStyleFont* aStyleFont, |
|
5311 const nsStyleText* aStyleText, |
|
5312 nscoord aLetterSpacing) |
|
5313 { |
|
5314 uint32_t result = 0; |
|
5315 if (aLetterSpacing != 0) { |
|
5316 result |= gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES; |
|
5317 } |
|
5318 if (aStyleText->mControlCharacterVisibility == NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN) { |
|
5319 result |= gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS; |
|
5320 } |
|
5321 switch (aStyleContext->StyleSVG()->mTextRendering) { |
|
5322 case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED: |
|
5323 result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED; |
|
5324 break; |
|
5325 case NS_STYLE_TEXT_RENDERING_AUTO: |
|
5326 if (aStyleFont->mFont.size < |
|
5327 aStyleContext->PresContext()->GetAutoQualityMinFontSize()) { |
|
5328 result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED; |
|
5329 } |
|
5330 break; |
|
5331 default: |
|
5332 break; |
|
5333 } |
|
5334 return result; |
|
5335 } |
|
5336 |
|
5337 /* static */ void |
|
5338 nsLayoutUtils::GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2, |
|
5339 nsRect* aHStrip, nsRect* aVStrip) { |
|
5340 NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(), |
|
5341 "expected rects at the same position"); |
|
5342 nsRect unionRect(aR1.x, aR1.y, std::max(aR1.width, aR2.width), |
|
5343 std::max(aR1.height, aR2.height)); |
|
5344 nscoord VStripStart = std::min(aR1.width, aR2.width); |
|
5345 nscoord HStripStart = std::min(aR1.height, aR2.height); |
|
5346 *aVStrip = unionRect; |
|
5347 aVStrip->x += VStripStart; |
|
5348 aVStrip->width -= VStripStart; |
|
5349 *aHStrip = unionRect; |
|
5350 aHStrip->y += HStripStart; |
|
5351 aHStrip->height -= HStripStart; |
|
5352 } |
|
5353 |
|
5354 nsDeviceContext* |
|
5355 nsLayoutUtils::GetDeviceContextForScreenInfo(nsPIDOMWindow* aWindow) |
|
5356 { |
|
5357 if (!aWindow) { |
|
5358 return nullptr; |
|
5359 } |
|
5360 |
|
5361 nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell(); |
|
5362 while (docShell) { |
|
5363 // Now make sure our size is up to date. That will mean that the device |
|
5364 // context does the right thing on multi-monitor systems when we return it to |
|
5365 // the caller. It will also make sure that our prescontext has been created, |
|
5366 // if we're supposed to have one. |
|
5367 nsCOMPtr<nsPIDOMWindow> win = do_GetInterface(docShell); |
|
5368 if (!win) { |
|
5369 // No reason to go on |
|
5370 return nullptr; |
|
5371 } |
|
5372 |
|
5373 win->EnsureSizeUpToDate(); |
|
5374 |
|
5375 nsRefPtr<nsPresContext> presContext; |
|
5376 docShell->GetPresContext(getter_AddRefs(presContext)); |
|
5377 if (presContext) { |
|
5378 nsDeviceContext* context = presContext->DeviceContext(); |
|
5379 if (context) { |
|
5380 return context; |
|
5381 } |
|
5382 } |
|
5383 |
|
5384 nsCOMPtr<nsIDocShellTreeItem> parentItem; |
|
5385 docShell->GetParent(getter_AddRefs(parentItem)); |
|
5386 docShell = do_QueryInterface(parentItem); |
|
5387 } |
|
5388 |
|
5389 return nullptr; |
|
5390 } |
|
5391 |
|
5392 /* static */ bool |
|
5393 nsLayoutUtils::IsReallyFixedPos(nsIFrame* aFrame) |
|
5394 { |
|
5395 NS_PRECONDITION(aFrame->GetParent(), |
|
5396 "IsReallyFixedPos called on frame not in tree"); |
|
5397 NS_PRECONDITION(aFrame->StyleDisplay()->mPosition == |
|
5398 NS_STYLE_POSITION_FIXED, |
|
5399 "IsReallyFixedPos called on non-'position:fixed' frame"); |
|
5400 |
|
5401 nsIAtom *parentType = aFrame->GetParent()->GetType(); |
|
5402 return parentType == nsGkAtoms::viewportFrame || |
|
5403 parentType == nsGkAtoms::pageContentFrame; |
|
5404 } |
|
5405 |
|
5406 nsLayoutUtils::SurfaceFromElementResult |
|
5407 nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement, |
|
5408 uint32_t aSurfaceFlags, |
|
5409 DrawTarget* aTarget) |
|
5410 { |
|
5411 SurfaceFromElementResult result; |
|
5412 nsresult rv; |
|
5413 |
|
5414 nsCOMPtr<imgIRequest> imgRequest; |
|
5415 rv = aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, |
|
5416 getter_AddRefs(imgRequest)); |
|
5417 if (NS_FAILED(rv) || !imgRequest) |
|
5418 return result; |
|
5419 |
|
5420 uint32_t status; |
|
5421 imgRequest->GetImageStatus(&status); |
|
5422 if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0) { |
|
5423 // Spec says to use GetComplete, but that only works on |
|
5424 // nsIDOMHTMLImageElement, and we support all sorts of other stuff |
|
5425 // here. Do this for now pending spec clarification. |
|
5426 result.mIsStillLoading = (status & imgIRequest::STATUS_ERROR) == 0; |
|
5427 return result; |
|
5428 } |
|
5429 |
|
5430 nsCOMPtr<nsIPrincipal> principal; |
|
5431 rv = imgRequest->GetImagePrincipal(getter_AddRefs(principal)); |
|
5432 if (NS_FAILED(rv)) |
|
5433 return result; |
|
5434 |
|
5435 nsCOMPtr<imgIContainer> imgContainer; |
|
5436 rv = imgRequest->GetImage(getter_AddRefs(imgContainer)); |
|
5437 if (NS_FAILED(rv)) |
|
5438 return result; |
|
5439 |
|
5440 uint32_t noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS; |
|
5441 |
|
5442 uint32_t whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME) |
|
5443 ? (uint32_t) imgIContainer::FRAME_FIRST |
|
5444 : (uint32_t) imgIContainer::FRAME_CURRENT; |
|
5445 uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE; |
|
5446 if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION) |
|
5447 frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION; |
|
5448 if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) { |
|
5449 frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; |
|
5450 result.mIsPremultiplied = false; |
|
5451 } |
|
5452 |
|
5453 int32_t imgWidth, imgHeight; |
|
5454 rv = imgContainer->GetWidth(&imgWidth); |
|
5455 nsresult rv2 = imgContainer->GetHeight(&imgHeight); |
|
5456 if (NS_FAILED(rv) || NS_FAILED(rv2)) |
|
5457 return result; |
|
5458 |
|
5459 if (!noRasterize || imgContainer->GetType() == imgIContainer::TYPE_RASTER) { |
|
5460 if (aSurfaceFlags & SFE_WANT_IMAGE_SURFACE) { |
|
5461 frameFlags |= imgIContainer::FLAG_WANT_DATA_SURFACE; |
|
5462 } |
|
5463 result.mSourceSurface = imgContainer->GetFrame(whichFrame, frameFlags); |
|
5464 if (!result.mSourceSurface) { |
|
5465 return result; |
|
5466 } |
|
5467 // The surface we return is likely to be cached. We don't want to have to |
|
5468 // convert to a surface that's compatible with aTarget each time it's used |
|
5469 // (that would result in terrible performance), so we convert once here |
|
5470 // upfront if aTarget is specified. |
|
5471 if (aTarget) { |
|
5472 RefPtr<SourceSurface> optSurface = |
|
5473 aTarget->OptimizeSourceSurface(result.mSourceSurface); |
|
5474 if (optSurface) { |
|
5475 result.mSourceSurface = optSurface; |
|
5476 } |
|
5477 } |
|
5478 } else { |
|
5479 result.mDrawInfo.mImgContainer = imgContainer; |
|
5480 result.mDrawInfo.mWhichFrame = whichFrame; |
|
5481 result.mDrawInfo.mDrawingFlags = frameFlags; |
|
5482 } |
|
5483 |
|
5484 int32_t corsmode; |
|
5485 if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) { |
|
5486 result.mCORSUsed = (corsmode != imgIRequest::CORS_NONE); |
|
5487 } |
|
5488 |
|
5489 result.mSize = gfxIntSize(imgWidth, imgHeight); |
|
5490 result.mPrincipal = principal.forget(); |
|
5491 // no images, including SVG images, can load content from another domain. |
|
5492 result.mIsWriteOnly = false; |
|
5493 result.mImageRequest = imgRequest.forget(); |
|
5494 |
|
5495 return result; |
|
5496 } |
|
5497 |
|
5498 nsLayoutUtils::SurfaceFromElementResult |
|
5499 nsLayoutUtils::SurfaceFromElement(HTMLImageElement *aElement, |
|
5500 uint32_t aSurfaceFlags, |
|
5501 DrawTarget* aTarget) |
|
5502 { |
|
5503 return SurfaceFromElement(static_cast<nsIImageLoadingContent*>(aElement), |
|
5504 aSurfaceFlags, aTarget); |
|
5505 } |
|
5506 |
|
5507 nsLayoutUtils::SurfaceFromElementResult |
|
5508 nsLayoutUtils::SurfaceFromElement(HTMLCanvasElement* aElement, |
|
5509 uint32_t aSurfaceFlags, |
|
5510 DrawTarget* aTarget) |
|
5511 { |
|
5512 SurfaceFromElementResult result; |
|
5513 |
|
5514 bool* isPremultiplied = nullptr; |
|
5515 if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) { |
|
5516 isPremultiplied = &result.mIsPremultiplied; |
|
5517 } |
|
5518 |
|
5519 gfxIntSize size = aElement->GetSize(); |
|
5520 |
|
5521 result.mSourceSurface = aElement->GetSurfaceSnapshot(isPremultiplied); |
|
5522 if (!result.mSourceSurface) { |
|
5523 // If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just |
|
5524 // draw nothing, so return an empty surface. |
|
5525 DrawTarget *ref = aTarget ? aTarget : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); |
|
5526 RefPtr<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height), |
|
5527 SurfaceFormat::B8G8R8A8); |
|
5528 if (dt) { |
|
5529 result.mSourceSurface = dt->Snapshot(); |
|
5530 } |
|
5531 } else if (aTarget) { |
|
5532 RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface); |
|
5533 if (opt) { |
|
5534 result.mSourceSurface = opt; |
|
5535 } |
|
5536 } |
|
5537 |
|
5538 // Ensure that any future changes to the canvas trigger proper invalidation, |
|
5539 // in case this is being used by -moz-element() |
|
5540 aElement->MarkContextClean(); |
|
5541 |
|
5542 result.mSize = size; |
|
5543 result.mPrincipal = aElement->NodePrincipal(); |
|
5544 result.mIsWriteOnly = aElement->IsWriteOnly(); |
|
5545 |
|
5546 return result; |
|
5547 } |
|
5548 |
|
5549 nsLayoutUtils::SurfaceFromElementResult |
|
5550 nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement, |
|
5551 uint32_t aSurfaceFlags, |
|
5552 DrawTarget* aTarget) |
|
5553 { |
|
5554 SurfaceFromElementResult result; |
|
5555 |
|
5556 NS_WARN_IF_FALSE((aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!"); |
|
5557 |
|
5558 uint16_t readyState; |
|
5559 if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) && |
|
5560 (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING || |
|
5561 readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) { |
|
5562 result.mIsStillLoading = true; |
|
5563 return result; |
|
5564 } |
|
5565 |
|
5566 // If it doesn't have a principal, just bail |
|
5567 nsCOMPtr<nsIPrincipal> principal = aElement->GetCurrentPrincipal(); |
|
5568 if (!principal) |
|
5569 return result; |
|
5570 |
|
5571 ImageContainer *container = aElement->GetImageContainer(); |
|
5572 if (!container) |
|
5573 return result; |
|
5574 |
|
5575 mozilla::gfx::IntSize size; |
|
5576 result.mSourceSurface = container->GetCurrentAsSourceSurface(&size); |
|
5577 if (!result.mSourceSurface) |
|
5578 return result; |
|
5579 |
|
5580 if (aTarget) { |
|
5581 RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface); |
|
5582 if (opt) { |
|
5583 result.mSourceSurface = opt; |
|
5584 } |
|
5585 } |
|
5586 |
|
5587 result.mCORSUsed = aElement->GetCORSMode() != CORS_NONE; |
|
5588 result.mSize = ThebesIntSize(size); |
|
5589 result.mPrincipal = principal.forget(); |
|
5590 result.mIsWriteOnly = false; |
|
5591 |
|
5592 return result; |
|
5593 } |
|
5594 |
|
5595 nsLayoutUtils::SurfaceFromElementResult |
|
5596 nsLayoutUtils::SurfaceFromElement(dom::Element* aElement, |
|
5597 uint32_t aSurfaceFlags, |
|
5598 DrawTarget* aTarget) |
|
5599 { |
|
5600 // If it's a <canvas>, we may be able to just grab its internal surface |
|
5601 if (HTMLCanvasElement* canvas = |
|
5602 HTMLCanvasElement::FromContentOrNull(aElement)) { |
|
5603 return SurfaceFromElement(canvas, aSurfaceFlags, aTarget); |
|
5604 } |
|
5605 |
|
5606 // Maybe it's <video>? |
|
5607 if (HTMLVideoElement* video = |
|
5608 HTMLVideoElement::FromContentOrNull(aElement)) { |
|
5609 return SurfaceFromElement(video, aSurfaceFlags, aTarget); |
|
5610 } |
|
5611 |
|
5612 // Finally, check if it's a normal image |
|
5613 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement); |
|
5614 |
|
5615 if (!imageLoader) { |
|
5616 return SurfaceFromElementResult(); |
|
5617 } |
|
5618 |
|
5619 return SurfaceFromElement(imageLoader, aSurfaceFlags, aTarget); |
|
5620 } |
|
5621 |
|
5622 /* static */ |
|
5623 nsIContent* |
|
5624 nsLayoutUtils::GetEditableRootContentByContentEditable(nsIDocument* aDocument) |
|
5625 { |
|
5626 // If the document is in designMode we should return nullptr. |
|
5627 if (!aDocument || aDocument->HasFlag(NODE_IS_EDITABLE)) { |
|
5628 return nullptr; |
|
5629 } |
|
5630 |
|
5631 // contenteditable only works with HTML document. |
|
5632 // Note: Use nsIDOMHTMLDocument rather than nsIHTMLDocument for getting the |
|
5633 // body node because nsIDOMHTMLDocument::GetBody() does something |
|
5634 // additional work for some cases and nsEditor uses them. |
|
5635 nsCOMPtr<nsIDOMHTMLDocument> domHTMLDoc = do_QueryInterface(aDocument); |
|
5636 if (!domHTMLDoc) { |
|
5637 return nullptr; |
|
5638 } |
|
5639 |
|
5640 Element* rootElement = aDocument->GetRootElement(); |
|
5641 if (rootElement && rootElement->IsEditable()) { |
|
5642 return rootElement; |
|
5643 } |
|
5644 |
|
5645 // If there are no editable root element, check its <body> element. |
|
5646 // Note that the body element could be <frameset> element. |
|
5647 nsCOMPtr<nsIDOMHTMLElement> body; |
|
5648 nsresult rv = domHTMLDoc->GetBody(getter_AddRefs(body)); |
|
5649 nsCOMPtr<nsIContent> content = do_QueryInterface(body); |
|
5650 if (NS_SUCCEEDED(rv) && content && content->IsEditable()) { |
|
5651 return content; |
|
5652 } |
|
5653 return nullptr; |
|
5654 } |
|
5655 |
|
5656 #ifdef DEBUG |
|
5657 /* static */ void |
|
5658 nsLayoutUtils::AssertNoDuplicateContinuations(nsIFrame* aContainer, |
|
5659 const nsFrameList& aFrameList) |
|
5660 { |
|
5661 for (nsIFrame* f = aFrameList.FirstChild(); f ; f = f->GetNextSibling()) { |
|
5662 // Check only later continuations of f; we deal with checking the |
|
5663 // earlier continuations when we hit those earlier continuations in |
|
5664 // the frame list. |
|
5665 for (nsIFrame *c = f; (c = c->GetNextInFlow());) { |
|
5666 NS_ASSERTION(c->GetParent() != aContainer || |
|
5667 !aFrameList.ContainsFrame(c), |
|
5668 "Two continuations of the same frame in the same " |
|
5669 "frame list"); |
|
5670 } |
|
5671 } |
|
5672 } |
|
5673 |
|
5674 // Is one of aFrame's ancestors a letter frame? |
|
5675 static bool |
|
5676 IsInLetterFrame(nsIFrame *aFrame) |
|
5677 { |
|
5678 for (nsIFrame *f = aFrame->GetParent(); f; f = f->GetParent()) { |
|
5679 if (f->GetType() == nsGkAtoms::letterFrame) { |
|
5680 return true; |
|
5681 } |
|
5682 } |
|
5683 return false; |
|
5684 } |
|
5685 |
|
5686 /* static */ void |
|
5687 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame *aSubtreeRoot) |
|
5688 { |
|
5689 NS_ASSERTION(aSubtreeRoot->GetPrevInFlow(), |
|
5690 "frame tree not empty, but caller reported complete status"); |
|
5691 |
|
5692 // Also assert that text frames map no text. |
|
5693 int32_t start, end; |
|
5694 nsresult rv = aSubtreeRoot->GetOffsets(start, end); |
|
5695 NS_ASSERTION(NS_SUCCEEDED(rv), "GetOffsets failed"); |
|
5696 // In some cases involving :first-letter, we'll partially unlink a |
|
5697 // continuation in the middle of a continuation chain from its |
|
5698 // previous and next continuations before destroying it, presumably so |
|
5699 // that we don't also destroy the later continuations. Once we've |
|
5700 // done this, GetOffsets returns incorrect values. |
|
5701 // For examples, see list of tests in |
|
5702 // https://bugzilla.mozilla.org/show_bug.cgi?id=619021#c29 |
|
5703 NS_ASSERTION(start == end || IsInLetterFrame(aSubtreeRoot), |
|
5704 "frame tree not empty, but caller reported complete status"); |
|
5705 |
|
5706 nsIFrame::ChildListIterator lists(aSubtreeRoot); |
|
5707 for (; !lists.IsDone(); lists.Next()) { |
|
5708 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
5709 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
5710 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(childFrames.get()); |
|
5711 } |
|
5712 } |
|
5713 } |
|
5714 #endif |
|
5715 |
|
5716 static void |
|
5717 GetFontFacesForFramesInner(nsIFrame* aFrame, nsFontFaceList* aFontFaceList) |
|
5718 { |
|
5719 NS_PRECONDITION(aFrame, "NULL frame pointer"); |
|
5720 |
|
5721 if (aFrame->GetType() == nsGkAtoms::textFrame) { |
|
5722 if (!aFrame->GetPrevContinuation()) { |
|
5723 nsLayoutUtils::GetFontFacesForText(aFrame, 0, INT32_MAX, true, |
|
5724 aFontFaceList); |
|
5725 } |
|
5726 return; |
|
5727 } |
|
5728 |
|
5729 nsIFrame::ChildListID childLists[] = { nsIFrame::kPrincipalList, |
|
5730 nsIFrame::kPopupList }; |
|
5731 for (size_t i = 0; i < ArrayLength(childLists); ++i) { |
|
5732 nsFrameList children(aFrame->GetChildList(childLists[i])); |
|
5733 for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { |
|
5734 nsIFrame* child = e.get(); |
|
5735 child = nsPlaceholderFrame::GetRealFrameFor(child); |
|
5736 GetFontFacesForFramesInner(child, aFontFaceList); |
|
5737 } |
|
5738 } |
|
5739 } |
|
5740 |
|
5741 /* static */ |
|
5742 nsresult |
|
5743 nsLayoutUtils::GetFontFacesForFrames(nsIFrame* aFrame, |
|
5744 nsFontFaceList* aFontFaceList) |
|
5745 { |
|
5746 NS_PRECONDITION(aFrame, "NULL frame pointer"); |
|
5747 |
|
5748 while (aFrame) { |
|
5749 GetFontFacesForFramesInner(aFrame, aFontFaceList); |
|
5750 aFrame = GetNextContinuationOrIBSplitSibling(aFrame); |
|
5751 } |
|
5752 |
|
5753 return NS_OK; |
|
5754 } |
|
5755 |
|
5756 /* static */ |
|
5757 nsresult |
|
5758 nsLayoutUtils::GetFontFacesForText(nsIFrame* aFrame, |
|
5759 int32_t aStartOffset, int32_t aEndOffset, |
|
5760 bool aFollowContinuations, |
|
5761 nsFontFaceList* aFontFaceList) |
|
5762 { |
|
5763 NS_PRECONDITION(aFrame, "NULL frame pointer"); |
|
5764 |
|
5765 if (aFrame->GetType() != nsGkAtoms::textFrame) { |
|
5766 return NS_OK; |
|
5767 } |
|
5768 |
|
5769 nsTextFrame* curr = static_cast<nsTextFrame*>(aFrame); |
|
5770 do { |
|
5771 int32_t fstart = std::max(curr->GetContentOffset(), aStartOffset); |
|
5772 int32_t fend = std::min(curr->GetContentEnd(), aEndOffset); |
|
5773 if (fstart >= fend) { |
|
5774 curr = static_cast<nsTextFrame*>(curr->GetNextContinuation()); |
|
5775 continue; |
|
5776 } |
|
5777 |
|
5778 // curr is overlapping with the offset we want |
|
5779 gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated); |
|
5780 gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated); |
|
5781 NS_ENSURE_TRUE(textRun, NS_ERROR_OUT_OF_MEMORY); |
|
5782 |
|
5783 // include continuations in the range that share the same textrun |
|
5784 nsTextFrame* next = nullptr; |
|
5785 if (aFollowContinuations && fend < aEndOffset) { |
|
5786 next = static_cast<nsTextFrame*>(curr->GetNextContinuation()); |
|
5787 while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) { |
|
5788 fend = std::min(next->GetContentEnd(), aEndOffset); |
|
5789 next = fend < aEndOffset ? |
|
5790 static_cast<nsTextFrame*>(next->GetNextContinuation()) : nullptr; |
|
5791 } |
|
5792 } |
|
5793 |
|
5794 uint32_t skipStart = iter.ConvertOriginalToSkipped(fstart); |
|
5795 uint32_t skipEnd = iter.ConvertOriginalToSkipped(fend); |
|
5796 aFontFaceList->AddFontsFromTextRun(textRun, skipStart, skipEnd - skipStart); |
|
5797 curr = next; |
|
5798 } while (aFollowContinuations && curr); |
|
5799 |
|
5800 return NS_OK; |
|
5801 } |
|
5802 |
|
5803 /* static */ |
|
5804 size_t |
|
5805 nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame* aFrame, |
|
5806 MallocSizeOf aMallocSizeOf, |
|
5807 bool clear) |
|
5808 { |
|
5809 NS_PRECONDITION(aFrame, "NULL frame pointer"); |
|
5810 |
|
5811 size_t total = 0; |
|
5812 |
|
5813 if (aFrame->GetType() == nsGkAtoms::textFrame) { |
|
5814 nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame); |
|
5815 for (uint32_t i = 0; i < 2; ++i) { |
|
5816 gfxTextRun *run = textFrame->GetTextRun( |
|
5817 (i != 0) ? nsTextFrame::eInflated : nsTextFrame::eNotInflated); |
|
5818 if (run) { |
|
5819 if (clear) { |
|
5820 run->ResetSizeOfAccountingFlags(); |
|
5821 } else { |
|
5822 total += run->MaybeSizeOfIncludingThis(aMallocSizeOf); |
|
5823 } |
|
5824 } |
|
5825 } |
|
5826 return total; |
|
5827 } |
|
5828 |
|
5829 nsAutoTArray<nsIFrame::ChildList,4> childListArray; |
|
5830 aFrame->GetChildLists(&childListArray); |
|
5831 |
|
5832 for (nsIFrame::ChildListArrayIterator childLists(childListArray); |
|
5833 !childLists.IsDone(); childLists.Next()) { |
|
5834 for (nsFrameList::Enumerator e(childLists.CurrentList()); |
|
5835 !e.AtEnd(); e.Next()) { |
|
5836 total += SizeOfTextRunsForFrames(e.get(), aMallocSizeOf, clear); |
|
5837 } |
|
5838 } |
|
5839 return total; |
|
5840 } |
|
5841 |
|
5842 /* static */ |
|
5843 void |
|
5844 nsLayoutUtils::Initialize() |
|
5845 { |
|
5846 Preferences::AddUintVarCache(&sFontSizeInflationMaxRatio, |
|
5847 "font.size.inflation.maxRatio"); |
|
5848 Preferences::AddUintVarCache(&sFontSizeInflationEmPerLine, |
|
5849 "font.size.inflation.emPerLine"); |
|
5850 Preferences::AddUintVarCache(&sFontSizeInflationMinTwips, |
|
5851 "font.size.inflation.minTwips"); |
|
5852 Preferences::AddUintVarCache(&sFontSizeInflationLineThreshold, |
|
5853 "font.size.inflation.lineThreshold"); |
|
5854 Preferences::AddIntVarCache(&sFontSizeInflationMappingIntercept, |
|
5855 "font.size.inflation.mappingIntercept"); |
|
5856 Preferences::AddBoolVarCache(&sFontSizeInflationForceEnabled, |
|
5857 "font.size.inflation.forceEnabled"); |
|
5858 Preferences::AddBoolVarCache(&sFontSizeInflationDisabledInMasterProcess, |
|
5859 "font.size.inflation.disabledInMasterProcess"); |
|
5860 Preferences::AddBoolVarCache(&sInvalidationDebuggingIsEnabled, |
|
5861 "nglayout.debug.invalidation"); |
|
5862 Preferences::AddBoolVarCache(&sCSSVariablesEnabled, |
|
5863 "layout.css.variables.enabled"); |
|
5864 Preferences::AddBoolVarCache(&sInterruptibleReflowEnabled, |
|
5865 "layout.interruptible-reflow.enabled"); |
|
5866 |
|
5867 Preferences::RegisterCallback(GridEnabledPrefChangeCallback, |
|
5868 GRID_ENABLED_PREF_NAME); |
|
5869 GridEnabledPrefChangeCallback(GRID_ENABLED_PREF_NAME, nullptr); |
|
5870 Preferences::RegisterCallback(StickyEnabledPrefChangeCallback, |
|
5871 STICKY_ENABLED_PREF_NAME); |
|
5872 StickyEnabledPrefChangeCallback(STICKY_ENABLED_PREF_NAME, nullptr); |
|
5873 Preferences::RegisterCallback(TextAlignTrueEnabledPrefChangeCallback, |
|
5874 TEXT_ALIGN_TRUE_ENABLED_PREF_NAME); |
|
5875 TextAlignTrueEnabledPrefChangeCallback(TEXT_ALIGN_TRUE_ENABLED_PREF_NAME, |
|
5876 nullptr); |
|
5877 |
|
5878 nsComputedDOMStyle::RegisterPrefChangeCallbacks(); |
|
5879 } |
|
5880 |
|
5881 /* static */ |
|
5882 void |
|
5883 nsLayoutUtils::Shutdown() |
|
5884 { |
|
5885 if (sContentMap) { |
|
5886 delete sContentMap; |
|
5887 sContentMap = nullptr; |
|
5888 } |
|
5889 |
|
5890 Preferences::UnregisterCallback(GridEnabledPrefChangeCallback, |
|
5891 GRID_ENABLED_PREF_NAME); |
|
5892 Preferences::UnregisterCallback(StickyEnabledPrefChangeCallback, |
|
5893 STICKY_ENABLED_PREF_NAME); |
|
5894 |
|
5895 nsComputedDOMStyle::UnregisterPrefChangeCallbacks(); |
|
5896 } |
|
5897 |
|
5898 /* static */ |
|
5899 void |
|
5900 nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext, |
|
5901 imgIRequest* aRequest, |
|
5902 bool* aRequestRegistered) |
|
5903 { |
|
5904 if (!aPresContext) { |
|
5905 return; |
|
5906 } |
|
5907 |
|
5908 if (aRequestRegistered && *aRequestRegistered) { |
|
5909 // Our request is already registered with the refresh driver, so |
|
5910 // no need to register it again. |
|
5911 return; |
|
5912 } |
|
5913 |
|
5914 if (aRequest) { |
|
5915 if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) { |
|
5916 NS_WARNING("Unable to add image request"); |
|
5917 return; |
|
5918 } |
|
5919 |
|
5920 if (aRequestRegistered) { |
|
5921 *aRequestRegistered = true; |
|
5922 } |
|
5923 } |
|
5924 } |
|
5925 |
|
5926 /* static */ |
|
5927 void |
|
5928 nsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext* aPresContext, |
|
5929 imgIRequest* aRequest, |
|
5930 bool* aRequestRegistered) |
|
5931 { |
|
5932 if (!aPresContext) { |
|
5933 return; |
|
5934 } |
|
5935 |
|
5936 if (aRequestRegistered && *aRequestRegistered) { |
|
5937 // Our request is already registered with the refresh driver, so |
|
5938 // no need to register it again. |
|
5939 return; |
|
5940 } |
|
5941 |
|
5942 if (aRequest) { |
|
5943 nsCOMPtr<imgIContainer> image; |
|
5944 if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) { |
|
5945 // Check to verify that the image is animated. If so, then add it to the |
|
5946 // list of images tracked by the refresh driver. |
|
5947 bool isAnimated = false; |
|
5948 nsresult rv = image->GetAnimated(&isAnimated); |
|
5949 if (NS_SUCCEEDED(rv) && isAnimated) { |
|
5950 if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) { |
|
5951 NS_WARNING("Unable to add image request"); |
|
5952 return; |
|
5953 } |
|
5954 |
|
5955 if (aRequestRegistered) { |
|
5956 *aRequestRegistered = true; |
|
5957 } |
|
5958 } |
|
5959 } |
|
5960 } |
|
5961 } |
|
5962 |
|
5963 /* static */ |
|
5964 void |
|
5965 nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext, |
|
5966 imgIRequest* aRequest, |
|
5967 bool* aRequestRegistered) |
|
5968 { |
|
5969 if (!aPresContext) { |
|
5970 return; |
|
5971 } |
|
5972 |
|
5973 // Deregister our imgIRequest with the refresh driver to |
|
5974 // complete tear-down, but only if it has been registered |
|
5975 if (aRequestRegistered && !*aRequestRegistered) { |
|
5976 return; |
|
5977 } |
|
5978 |
|
5979 if (aRequest) { |
|
5980 nsCOMPtr<imgIContainer> image; |
|
5981 if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) { |
|
5982 aPresContext->RefreshDriver()->RemoveImageRequest(aRequest); |
|
5983 |
|
5984 if (aRequestRegistered) { |
|
5985 *aRequestRegistered = false; |
|
5986 } |
|
5987 } |
|
5988 } |
|
5989 } |
|
5990 |
|
5991 /* static */ |
|
5992 void |
|
5993 nsLayoutUtils::PostRestyleEvent(Element* aElement, |
|
5994 nsRestyleHint aRestyleHint, |
|
5995 nsChangeHint aMinChangeHint) |
|
5996 { |
|
5997 nsIDocument* doc = aElement->GetCurrentDoc(); |
|
5998 if (doc) { |
|
5999 nsCOMPtr<nsIPresShell> presShell = doc->GetShell(); |
|
6000 if (presShell) { |
|
6001 presShell->GetPresContext()->RestyleManager()->PostRestyleEvent( |
|
6002 aElement, aRestyleHint, aMinChangeHint); |
|
6003 } |
|
6004 } |
|
6005 } |
|
6006 |
|
6007 nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName, |
|
6008 const nsAString& aValue) |
|
6009 : mContent(aContent), |
|
6010 mAttrName(aAttrName), |
|
6011 mValue(aValue) |
|
6012 { |
|
6013 NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash"); |
|
6014 } |
|
6015 |
|
6016 nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName, |
|
6017 int32_t aValue) |
|
6018 : mContent(aContent), |
|
6019 mAttrName(aAttrName) |
|
6020 { |
|
6021 NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash"); |
|
6022 mValue.AppendInt(aValue); |
|
6023 } |
|
6024 |
|
6025 NS_IMETHODIMP |
|
6026 nsSetAttrRunnable::Run() |
|
6027 { |
|
6028 return mContent->SetAttr(kNameSpaceID_None, mAttrName, mValue, true); |
|
6029 } |
|
6030 |
|
6031 nsUnsetAttrRunnable::nsUnsetAttrRunnable(nsIContent* aContent, |
|
6032 nsIAtom* aAttrName) |
|
6033 : mContent(aContent), |
|
6034 mAttrName(aAttrName) |
|
6035 { |
|
6036 NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash"); |
|
6037 } |
|
6038 |
|
6039 NS_IMETHODIMP |
|
6040 nsUnsetAttrRunnable::Run() |
|
6041 { |
|
6042 return mContent->UnsetAttr(kNameSpaceID_None, mAttrName, true); |
|
6043 } |
|
6044 |
|
6045 /** |
|
6046 * Compute the minimum font size inside of a container with the given |
|
6047 * width, such that **when the user zooms the container to fill the full |
|
6048 * width of the device**, the fonts satisfy our minima. |
|
6049 */ |
|
6050 static nscoord |
|
6051 MinimumFontSizeFor(nsPresContext* aPresContext, nscoord aContainerWidth) |
|
6052 { |
|
6053 nsIPresShell* presShell = aPresContext->PresShell(); |
|
6054 |
|
6055 uint32_t emPerLine = presShell->FontSizeInflationEmPerLine(); |
|
6056 uint32_t minTwips = presShell->FontSizeInflationMinTwips(); |
|
6057 if (emPerLine == 0 && minTwips == 0) { |
|
6058 return 0; |
|
6059 } |
|
6060 |
|
6061 // Clamp the container width to the device dimensions |
|
6062 nscoord iFrameWidth = aPresContext->GetVisibleArea().width; |
|
6063 nscoord effectiveContainerWidth = std::min(iFrameWidth, aContainerWidth); |
|
6064 |
|
6065 nscoord byLine = 0, byInch = 0; |
|
6066 if (emPerLine != 0) { |
|
6067 byLine = effectiveContainerWidth / emPerLine; |
|
6068 } |
|
6069 if (minTwips != 0) { |
|
6070 // REVIEW: Is this giving us app units and sizes *not* counting |
|
6071 // viewport scaling? |
|
6072 float deviceWidthInches = |
|
6073 aPresContext->ScreenWidthInchesForFontInflation(); |
|
6074 byInch = NSToCoordRound(effectiveContainerWidth / |
|
6075 (deviceWidthInches * 1440 / |
|
6076 minTwips )); |
|
6077 } |
|
6078 return std::max(byLine, byInch); |
|
6079 } |
|
6080 |
|
6081 /* static */ float |
|
6082 nsLayoutUtils::FontSizeInflationInner(const nsIFrame *aFrame, |
|
6083 nscoord aMinFontSize) |
|
6084 { |
|
6085 // Note that line heights should be inflated by the same ratio as the |
|
6086 // font size of the same text; thus we operate only on the font size |
|
6087 // even when we're scaling a line height. |
|
6088 nscoord styleFontSize = aFrame->StyleFont()->mFont.size; |
|
6089 if (styleFontSize <= 0) { |
|
6090 // Never scale zero font size. |
|
6091 return 1.0; |
|
6092 } |
|
6093 |
|
6094 if (aMinFontSize <= 0) { |
|
6095 // No need to scale. |
|
6096 return 1.0; |
|
6097 } |
|
6098 |
|
6099 // If between this current frame and its font inflation container there is a |
|
6100 // non-inline element with fixed width or height, then we should not inflate |
|
6101 // fonts for this frame. |
|
6102 for (const nsIFrame* f = aFrame; |
|
6103 f && !f->IsContainerForFontSizeInflation(); |
|
6104 f = f->GetParent()) { |
|
6105 nsIContent* content = f->GetContent(); |
|
6106 nsIAtom* fType = f->GetType(); |
|
6107 // Also, if there is more than one frame corresponding to a single |
|
6108 // content node, we want the outermost one. |
|
6109 if (!(f->GetParent() && f->GetParent()->GetContent() == content) && |
|
6110 // ignore width/height on inlines since they don't apply |
|
6111 fType != nsGkAtoms::inlineFrame && |
|
6112 // ignore width on radios and checkboxes since we enlarge them and |
|
6113 // they have width/height in ua.css |
|
6114 fType != nsGkAtoms::formControlFrame) { |
|
6115 nsStyleCoord stylePosWidth = f->StylePosition()->mWidth; |
|
6116 nsStyleCoord stylePosHeight = f->StylePosition()->mHeight; |
|
6117 if (stylePosWidth.GetUnit() != eStyleUnit_Auto || |
|
6118 stylePosHeight.GetUnit() != eStyleUnit_Auto) { |
|
6119 |
|
6120 return 1.0; |
|
6121 } |
|
6122 } |
|
6123 } |
|
6124 |
|
6125 int32_t interceptParam = nsLayoutUtils::FontSizeInflationMappingIntercept(); |
|
6126 float maxRatio = (float)nsLayoutUtils::FontSizeInflationMaxRatio() / 100.0f; |
|
6127 |
|
6128 float ratio = float(styleFontSize) / float(aMinFontSize); |
|
6129 float inflationRatio; |
|
6130 |
|
6131 // Given a minimum inflated font size m, a specified font size s, we want to |
|
6132 // find the inflated font size i and then return the ratio of i to s (i/s). |
|
6133 if (interceptParam >= 0) { |
|
6134 // Since the mapping intercept parameter P is greater than zero, we use it |
|
6135 // to determine the point where our mapping function intersects the i=s |
|
6136 // line. This means that we have an equation of the form: |
|
6137 // |
|
6138 // i = m + s·(P/2)/(1 + P/2), if s <= (1 + P/2)·m |
|
6139 // i = s, if s >= (1 + P/2)·m |
|
6140 |
|
6141 float intercept = 1 + float(interceptParam)/2.0f; |
|
6142 if (ratio >= intercept) { |
|
6143 // If we're already at 1+P/2 or more times the minimum, don't scale. |
|
6144 return 1.0; |
|
6145 } |
|
6146 |
|
6147 // The point (intercept, intercept) is where the part of the i vs. s graph |
|
6148 // that's not slope 1 meets the i=s line. (This part of the |
|
6149 // graph is a line from (0, m), to that point). We calculate the |
|
6150 // intersection point to be ((1+P/2)m, (1+P/2)m), where P is the |
|
6151 // intercept parameter above. We then need to return i/s. |
|
6152 inflationRatio = (1.0f + (ratio * (intercept - 1) / intercept)) / ratio; |
|
6153 } else { |
|
6154 // This is the case where P is negative. We essentially want to implement |
|
6155 // the case for P=infinity here, so we make i = s + m, which means that |
|
6156 // i/s = s/s + m/s = 1 + 1/ratio |
|
6157 inflationRatio = 1 + 1.0f / ratio; |
|
6158 } |
|
6159 |
|
6160 if (maxRatio > 1.0 && inflationRatio > maxRatio) { |
|
6161 return maxRatio; |
|
6162 } else { |
|
6163 return inflationRatio; |
|
6164 } |
|
6165 } |
|
6166 |
|
6167 static bool |
|
6168 ShouldInflateFontsForContainer(const nsIFrame *aFrame) |
|
6169 { |
|
6170 // We only want to inflate fonts for text that is in a place |
|
6171 // with room to expand. The question is what the best heuristic for |
|
6172 // that is... |
|
6173 // For now, we're going to use NS_FRAME_IN_CONSTRAINED_HEIGHT, which |
|
6174 // indicates whether the frame is inside something with a constrained |
|
6175 // height (propagating down the tree), but the propagation stops when |
|
6176 // we hit overflow-y: scroll or auto. |
|
6177 const nsStyleText* styleText = aFrame->StyleText(); |
|
6178 |
|
6179 return styleText->mTextSizeAdjust != NS_STYLE_TEXT_SIZE_ADJUST_NONE && |
|
6180 !(aFrame->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT) && |
|
6181 // We also want to disable font inflation for containers that have |
|
6182 // preformatted text. |
|
6183 styleText->WhiteSpaceCanWrap(aFrame); |
|
6184 } |
|
6185 |
|
6186 nscoord |
|
6187 nsLayoutUtils::InflationMinFontSizeFor(const nsIFrame *aFrame) |
|
6188 { |
|
6189 nsPresContext *presContext = aFrame->PresContext(); |
|
6190 if (!FontSizeInflationEnabled(presContext) || |
|
6191 presContext->mInflationDisabledForShrinkWrap) { |
|
6192 return 0; |
|
6193 } |
|
6194 |
|
6195 for (const nsIFrame *f = aFrame; f; f = f->GetParent()) { |
|
6196 if (f->IsContainerForFontSizeInflation()) { |
|
6197 if (!ShouldInflateFontsForContainer(f)) { |
|
6198 return 0; |
|
6199 } |
|
6200 |
|
6201 nsFontInflationData *data = |
|
6202 nsFontInflationData::FindFontInflationDataFor(aFrame); |
|
6203 // FIXME: The need to null-check here is sort of a bug, and might |
|
6204 // lead to incorrect results. |
|
6205 if (!data || !data->InflationEnabled()) { |
|
6206 return 0; |
|
6207 } |
|
6208 |
|
6209 return MinimumFontSizeFor(aFrame->PresContext(), |
|
6210 data->EffectiveWidth()); |
|
6211 } |
|
6212 } |
|
6213 |
|
6214 NS_ABORT_IF_FALSE(false, "root should always be container"); |
|
6215 |
|
6216 return 0; |
|
6217 } |
|
6218 |
|
6219 float |
|
6220 nsLayoutUtils::FontSizeInflationFor(const nsIFrame *aFrame) |
|
6221 { |
|
6222 if (aFrame->IsSVGText()) { |
|
6223 const nsIFrame* container = aFrame; |
|
6224 while (container->GetType() != nsGkAtoms::svgTextFrame) { |
|
6225 container = container->GetParent(); |
|
6226 } |
|
6227 NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame"); |
|
6228 return |
|
6229 static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor(); |
|
6230 } |
|
6231 |
|
6232 if (!FontSizeInflationEnabled(aFrame->PresContext())) { |
|
6233 return 1.0f; |
|
6234 } |
|
6235 |
|
6236 return FontSizeInflationInner(aFrame, InflationMinFontSizeFor(aFrame)); |
|
6237 } |
|
6238 |
|
6239 /* static */ bool |
|
6240 nsLayoutUtils::FontSizeInflationEnabled(nsPresContext *aPresContext) |
|
6241 { |
|
6242 nsIPresShell* presShell = aPresContext->GetPresShell(); |
|
6243 |
|
6244 if (!presShell) { |
|
6245 return false; |
|
6246 } |
|
6247 |
|
6248 return presShell->FontSizeInflationEnabled(); |
|
6249 } |
|
6250 |
|
6251 /* static */ nsRect |
|
6252 nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame, |
|
6253 const nsSize& aFrameSize) |
|
6254 { |
|
6255 nsCSSShadowArray* boxShadows = aFrame->StyleBorder()->mBoxShadow; |
|
6256 if (!boxShadows) { |
|
6257 return nsRect(); |
|
6258 } |
|
6259 |
|
6260 nsRect shadows; |
|
6261 int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
6262 for (uint32_t i = 0; i < boxShadows->Length(); ++i) { |
|
6263 nsRect tmpRect(nsPoint(0, 0), aFrameSize); |
|
6264 nsCSSShadowItem* shadow = boxShadows->ShadowAt(i); |
|
6265 |
|
6266 // inset shadows are never painted outside the frame |
|
6267 if (shadow->mInset) |
|
6268 continue; |
|
6269 |
|
6270 tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset)); |
|
6271 tmpRect.Inflate(shadow->mSpread); |
|
6272 tmpRect.Inflate( |
|
6273 nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D)); |
|
6274 shadows.UnionRect(shadows, tmpRect); |
|
6275 } |
|
6276 return shadows; |
|
6277 } |
|
6278 |
|
6279 /* static */ void |
|
6280 nsLayoutUtils::UpdateImageVisibilityForFrame(nsIFrame* aImageFrame) |
|
6281 { |
|
6282 #ifdef DEBUG |
|
6283 nsIAtom* type = aImageFrame->GetType(); |
|
6284 MOZ_ASSERT(type == nsGkAtoms::imageFrame || |
|
6285 type == nsGkAtoms::imageControlFrame || |
|
6286 type == nsGkAtoms::svgImageFrame, "wrong type of frame"); |
|
6287 #endif |
|
6288 |
|
6289 nsCOMPtr<nsIImageLoadingContent> content = do_QueryInterface(aImageFrame->GetContent()); |
|
6290 if (!content) { |
|
6291 return; |
|
6292 } |
|
6293 |
|
6294 nsIPresShell* presShell = aImageFrame->PresContext()->PresShell(); |
|
6295 if (presShell->AssumeAllImagesVisible()) { |
|
6296 presShell->EnsureImageInVisibleList(content); |
|
6297 return; |
|
6298 } |
|
6299 |
|
6300 bool visible = true; |
|
6301 nsIFrame* f = aImageFrame->GetParent(); |
|
6302 nsRect rect = aImageFrame->GetContentRectRelativeToSelf(); |
|
6303 nsIFrame* rectFrame = aImageFrame; |
|
6304 while (f) { |
|
6305 nsIScrollableFrame* sf = do_QueryFrame(f); |
|
6306 if (sf) { |
|
6307 nsRect transformedRect = |
|
6308 nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f); |
|
6309 if (!sf->IsRectNearlyVisible(transformedRect)) { |
|
6310 visible = false; |
|
6311 break; |
|
6312 } |
|
6313 // Move transformedRect to be contained in the scrollport as best we can |
|
6314 // (it might not fit) to pretend that it was scrolled into view. |
|
6315 nsRect scrollPort = sf->GetScrollPortRect(); |
|
6316 if (transformedRect.XMost() > scrollPort.XMost()) { |
|
6317 transformedRect.x -= transformedRect.XMost() - scrollPort.XMost(); |
|
6318 } |
|
6319 if (transformedRect.x < scrollPort.x) { |
|
6320 transformedRect.x = scrollPort.x; |
|
6321 } |
|
6322 if (transformedRect.YMost() > scrollPort.YMost()) { |
|
6323 transformedRect.y -= transformedRect.YMost() - scrollPort.YMost(); |
|
6324 } |
|
6325 if (transformedRect.y < scrollPort.y) { |
|
6326 transformedRect.y = scrollPort.y; |
|
6327 } |
|
6328 transformedRect.width = std::min(transformedRect.width, scrollPort.width); |
|
6329 transformedRect.height = std::min(transformedRect.height, scrollPort.height); |
|
6330 rect = transformedRect; |
|
6331 rectFrame = f; |
|
6332 } |
|
6333 nsIFrame* parent = f->GetParent(); |
|
6334 if (!parent) { |
|
6335 parent = nsLayoutUtils::GetCrossDocParentFrame(f); |
|
6336 if (parent && parent->PresContext()->IsChrome()) { |
|
6337 break; |
|
6338 } |
|
6339 } |
|
6340 f = parent; |
|
6341 } |
|
6342 |
|
6343 if (visible) { |
|
6344 presShell->EnsureImageInVisibleList(content); |
|
6345 } else { |
|
6346 presShell->RemoveImageFromVisibleList(content); |
|
6347 } |
|
6348 } |
|
6349 |
|
6350 /* static */ nsSize |
|
6351 nsLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame* aFrame) |
|
6352 { |
|
6353 nsSize size(aFrame->GetSize()); |
|
6354 |
|
6355 nsPresContext* presContext = aFrame->PresContext(); |
|
6356 nsIPresShell* presShell = presContext->PresShell(); |
|
6357 |
|
6358 // See the comments in the code that calculates the root |
|
6359 // composition bounds in RecordFrameMetrics. |
|
6360 // TODO: Reuse that code here. |
|
6361 bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument() |
|
6362 && aFrame == presShell->GetRootScrollFrame(); |
|
6363 if (isRootContentDocRootScrollFrame) { |
|
6364 if (nsIFrame* rootFrame = presShell->GetRootFrame()) { |
|
6365 if (nsView* view = rootFrame->GetView()) { |
|
6366 nsSize viewSize = view->GetBounds().Size(); |
|
6367 nsIWidget* widget = |
|
6368 #ifdef MOZ_WIDGET_ANDROID |
|
6369 rootFrame->GetNearestWidget(); |
|
6370 #else |
|
6371 view->GetWidget(); |
|
6372 #endif |
|
6373 if (widget) { |
|
6374 nsIntRect widgetBounds; |
|
6375 widget->GetBounds(widgetBounds); |
|
6376 int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel(); |
|
6377 size = nsSize(widgetBounds.width * auPerDevPixel, |
|
6378 widgetBounds.height * auPerDevPixel); |
|
6379 #ifdef MOZ_WIDGET_ANDROID |
|
6380 if (viewSize.height < size.height) { |
|
6381 size.height = viewSize.height; |
|
6382 } |
|
6383 #endif |
|
6384 } else { |
|
6385 size = viewSize; |
|
6386 } |
|
6387 } |
|
6388 } |
|
6389 } |
|
6390 |
|
6391 // Adjust composition bounds for the size of scroll bars. |
|
6392 nsIScrollableFrame* scrollableFrame = aFrame->GetScrollTargetFrame(); |
|
6393 if (scrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) { |
|
6394 nsMargin margins = scrollableFrame->GetActualScrollbarSizes(); |
|
6395 size.width -= margins.LeftRight(); |
|
6396 size.height -= margins.TopBottom(); |
|
6397 } |
|
6398 |
|
6399 return size; |
|
6400 } |
|
6401 /* static */ CSSSize |
|
6402 nsLayoutUtils::CalculateRootCompositionSize(nsIFrame* aFrame, |
|
6403 bool aIsRootContentDocRootScrollFrame, |
|
6404 const FrameMetrics& aMetrics) |
|
6405 { |
|
6406 |
|
6407 if (aIsRootContentDocRootScrollFrame) { |
|
6408 return ViewAs<LayerPixel>(ParentLayerSize(aMetrics.mCompositionBounds.Size()), |
|
6409 PixelCastJustification::ParentLayerToLayerForRootComposition) |
|
6410 / aMetrics.LayersPixelsPerCSSPixel(); |
|
6411 } |
|
6412 nsPresContext* presContext = aFrame->PresContext(); |
|
6413 LayerSize rootCompositionSize; |
|
6414 nsPresContext* rootPresContext = |
|
6415 presContext->GetToplevelContentDocumentPresContext(); |
|
6416 if (!rootPresContext) { |
|
6417 rootPresContext = presContext->GetRootPresContext(); |
|
6418 } |
|
6419 nsIPresShell* rootPresShell = nullptr; |
|
6420 if (rootPresContext) { |
|
6421 // See the comments in the code that calculates the root |
|
6422 // composition bounds in RecordFrameMetrics. |
|
6423 // TODO: Reuse that code here. |
|
6424 nsIPresShell* rootPresShell = rootPresContext->PresShell(); |
|
6425 if (nsIFrame* rootFrame = rootPresShell->GetRootFrame()) { |
|
6426 if (nsView* view = rootFrame->GetView()) { |
|
6427 LayoutDeviceToParentLayerScale parentResolution( |
|
6428 rootPresShell->GetCumulativeResolution().width |
|
6429 / rootPresShell->GetResolution().width); |
|
6430 int32_t rootAUPerDevPixel = rootPresContext->AppUnitsPerDevPixel(); |
|
6431 nsRect viewBounds = view->GetBounds(); |
|
6432 LayerSize viewSize = ViewAs<LayerPixel>( |
|
6433 (LayoutDeviceRect::FromAppUnits(viewBounds, rootAUPerDevPixel) |
|
6434 * parentResolution).Size(), PixelCastJustification::ParentLayerToLayerForRootComposition); |
|
6435 nsIWidget* widget = |
|
6436 #ifdef MOZ_WIDGET_ANDROID |
|
6437 rootFrame->GetNearestWidget(); |
|
6438 #else |
|
6439 view->GetWidget(); |
|
6440 #endif |
|
6441 if (widget) { |
|
6442 nsIntRect widgetBounds; |
|
6443 widget->GetBounds(widgetBounds); |
|
6444 rootCompositionSize = LayerSize(ViewAs<LayerPixel>(widgetBounds.Size())); |
|
6445 #ifdef MOZ_WIDGET_ANDROID |
|
6446 if (viewSize.height < rootCompositionSize.height) { |
|
6447 rootCompositionSize.height = viewSize.height; |
|
6448 } |
|
6449 #endif |
|
6450 } else { |
|
6451 rootCompositionSize = viewSize; |
|
6452 } |
|
6453 } |
|
6454 } |
|
6455 } else { |
|
6456 nsIWidget* widget = aFrame->GetNearestWidget(); |
|
6457 nsIntRect widgetBounds; |
|
6458 widget->GetBounds(widgetBounds); |
|
6459 rootCompositionSize = LayerSize(ViewAs<LayerPixel>(widgetBounds.Size())); |
|
6460 } |
|
6461 |
|
6462 // Adjust composition size for the size of scroll bars. |
|
6463 nsIFrame* rootRootScrollFrame = rootPresShell ? rootPresShell->GetRootScrollFrame() : nullptr; |
|
6464 nsIScrollableFrame* rootScrollableFrame = nullptr; |
|
6465 if (rootRootScrollFrame) { |
|
6466 rootScrollableFrame = rootRootScrollFrame->GetScrollTargetFrame(); |
|
6467 } |
|
6468 if (rootScrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) { |
|
6469 CSSMargin margins = CSSMargin::FromAppUnits(rootScrollableFrame->GetActualScrollbarSizes()); |
|
6470 // Scrollbars are not subject to scaling, so CSS pixels = layer pixels for them. |
|
6471 rootCompositionSize.width -= margins.LeftRight(); |
|
6472 rootCompositionSize.height -= margins.TopBottom(); |
|
6473 } |
|
6474 |
|
6475 return rootCompositionSize / aMetrics.LayersPixelsPerCSSPixel(); |
|
6476 } |
|
6477 |
|
6478 /* static */ nsRect |
|
6479 nsLayoutUtils::CalculateScrollableRectForFrame(nsIScrollableFrame* aScrollableFrame, nsIFrame* aRootFrame) |
|
6480 { |
|
6481 nsRect contentBounds; |
|
6482 if (aScrollableFrame) { |
|
6483 contentBounds = aScrollableFrame->GetScrollRange(); |
|
6484 |
|
6485 // We ifndef the below code for Fennec because it requires special behaviour |
|
6486 // on the APZC side. Because Fennec has it's own PZC implementation which doesn't |
|
6487 // provide the special behaviour, this code will cause it to break. We can remove |
|
6488 // the ifndef once Fennec switches over to APZ or if we add the special handling |
|
6489 // to Fennec |
|
6490 #ifndef MOZ_WIDGET_ANDROID |
|
6491 nsPoint scrollPosition = aScrollableFrame->GetScrollPosition(); |
|
6492 if (aScrollableFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) { |
|
6493 contentBounds.y = scrollPosition.y; |
|
6494 contentBounds.height = 0; |
|
6495 } |
|
6496 if (aScrollableFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) { |
|
6497 contentBounds.x = scrollPosition.x; |
|
6498 contentBounds.width = 0; |
|
6499 } |
|
6500 #endif |
|
6501 |
|
6502 contentBounds.width += aScrollableFrame->GetScrollPortRect().width; |
|
6503 contentBounds.height += aScrollableFrame->GetScrollPortRect().height; |
|
6504 } else { |
|
6505 contentBounds = aRootFrame->GetRect(); |
|
6506 } |
|
6507 return contentBounds; |
|
6508 } |
|
6509 |
|
6510 /* static */ nsRect |
|
6511 nsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame* aFrame) |
|
6512 { |
|
6513 nsRect scrollableRect = |
|
6514 CalculateScrollableRectForFrame(aFrame->GetScrollTargetFrame(), |
|
6515 aFrame->PresContext()->PresShell()->GetRootFrame()); |
|
6516 nsSize compSize = CalculateCompositionSizeForFrame(aFrame); |
|
6517 |
|
6518 if (aFrame == aFrame->PresContext()->PresShell()->GetRootScrollFrame()) { |
|
6519 // the composition size for the root scroll frame does not include the |
|
6520 // local resolution, so we adjust. |
|
6521 gfxSize res = aFrame->PresContext()->PresShell()->GetResolution(); |
|
6522 compSize.width = NSToCoordRound(compSize.width / ((float) res.width)); |
|
6523 compSize.height = NSToCoordRound(compSize.height / ((float) res.height)); |
|
6524 } |
|
6525 |
|
6526 if (scrollableRect.width < compSize.width) { |
|
6527 scrollableRect.x = std::max(0, |
|
6528 scrollableRect.x - (compSize.width - scrollableRect.width)); |
|
6529 scrollableRect.width = compSize.width; |
|
6530 } |
|
6531 |
|
6532 if (scrollableRect.height < compSize.height) { |
|
6533 scrollableRect.y = std::max(0, |
|
6534 scrollableRect.y - (compSize.height - scrollableRect.height)); |
|
6535 scrollableRect.height = compSize.height; |
|
6536 } |
|
6537 return scrollableRect; |
|
6538 } |
|
6539 |
|
6540 /* static */ bool |
|
6541 nsLayoutUtils::WantSubAPZC() |
|
6542 { |
|
6543 // TODO Turn this on for inprocess OMTC on all platforms |
|
6544 bool wantSubAPZC = gfxPrefs::APZSubframeEnabled(); |
|
6545 #ifdef MOZ_WIDGET_GONK |
|
6546 if (XRE_GetProcessType() != GeckoProcessType_Content) { |
|
6547 wantSubAPZC = false; |
|
6548 } |
|
6549 #endif |
|
6550 return wantSubAPZC; |
|
6551 } |
|
6552 |
|
6553 nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult() |
|
6554 // Use safe default values here |
|
6555 : mIsWriteOnly(true) |
|
6556 , mIsStillLoading(false) |
|
6557 , mCORSUsed(false) |
|
6558 , mIsPremultiplied(true) |
|
6559 { |
|
6560 } |
|
6561 |
|
6562 bool |
|
6563 nsLayoutUtils::IsNonWrapperBlock(nsIFrame* aFrame) |
|
6564 { |
|
6565 return GetAsBlock(aFrame) && !aFrame->IsBlockWrapper(); |
|
6566 } |
|
6567 |
|
6568 bool |
|
6569 nsLayoutUtils::NeedsPrintPreviewBackground(nsPresContext* aPresContext) |
|
6570 { |
|
6571 return aPresContext->IsRootPaginatedDocument() && |
|
6572 (aPresContext->Type() == nsPresContext::eContext_PrintPreview || |
|
6573 aPresContext->Type() == nsPresContext::eContext_PageLayout); |
|
6574 } |
|
6575 |
|
6576 AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame *aFrame) |
|
6577 { |
|
6578 // FIXME: Now that inflation calculations are based on the flow |
|
6579 // root's NCA's (nearest common ancestor of its inflatable |
|
6580 // descendants) width, we could probably disable inflation in |
|
6581 // fewer cases than we currently do. |
|
6582 if (aFrame->IsContainerForFontSizeInflation()) { |
|
6583 mPresContext = aFrame->PresContext(); |
|
6584 mOldValue = mPresContext->mInflationDisabledForShrinkWrap; |
|
6585 mPresContext->mInflationDisabledForShrinkWrap = true; |
|
6586 } else { |
|
6587 // indicate we have nothing to restore |
|
6588 mPresContext = nullptr; |
|
6589 } |
|
6590 } |
|
6591 |
|
6592 AutoMaybeDisableFontInflation::~AutoMaybeDisableFontInflation() |
|
6593 { |
|
6594 if (mPresContext) { |
|
6595 mPresContext->mInflationDisabledForShrinkWrap = mOldValue; |
|
6596 } |
|
6597 } |