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/. */
7 #include "nsLayoutUtils.h"
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/BasicEvents.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/MemoryReporting.h"
13 #include "nsPresContext.h"
14 #include "nsIContent.h"
15 #include "nsIDOMHTMLDocument.h"
16 #include "nsIDOMHTMLElement.h"
17 #include "nsFrameList.h"
18 #include "nsGkAtoms.h"
20 #include "nsCSSPseudoElements.h"
21 #include "nsCSSAnonBoxes.h"
22 #include "nsCSSColorUtils.h"
24 #include "nsViewManager.h"
25 #include "nsPlaceholderFrame.h"
26 #include "nsIScrollableFrame.h"
27 #include "nsIDOMEvent.h"
28 #include "nsDisplayList.h"
30 #include "nsFrameManager.h"
31 #include "nsBlockFrame.h"
32 #include "nsBidiPresUtils.h"
33 #include "imgIContainer.h"
35 #include "ImageRegion.h"
37 #include "gfxContext.h"
38 #include "nsRenderingContext.h"
39 #include "nsIInterfaceRequestorUtils.h"
40 #include "nsCSSRendering.h"
41 #include "nsThemeConstants.h"
42 #include "nsPIDOMWindow.h"
43 #include "nsIDocShell.h"
44 #include "nsIWidget.h"
45 #include "gfxMatrix.h"
49 #include "mozilla/dom/HTMLCanvasElement.h"
50 #include "nsICanvasRenderingContextInternal.h"
51 #include "gfxPlatform.h"
53 #include "mozilla/dom/HTMLVideoElement.h"
54 #include "mozilla/dom/HTMLImageElement.h"
55 #include "mozilla/dom/DOMRect.h"
56 #include "imgIRequest.h"
57 #include "nsIImageLoadingContent.h"
59 #include "nsCSSProps.h"
60 #include "nsListControlFrame.h"
61 #include "mozilla/dom/Element.h"
62 #include "nsCanvasFrame.h"
63 #include "gfxDrawable.h"
65 #include "nsDataHashtable.h"
66 #include "nsTextFrame.h"
67 #include "nsFontFaceList.h"
68 #include "nsFontInflationData.h"
69 #include "nsSVGUtils.h"
70 #include "SVGImageContext.h"
71 #include "SVGTextFrame.h"
72 #include "nsStyleStructInlines.h"
73 #include "nsStyleTransformMatrix.h"
74 #include "nsIFrameInlines.h"
75 #include "ImageContainer.h"
76 #include "nsComputedDOMStyle.h"
77 #include "ActiveLayerTracker.h"
78 #include "mozilla/gfx/2D.h"
79 #include "gfx2DGlue.h"
80 #include "mozilla/LookAndFeel.h"
81 #include "UnitTransforms.h"
82 #include "TiledLayerBuffer.h" // For TILEDLAYERBUFFER_TILE_SIZE
83 #include "ClientLayerManager.h"
84 #include "nsRefreshDriver.h"
85 #include "nsIContentViewer.h"
86 #include "LayersLogging.h"
87 #include "mozilla/Preferences.h"
88 #include "nsFrameSelection.h"
89 #include "FrameLayerBuilder.h"
92 #include "nsXULPopupManager.h"
95 #include "GeckoProfiler.h"
96 #include "nsAnimationManager.h"
97 #include "nsTransitionManager.h"
98 #include "RestyleManager.h"
100 // Additional includes used on B2G by code in GetOrMaybeCreateDisplayPort().
101 #ifdef MOZ_WIDGET_GONK
102 #include "mozilla/layers/AsyncPanZoomController.h"
105 using namespace mozilla
;
106 using namespace mozilla::dom
;
107 using namespace mozilla::image
;
108 using namespace mozilla::layers
;
109 using namespace mozilla::layout
;
110 using namespace mozilla::gfx
;
112 #define GRID_ENABLED_PREF_NAME "layout.css.grid.enabled"
113 #define RUBY_ENABLED_PREF_NAME "layout.css.ruby.enabled"
114 #define STICKY_ENABLED_PREF_NAME "layout.css.sticky.enabled"
115 #define TEXT_ALIGN_TRUE_ENABLED_PREF_NAME "layout.css.text-align-true-value.enabled"
118 // TODO: remove, see bug 598468.
119 bool nsLayoutUtils::gPreventAssertInCompareTreePosition
= false;
122 typedef FrameMetrics::ViewID ViewID
;
124 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationEmPerLine
;
125 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationMinTwips
;
126 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationLineThreshold
;
127 /* static */ int32_t nsLayoutUtils::sFontSizeInflationMappingIntercept
;
128 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationMaxRatio
;
129 /* static */ bool nsLayoutUtils::sFontSizeInflationForceEnabled
;
130 /* static */ bool nsLayoutUtils::sFontSizeInflationDisabledInMasterProcess
;
131 /* static */ bool nsLayoutUtils::sInvalidationDebuggingIsEnabled
;
132 /* static */ bool nsLayoutUtils::sCSSVariablesEnabled
;
133 /* static */ bool nsLayoutUtils::sInterruptibleReflowEnabled
;
135 static ViewID sScrollIdCounter
= FrameMetrics::START_SCROLL_ID
;
137 typedef nsDataHashtable
<nsUint64HashKey
, nsIContent
*> ContentMap
;
138 static ContentMap
* sContentMap
= nullptr;
139 static ContentMap
& GetContentMap() {
141 sContentMap
= new ContentMap();
146 // When the pref "layout.css.grid.enabled" changes, this function is invoked
147 // to let us update kDisplayKTable, to selectively disable or restore the
148 // entries for "grid" and "inline-grid" in that table.
150 GridEnabledPrefChangeCallback(const char* aPrefName
, void* aClosure
)
152 MOZ_ASSERT(strncmp(aPrefName
, GRID_ENABLED_PREF_NAME
,
153 ArrayLength(GRID_ENABLED_PREF_NAME
)) == 0,
154 "We only registered this callback for a single pref, so it "
155 "should only be called for that pref");
157 static int32_t sIndexOfGridInDisplayTable
;
158 static int32_t sIndexOfInlineGridInDisplayTable
;
159 static bool sAreGridKeywordIndicesInitialized
; // initialized to false
162 Preferences::GetBool(GRID_ENABLED_PREF_NAME
, false);
163 if (!sAreGridKeywordIndicesInitialized
) {
164 // First run: find the position of "grid" and "inline-grid" in
166 sIndexOfGridInDisplayTable
=
167 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_grid
,
168 nsCSSProps::kDisplayKTable
);
169 MOZ_ASSERT(sIndexOfGridInDisplayTable
>= 0,
170 "Couldn't find grid in kDisplayKTable");
171 sIndexOfInlineGridInDisplayTable
=
172 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_grid
,
173 nsCSSProps::kDisplayKTable
);
174 MOZ_ASSERT(sIndexOfInlineGridInDisplayTable
>= 0,
175 "Couldn't find inline-grid in kDisplayKTable");
176 sAreGridKeywordIndicesInitialized
= true;
179 // OK -- now, stomp on or restore the "grid" entries in kDisplayKTable,
180 // depending on whether the grid pref is enabled vs. disabled.
181 if (sIndexOfGridInDisplayTable
>= 0) {
182 nsCSSProps::kDisplayKTable
[sIndexOfGridInDisplayTable
] =
183 isGridEnabled
? eCSSKeyword_grid
: eCSSKeyword_UNKNOWN
;
185 if (sIndexOfInlineGridInDisplayTable
>= 0) {
186 nsCSSProps::kDisplayKTable
[sIndexOfInlineGridInDisplayTable
] =
187 isGridEnabled
? eCSSKeyword_inline_grid
: eCSSKeyword_UNKNOWN
;
192 RubyEnabledPrefChangeCallback(const char* aPrefName
, void* aClosure
)
194 MOZ_ASSERT(strncmp(aPrefName
, RUBY_ENABLED_PREF_NAME
,
195 ArrayLength(RUBY_ENABLED_PREF_NAME
)) == 0,
196 "We only registered this callback for a single pref, so it "
197 "should only be called for that pref");
199 static int32_t sIndexOfRubyInDisplayTable
;
200 static int32_t sIndexOfRubyBaseInDisplayTable
;
201 static int32_t sIndexOfRubyBaseContainerInDisplayTable
;
202 static int32_t sIndexOfRubyTextInDisplayTable
;
203 static int32_t sIndexOfRubyTextContainerInDisplayTable
;
204 static bool sAreRubyKeywordIndicesInitialized
; // initialized to false
207 Preferences::GetBool(RUBY_ENABLED_PREF_NAME
, false);
208 if (!sAreRubyKeywordIndicesInitialized
) {
209 // First run: find the position of the ruby display values in
211 sIndexOfRubyInDisplayTable
=
212 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_ruby
,
213 nsCSSProps::kDisplayKTable
);
214 MOZ_ASSERT(sIndexOfRubyInDisplayTable
>= 0,
215 "Couldn't find ruby in kDisplayKTable");
216 sIndexOfRubyBaseInDisplayTable
=
217 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_ruby_base
,
218 nsCSSProps::kDisplayKTable
);
219 MOZ_ASSERT(sIndexOfRubyBaseInDisplayTable
>= 0,
220 "Couldn't find ruby-base in kDisplayKTable");
221 sIndexOfRubyBaseContainerInDisplayTable
=
222 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_ruby_base_container
,
223 nsCSSProps::kDisplayKTable
);
224 MOZ_ASSERT(sIndexOfRubyBaseContainerInDisplayTable
>= 0,
225 "Couldn't find ruby-base-container in kDisplayKTable");
226 sIndexOfRubyTextInDisplayTable
=
227 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_ruby_text
,
228 nsCSSProps::kDisplayKTable
);
229 MOZ_ASSERT(sIndexOfRubyTextInDisplayTable
>= 0,
230 "Couldn't find ruby-text in kDisplayKTable");
231 sIndexOfRubyTextContainerInDisplayTable
=
232 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_ruby_text_container
,
233 nsCSSProps::kDisplayKTable
);
234 MOZ_ASSERT(sIndexOfRubyTextContainerInDisplayTable
>= 0,
235 "Couldn't find ruby-text-container in kDisplayKTable");
236 sAreRubyKeywordIndicesInitialized
= true;
239 // OK -- now, stomp on or restore the "ruby" entries in kDisplayKTable,
240 // depending on whether the ruby pref is enabled vs. disabled.
241 if (sIndexOfRubyInDisplayTable
>= 0) {
242 nsCSSProps::kDisplayKTable
[sIndexOfRubyInDisplayTable
] =
243 isRubyEnabled
? eCSSKeyword_ruby
: eCSSKeyword_UNKNOWN
;
245 if (sIndexOfRubyBaseInDisplayTable
>= 0) {
246 nsCSSProps::kDisplayKTable
[sIndexOfRubyBaseInDisplayTable
] =
247 isRubyEnabled
? eCSSKeyword_ruby_base
: eCSSKeyword_UNKNOWN
;
249 if (sIndexOfRubyBaseContainerInDisplayTable
>= 0) {
250 nsCSSProps::kDisplayKTable
[sIndexOfRubyBaseContainerInDisplayTable
] =
251 isRubyEnabled
? eCSSKeyword_ruby_base_container
: eCSSKeyword_UNKNOWN
;
253 if (sIndexOfRubyTextInDisplayTable
>= 0) {
254 nsCSSProps::kDisplayKTable
[sIndexOfRubyTextInDisplayTable
] =
255 isRubyEnabled
? eCSSKeyword_ruby_text
: eCSSKeyword_UNKNOWN
;
257 if (sIndexOfRubyTextContainerInDisplayTable
>= 0) {
258 nsCSSProps::kDisplayKTable
[sIndexOfRubyTextContainerInDisplayTable
] =
259 isRubyEnabled
? eCSSKeyword_ruby_text_container
: eCSSKeyword_UNKNOWN
;
263 // When the pref "layout.css.sticky.enabled" changes, this function is invoked
264 // to let us update kPositionKTable, to selectively disable or restore the
265 // entry for "sticky" in that table.
267 StickyEnabledPrefChangeCallback(const char* aPrefName
, void* aClosure
)
269 MOZ_ASSERT(strncmp(aPrefName
, STICKY_ENABLED_PREF_NAME
,
270 ArrayLength(STICKY_ENABLED_PREF_NAME
)) == 0,
271 "We only registered this callback for a single pref, so it "
272 "should only be called for that pref");
274 static int32_t sIndexOfStickyInPositionTable
;
275 static bool sIsStickyKeywordIndexInitialized
; // initialized to false
277 bool isStickyEnabled
=
278 Preferences::GetBool(STICKY_ENABLED_PREF_NAME
, false);
280 if (!sIsStickyKeywordIndexInitialized
) {
281 // First run: find the position of "sticky" in kPositionKTable.
282 sIndexOfStickyInPositionTable
=
283 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_sticky
,
284 nsCSSProps::kPositionKTable
);
285 MOZ_ASSERT(sIndexOfStickyInPositionTable
>= 0,
286 "Couldn't find sticky in kPositionKTable");
287 sIsStickyKeywordIndexInitialized
= true;
290 // OK -- now, stomp on or restore the "sticky" entry in kPositionKTable,
291 // depending on whether the sticky pref is enabled vs. disabled.
292 nsCSSProps::kPositionKTable
[sIndexOfStickyInPositionTable
] =
293 isStickyEnabled
? eCSSKeyword_sticky
: eCSSKeyword_UNKNOWN
;
296 // When the pref "layout.css.text-align-true-value.enabled" changes, this
297 // function is called to let us update kTextAlignKTable & kTextAlignLastKTable,
298 // to selectively disable or restore the entries for "true" in those tables.
300 TextAlignTrueEnabledPrefChangeCallback(const char* aPrefName
, void* aClosure
)
302 NS_ASSERTION(strcmp(aPrefName
, TEXT_ALIGN_TRUE_ENABLED_PREF_NAME
) == 0,
303 "Did you misspell " TEXT_ALIGN_TRUE_ENABLED_PREF_NAME
" ?");
305 static bool sIsInitialized
;
306 static int32_t sIndexOfTrueInTextAlignTable
;
307 static int32_t sIndexOfTrueInTextAlignLastTable
;
308 bool isTextAlignTrueEnabled
=
309 Preferences::GetBool(TEXT_ALIGN_TRUE_ENABLED_PREF_NAME
, false);
311 if (!sIsInitialized
) {
312 // First run: find the position of "true" in kTextAlignKTable.
313 sIndexOfTrueInTextAlignTable
=
314 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_true
,
315 nsCSSProps::kTextAlignKTable
);
316 // First run: find the position of "true" in kTextAlignLastKTable.
317 sIndexOfTrueInTextAlignLastTable
=
318 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_true
,
319 nsCSSProps::kTextAlignLastKTable
);
320 sIsInitialized
= true;
323 // OK -- now, stomp on or restore the "true" entry in the keyword tables,
324 // depending on whether the pref is enabled vs. disabled.
325 MOZ_ASSERT(sIndexOfTrueInTextAlignTable
>= 0);
326 nsCSSProps::kTextAlignKTable
[sIndexOfTrueInTextAlignTable
] =
327 isTextAlignTrueEnabled
? eCSSKeyword_true
: eCSSKeyword_UNKNOWN
;
328 MOZ_ASSERT(sIndexOfTrueInTextAlignLastTable
>= 0);
329 nsCSSProps::kTextAlignLastKTable
[sIndexOfTrueInTextAlignLastTable
] =
330 isTextAlignTrueEnabled
? eCSSKeyword_true
: eCSSKeyword_UNKNOWN
;
333 static AnimationPlayerCollection
*
334 GetAnimationsOrTransitionsForCompositor(nsIContent
* aContent
,
335 nsIAtom
* aAnimationProperty
,
336 nsCSSProperty aProperty
)
338 AnimationPlayerCollection
* collection
=
339 static_cast<AnimationPlayerCollection
*>(
340 aContent
->GetProperty(aAnimationProperty
));
342 bool propertyMatches
= collection
->HasAnimationOfProperty(aProperty
);
343 if (propertyMatches
&&
344 collection
->CanPerformOnCompositorThread(
345 AnimationPlayerCollection::CanAnimate_AllowPartial
)) {
354 nsLayoutUtils::HasAnimationsForCompositor(nsIContent
* aContent
,
355 nsCSSProperty aProperty
)
357 if (!aContent
->MayHaveAnimations())
359 return GetAnimationsOrTransitionsForCompositor(
360 aContent
, nsGkAtoms::animationsProperty
, aProperty
) ||
361 GetAnimationsOrTransitionsForCompositor(
362 aContent
, nsGkAtoms::transitionsProperty
, aProperty
);
365 static AnimationPlayerCollection
*
366 GetAnimationsOrTransitions(nsIContent
* aContent
,
367 nsIAtom
* aAnimationProperty
,
368 nsCSSProperty aProperty
)
370 AnimationPlayerCollection
* collection
=
371 static_cast<AnimationPlayerCollection
*>(aContent
->GetProperty(
372 aAnimationProperty
));
374 bool propertyMatches
= collection
->HasAnimationOfProperty(aProperty
);
375 if (propertyMatches
) {
383 nsLayoutUtils::HasAnimations(nsIContent
* aContent
,
384 nsCSSProperty aProperty
)
386 if (!aContent
->MayHaveAnimations())
388 return GetAnimationsOrTransitions(aContent
, nsGkAtoms::animationsProperty
,
390 GetAnimationsOrTransitions(aContent
, nsGkAtoms::transitionsProperty
,
395 nsLayoutUtils::HasCurrentAnimations(nsIContent
* aContent
,
396 nsIAtom
* aAnimationProperty
,
397 nsPresContext
* aPresContext
)
399 if (!aContent
->MayHaveAnimations())
402 AnimationPlayerCollection
* collection
=
403 static_cast<AnimationPlayerCollection
*>(
404 aContent
->GetProperty(aAnimationProperty
));
405 return (collection
&& collection
->HasCurrentAnimations());
409 GetScaleForValue(const StyleAnimationValue
& aValue
, nsIFrame
* aFrame
)
412 NS_WARNING("No frame.");
415 if (aValue
.GetUnit() != StyleAnimationValue::eUnit_Transform
) {
416 NS_WARNING("Expected a transform.");
420 nsCSSValueSharedList
* list
= aValue
.GetCSSValueSharedListValue();
421 MOZ_ASSERT(list
->mHead
);
423 if (list
->mHead
->mValue
.GetUnit() == eCSSUnit_None
) {
424 // There is an animation, but no actual transform yet.
428 nsRect frameBounds
= aFrame
->GetRect();
430 gfx3DMatrix transform
= nsStyleTransformMatrix::ReadTransforms(
432 aFrame
->StyleContext(),
433 aFrame
->PresContext(), dontCare
, frameBounds
,
434 aFrame
->PresContext()->AppUnitsPerDevPixel());
436 gfxMatrix transform2d
;
437 bool canDraw2D
= transform
.CanDraw2D(&transform2d
);
442 return transform2d
.ScaleFactors(true);
446 GetSuitableScale(float aMaxScale
, float aMinScale
)
448 // If the minimum scale >= 1.0f, use it; if the maximum <= 1.0f, use it;
449 // otherwise use 1.0f.
450 if (aMinScale
>= 1.0f
) {
453 else if (aMaxScale
<= 1.0f
) {
461 GetMinAndMaxScaleForAnimationProperty(nsIContent
* aContent
,
462 nsIAtom
* aAnimationProperty
,
466 AnimationPlayerCollection
* collection
=
467 GetAnimationsOrTransitionsForCompositor(aContent
, aAnimationProperty
,
468 eCSSProperty_transform
);
472 for (size_t playerIdx
= collection
->mPlayers
.Length(); playerIdx
-- != 0; ) {
473 AnimationPlayer
* player
= collection
->mPlayers
[playerIdx
];
474 if (!player
->GetSource() || player
->GetSource()->IsFinishedTransition()) {
477 dom::Animation
* anim
= player
->GetSource();
478 for (size_t propIdx
= anim
->Properties().Length(); propIdx
-- != 0; ) {
479 AnimationProperty
& prop
= anim
->Properties()[propIdx
];
480 if (prop
.mProperty
== eCSSProperty_transform
) {
481 for (uint32_t segIdx
= prop
.mSegments
.Length(); segIdx
-- != 0; ) {
482 AnimationPropertySegment
& segment
= prop
.mSegments
[segIdx
];
483 gfxSize from
= GetScaleForValue(segment
.mFromValue
,
484 aContent
->GetPrimaryFrame());
485 aMaxScale
.width
= std::max
<float>(aMaxScale
.width
, from
.width
);
486 aMaxScale
.height
= std::max
<float>(aMaxScale
.height
, from
.height
);
487 aMinScale
.width
= std::min
<float>(aMinScale
.width
, from
.width
);
488 aMinScale
.height
= std::min
<float>(aMinScale
.height
, from
.height
);
489 gfxSize to
= GetScaleForValue(segment
.mToValue
,
490 aContent
->GetPrimaryFrame());
491 aMaxScale
.width
= std::max
<float>(aMaxScale
.width
, to
.width
);
492 aMaxScale
.height
= std::max
<float>(aMaxScale
.height
, to
.height
);
493 aMinScale
.width
= std::min
<float>(aMinScale
.width
, to
.width
);
494 aMinScale
.height
= std::min
<float>(aMinScale
.height
, to
.height
);
502 nsLayoutUtils::ComputeSuitableScaleForAnimation(nsIContent
* aContent
)
504 gfxSize
maxScale(1.0f
, 1.0f
);
505 gfxSize
minScale(1.0f
, 1.0f
);
507 GetMinAndMaxScaleForAnimationProperty(aContent
,
508 nsGkAtoms::animationsProperty
, maxScale
, minScale
);
509 GetMinAndMaxScaleForAnimationProperty(aContent
,
510 nsGkAtoms::transitionsProperty
, maxScale
, minScale
);
512 return gfxSize(GetSuitableScale(maxScale
.width
, minScale
.width
),
513 GetSuitableScale(maxScale
.height
, minScale
.height
));
517 nsLayoutUtils::AreAsyncAnimationsEnabled()
519 static bool sAreAsyncAnimationsEnabled
;
520 static bool sAsyncPrefCached
= false;
522 if (!sAsyncPrefCached
) {
523 sAsyncPrefCached
= true;
524 Preferences::AddBoolVarCache(&sAreAsyncAnimationsEnabled
,
525 "layers.offmainthreadcomposition.async-animations");
528 return sAreAsyncAnimationsEnabled
&&
529 gfxPlatform::OffMainThreadCompositingEnabled();
533 nsLayoutUtils::IsAnimationLoggingEnabled()
535 static bool sShouldLog
;
536 static bool sShouldLogPrefCached
;
538 if (!sShouldLogPrefCached
) {
539 sShouldLogPrefCached
= true;
540 Preferences::AddBoolVarCache(&sShouldLog
,
541 "layers.offmainthreadcomposition.log-animations");
548 nsLayoutUtils::UseBackgroundNearestFiltering()
550 static bool sUseBackgroundNearestFilteringEnabled
;
551 static bool sUseBackgroundNearestFilteringPrefInitialised
= false;
553 if (!sUseBackgroundNearestFilteringPrefInitialised
) {
554 sUseBackgroundNearestFilteringPrefInitialised
= true;
555 sUseBackgroundNearestFilteringEnabled
=
556 Preferences::GetBool("gfx.filter.nearest.force-enabled", false);
559 return sUseBackgroundNearestFilteringEnabled
;
563 nsLayoutUtils::GPUImageScalingEnabled()
565 static bool sGPUImageScalingEnabled
;
566 static bool sGPUImageScalingPrefInitialised
= false;
568 if (!sGPUImageScalingPrefInitialised
) {
569 sGPUImageScalingPrefInitialised
= true;
570 sGPUImageScalingEnabled
=
571 Preferences::GetBool("layout.gpu-image-scaling.enabled", false);
574 return sGPUImageScalingEnabled
;
578 nsLayoutUtils::AnimatedImageLayersEnabled()
580 static bool sAnimatedImageLayersEnabled
;
581 static bool sAnimatedImageLayersPrefCached
= false;
583 if (!sAnimatedImageLayersPrefCached
) {
584 sAnimatedImageLayersPrefCached
= true;
585 Preferences::AddBoolVarCache(&sAnimatedImageLayersEnabled
,
586 "layout.animated-image-layers.enabled",
590 return sAnimatedImageLayersEnabled
;
594 nsLayoutUtils::CSSFiltersEnabled()
596 static bool sCSSFiltersEnabled
;
597 static bool sCSSFiltersPrefCached
= false;
599 if (!sCSSFiltersPrefCached
) {
600 sCSSFiltersPrefCached
= true;
601 Preferences::AddBoolVarCache(&sCSSFiltersEnabled
,
602 "layout.css.filters.enabled",
606 return sCSSFiltersEnabled
;
610 nsLayoutUtils::UnsetValueEnabled()
612 static bool sUnsetValueEnabled
;
613 static bool sUnsetValuePrefCached
= false;
615 if (!sUnsetValuePrefCached
) {
616 sUnsetValuePrefCached
= true;
617 Preferences::AddBoolVarCache(&sUnsetValueEnabled
,
618 "layout.css.unset-value.enabled",
622 return sUnsetValueEnabled
;
626 nsLayoutUtils::IsTextAlignTrueValueEnabled()
628 static bool sTextAlignTrueValueEnabled
;
629 static bool sTextAlignTrueValueEnabledPrefCached
= false;
631 if (!sTextAlignTrueValueEnabledPrefCached
) {
632 sTextAlignTrueValueEnabledPrefCached
= true;
633 Preferences::AddBoolVarCache(&sTextAlignTrueValueEnabled
,
634 TEXT_ALIGN_TRUE_ENABLED_PREF_NAME
,
638 return sTextAlignTrueValueEnabled
;
642 nsLayoutUtils::UnionChildOverflow(nsIFrame
* aFrame
,
643 nsOverflowAreas
& aOverflowAreas
,
644 FrameChildListIDs aSkipChildLists
)
646 // Iterate over all children except pop-ups.
647 FrameChildListIDs skip
= aSkipChildLists
|
648 nsIFrame::kSelectPopupList
| nsIFrame::kPopupList
;
649 for (nsIFrame::ChildListIterator
childLists(aFrame
);
650 !childLists
.IsDone(); childLists
.Next()) {
651 if (skip
.Contains(childLists
.CurrentID())) {
655 nsFrameList children
= childLists
.CurrentList();
656 for (nsFrameList::Enumerator
e(children
); !e
.AtEnd(); e
.Next()) {
657 nsIFrame
* child
= e
.get();
658 nsOverflowAreas childOverflow
=
659 child
->GetOverflowAreas() + child
->GetPosition();
660 aOverflowAreas
.UnionWith(childOverflow
);
665 static void DestroyViewID(void* aObject
, nsIAtom
* aPropertyName
,
666 void* aPropertyValue
, void* aData
)
668 ViewID
* id
= static_cast<ViewID
*>(aPropertyValue
);
669 GetContentMap().Remove(*id
);
674 * A namespace class for static layout utilities.
678 nsLayoutUtils::FindIDFor(const nsIContent
* aContent
, ViewID
* aOutViewId
)
680 void* scrollIdProperty
= aContent
->GetProperty(nsGkAtoms::RemoteId
);
681 if (scrollIdProperty
) {
682 *aOutViewId
= *static_cast<ViewID
*>(scrollIdProperty
);
689 nsLayoutUtils::FindOrCreateIDFor(nsIContent
* aContent
)
693 if (!FindIDFor(aContent
, &scrollId
)) {
694 scrollId
= sScrollIdCounter
++;
695 aContent
->SetProperty(nsGkAtoms::RemoteId
, new ViewID(scrollId
),
697 GetContentMap().Put(scrollId
, aContent
);
704 nsLayoutUtils::FindContentFor(ViewID aId
)
706 NS_ABORT_IF_FALSE(aId
!= FrameMetrics::NULL_SCROLL_ID
,
707 "Cannot find a content element in map for null IDs.");
709 bool exists
= GetContentMap().Get(aId
, &content
);
719 nsLayoutUtils::FindScrollableFrameFor(ViewID aId
)
721 nsIContent
* content
= FindContentFor(aId
);
726 nsIFrame
* scrolledFrame
= content
->GetPrimaryFrame();
727 if (scrolledFrame
&& content
->OwnerDoc()->GetRootElement() == content
) {
728 // The content is the root element of a subdocument, so return the root scrollable
729 // for the subdocument.
730 scrolledFrame
= scrolledFrame
->PresContext()->PresShell()->GetRootScrollFrame();
732 return scrolledFrame
? scrolledFrame
->GetScrollTargetFrame() : nullptr;
736 ApplyRectMultiplier(nsRect aRect
, float aMultiplier
)
738 if (aMultiplier
== 1.0f
) {
741 float newWidth
= aRect
.width
* aMultiplier
;
742 float newHeight
= aRect
.height
* aMultiplier
;
743 float newX
= aRect
.x
- ((newWidth
- aRect
.width
) / 2.0f
);
744 float newY
= aRect
.y
- ((newHeight
- aRect
.height
) / 2.0f
);
745 // Rounding doesn't matter too much here, do a round-in
746 return nsRect(ceil(newX
), ceil(newY
), floor(newWidth
), floor(newHeight
));
750 GetDisplayPortFromRectData(nsIContent
* aContent
,
751 DisplayPortPropertyData
* aRectData
,
754 // In the case where the displayport is set as a rect, we assume it is
755 // already aligned and clamped as necessary. The burden to do that is
756 // on the setter of the displayport. In practice very few places set the
757 // displayport directly as a rect (mostly tests). We still do need to
758 // expand it by the multiplier though.
759 return ApplyRectMultiplier(aRectData
->mRect
, aMultiplier
);
763 GetDisplayPortFromMarginsData(nsIContent
* aContent
,
764 DisplayPortMarginsPropertyData
* aMarginsData
,
767 // In the case where the displayport is set via margins, we apply the margins
768 // to a base rect. Then we align the expanded rect based on the alignment
769 // requested, further expand the rect by the multiplier, and finally, clamp it
770 // to the size of the scrollable rect.
773 if (nsRect
* baseData
= static_cast<nsRect
*>(aContent
->GetProperty(nsGkAtoms::DisplayPortBase
))) {
776 NS_WARNING("Attempting to get a margins-based displayport with no base data!");
779 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
781 // Turns out we can't really compute it. Oops. We still should return
782 // something sane. Note that although we can apply the multiplier on the
783 // base rect here, we can't tile-align or clamp the rect without a frame.
784 NS_WARNING("Attempting to get a displayport from a content with no primary frame!");
785 return ApplyRectMultiplier(base
, aMultiplier
);
789 if (aContent
->OwnerDoc()->GetRootElement() == aContent
) {
790 // We want the scroll frame, the root scroll frame differs from all
791 // others in that the primary frame is not the scroll frame.
792 frame
= frame
->PresContext()->PresShell()->GetRootScrollFrame();
797 if (nsIScrollableFrame
* scrollableFrame
= frame
->GetScrollTargetFrame()) {
798 scrollPos
= scrollableFrame
->GetScrollPosition();
801 nsPresContext
* presContext
= frame
->PresContext();
802 int32_t auPerDevPixel
= presContext
->AppUnitsPerDevPixel();
803 gfxSize res
= presContext
->PresShell()->GetCumulativeResolution();
805 // First convert the base rect to layer pixels
806 gfxSize parentRes
= res
;
808 // the base rect for root scroll frames is specified in the parent document
809 // coordinate space, so it doesn't include the local resolution.
810 gfxSize localRes
= presContext
->PresShell()->GetResolution();
811 parentRes
.width
/= localRes
.width
;
812 parentRes
.height
/= localRes
.height
;
814 LayerRect
layerRect(NSAppUnitsToFloatPixels(base
.x
, auPerDevPixel
) * parentRes
.width
,
815 NSAppUnitsToFloatPixels(base
.y
, auPerDevPixel
) * parentRes
.height
,
816 NSAppUnitsToFloatPixels(base
.width
, auPerDevPixel
) * parentRes
.width
,
817 NSAppUnitsToFloatPixels(base
.height
, auPerDevPixel
) * parentRes
.height
);
819 // Expand the rect by the margins
820 layerRect
.Inflate(aMarginsData
->mMargins
);
822 // And then align it to the requested alignment
823 if (aMarginsData
->mAlignmentX
> 0 || aMarginsData
->mAlignmentY
> 0) {
824 // Inflate the rectangle by 1 so that we always push to the next tile
825 // boundary. This is desirable to stop from having a rectangle with a
826 // moving origin occasionally being smaller when it coincidentally lines
827 // up to tile boundaries.
828 layerRect
.Inflate(1);
830 // Avoid division by zero.
831 if (aMarginsData
->mAlignmentX
== 0) {
832 aMarginsData
->mAlignmentX
= 1;
834 if (aMarginsData
->mAlignmentY
== 0) {
835 aMarginsData
->mAlignmentY
= 1;
838 LayerPoint
scrollPosLayer(NSAppUnitsToFloatPixels(scrollPos
.x
, auPerDevPixel
) * res
.width
,
839 NSAppUnitsToFloatPixels(scrollPos
.y
, auPerDevPixel
) * res
.height
);
841 layerRect
+= scrollPosLayer
;
842 float x
= aMarginsData
->mAlignmentX
* floor(layerRect
.x
/ aMarginsData
->mAlignmentX
);
843 float y
= aMarginsData
->mAlignmentY
* floor(layerRect
.y
/ aMarginsData
->mAlignmentY
);
844 float w
= aMarginsData
->mAlignmentX
* ceil(layerRect
.XMost() / aMarginsData
->mAlignmentX
) - x
;
845 float h
= aMarginsData
->mAlignmentY
* ceil(layerRect
.YMost() / aMarginsData
->mAlignmentY
) - y
;
846 layerRect
= LayerRect(x
, y
, w
, h
);
847 layerRect
-= scrollPosLayer
;
850 // Convert the aligned rect back into app units
851 nsRect
result(NSFloatPixelsToAppUnits(layerRect
.x
/ res
.width
, auPerDevPixel
),
852 NSFloatPixelsToAppUnits(layerRect
.y
/ res
.height
, auPerDevPixel
),
853 NSFloatPixelsToAppUnits(layerRect
.width
/ res
.width
, auPerDevPixel
),
854 NSFloatPixelsToAppUnits(layerRect
.height
/ res
.height
, auPerDevPixel
));
856 // Expand it for the low-res buffer if needed
857 result
= ApplyRectMultiplier(result
, aMultiplier
);
859 // Finally, clamp it to the expanded scrollable rect.
860 nsRect expandedScrollableRect
= nsLayoutUtils::CalculateExpandedScrollableRect(frame
);
861 result
= expandedScrollableRect
.Intersect(result
+ scrollPos
) - scrollPos
;
867 GetDisplayPortImpl(nsIContent
* aContent
, nsRect
*aResult
, float aMultiplier
)
869 DisplayPortPropertyData
* rectData
=
870 static_cast<DisplayPortPropertyData
*>(aContent
->GetProperty(nsGkAtoms::DisplayPort
));
871 DisplayPortMarginsPropertyData
* marginsData
=
872 static_cast<DisplayPortMarginsPropertyData
*>(aContent
->GetProperty(nsGkAtoms::DisplayPortMargins
));
874 if (!rectData
&& !marginsData
) {
875 // This content element has no displayport data at all
880 // We have displayport data, but the caller doesn't want the actual
881 // rect, so we don't need to actually compute it.
885 if (rectData
&& marginsData
) {
886 // choose margins if equal priority
887 if (rectData
->mPriority
> marginsData
->mPriority
) {
888 marginsData
= nullptr;
894 NS_ASSERTION((rectData
== nullptr) != (marginsData
== nullptr),
895 "Only one of rectData or marginsData should be set!");
898 *aResult
= GetDisplayPortFromRectData(aContent
, rectData
, aMultiplier
);
900 *aResult
= GetDisplayPortFromMarginsData(aContent
, marginsData
, aMultiplier
);
906 nsLayoutUtils::GetDisplayPort(nsIContent
* aContent
, nsRect
*aResult
)
908 if (gfxPrefs::UseLowPrecisionBuffer()) {
909 return GetDisplayPortImpl(aContent
, aResult
, 1.0f
/ gfxPrefs::LowPrecisionResolution());
911 return GetDisplayPortImpl(aContent
, aResult
, 1.0f
);
915 nsLayoutUtils::SetDisplayPortMargins(nsIContent
* aContent
,
916 nsIPresShell
* aPresShell
,
917 const LayerMargin
& aMargins
,
918 uint32_t aAlignmentX
,
919 uint32_t aAlignmentY
,
921 RepaintMode aRepaintMode
)
923 DisplayPortMarginsPropertyData
* currentData
=
924 static_cast<DisplayPortMarginsPropertyData
*>(aContent
->GetProperty(nsGkAtoms::DisplayPortMargins
));
925 if (currentData
&& currentData
->mPriority
> aPriority
) {
929 aContent
->SetProperty(nsGkAtoms::DisplayPortMargins
,
930 new DisplayPortMarginsPropertyData(
931 aMargins
, aAlignmentX
, aAlignmentY
, aPriority
),
932 nsINode::DeleteProperty
<DisplayPortMarginsPropertyData
>);
934 if (nsLayoutUtils::UsesAsyncScrolling()) {
935 nsIFrame
* rootScrollFrame
= aPresShell
->GetRootScrollFrame();
936 if (rootScrollFrame
&& aContent
== rootScrollFrame
->GetContent()) {
937 // We are setting a root displayport for a document.
938 // If we have APZ, then set a special flag on the pres shell so
939 // that we don't get scrollbars drawn.
940 aPresShell
->SetIgnoreViewportScrolling(true);
944 if (aRepaintMode
== RepaintMode::Repaint
) {
945 nsIFrame
* rootFrame
= aPresShell
->FrameManager()->GetRootFrame();
947 rootFrame
->SchedulePaint();
953 nsLayoutUtils::SetDisplayPortBase(nsIContent
* aContent
, const nsRect
& aBase
)
955 aContent
->SetProperty(nsGkAtoms::DisplayPortBase
, new nsRect(aBase
),
956 nsINode::DeleteProperty
<nsRect
>);
960 nsLayoutUtils::SetDisplayPortBaseIfNotSet(nsIContent
* aContent
, const nsRect
& aBase
)
962 if (!aContent
->GetProperty(nsGkAtoms::DisplayPortBase
)) {
963 SetDisplayPortBase(aContent
, aBase
);
968 nsLayoutUtils::GetCriticalDisplayPort(nsIContent
* aContent
, nsRect
* aResult
)
970 if (gfxPrefs::UseLowPrecisionBuffer()) {
971 return GetDisplayPortImpl(aContent
, aResult
, 1.0f
);
977 nsLayoutUtils::LastContinuationWithChild(nsContainerFrame
* aFrame
)
979 NS_PRECONDITION(aFrame
, "NULL frame pointer");
980 nsIFrame
* f
= aFrame
->LastContinuation();
981 while (!f
->GetFirstPrincipalChild() && f
->GetPrevContinuation()) {
982 f
= f
->GetPrevContinuation();
984 return static_cast<nsContainerFrame
*>(f
);
988 * GetFirstChildFrame returns the first "real" child frame of a
989 * given frame. It will descend down into pseudo-frames (unless the
990 * pseudo-frame is the :before generated frame).
991 * @param aFrame the frame
992 * @param aFrame the frame's content node
995 GetFirstChildFrame(nsIFrame
* aFrame
,
996 nsIContent
* aContent
)
998 NS_PRECONDITION(aFrame
, "NULL frame pointer");
1000 // Get the first child frame
1001 nsIFrame
* childFrame
= aFrame
->GetFirstPrincipalChild();
1003 // If the child frame is a pseudo-frame, then return its first child.
1004 // Note that the frame we create for the generated content is also a
1005 // pseudo-frame and so don't drill down in that case
1007 childFrame
->IsPseudoFrame(aContent
) &&
1008 !childFrame
->IsGeneratedContentFrame()) {
1009 return GetFirstChildFrame(childFrame
, aContent
);
1016 * GetLastChildFrame returns the last "real" child frame of a
1017 * given frame. It will descend down into pseudo-frames (unless the
1018 * pseudo-frame is the :after generated frame).
1019 * @param aFrame the frame
1020 * @param aFrame the frame's content node
1023 GetLastChildFrame(nsContainerFrame
* aFrame
,
1024 nsIContent
* aContent
)
1026 NS_PRECONDITION(aFrame
, "NULL frame pointer");
1028 // Get the last continuation frame that's a parent
1029 nsContainerFrame
* lastParentContinuation
=
1030 nsLayoutUtils::LastContinuationWithChild(aFrame
);
1031 nsIFrame
* lastChildFrame
=
1032 lastParentContinuation
->GetLastChild(nsIFrame::kPrincipalList
);
1033 if (lastChildFrame
) {
1034 // Get the frame's first continuation. This matters in case the frame has
1035 // been continued across multiple lines or split by BiDi resolution.
1036 lastChildFrame
= lastChildFrame
->FirstContinuation();
1038 // If the last child frame is a pseudo-frame, then return its last child.
1039 // Note that the frame we create for the generated content is also a
1040 // pseudo-frame and so don't drill down in that case
1041 if (lastChildFrame
&&
1042 lastChildFrame
->IsPseudoFrame(aContent
) &&
1043 !lastChildFrame
->IsGeneratedContentFrame()) {
1044 return GetLastChildFrame(static_cast<nsContainerFrame
*>(lastChildFrame
),
1048 return lastChildFrame
;
1056 nsLayoutUtils::GetChildListNameFor(nsIFrame
* aChildFrame
)
1058 nsIFrame::ChildListID id
= nsIFrame::kPrincipalList
;
1060 if (aChildFrame
->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER
) {
1061 nsIFrame
* pif
= aChildFrame
->GetPrevInFlow();
1062 if (pif
->GetParent() == aChildFrame
->GetParent()) {
1063 id
= nsIFrame::kExcessOverflowContainersList
;
1066 id
= nsIFrame::kOverflowContainersList
;
1069 // See if the frame is moved out of the flow
1070 else if (aChildFrame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
) {
1071 // Look at the style information to tell
1072 const nsStyleDisplay
* disp
= aChildFrame
->StyleDisplay();
1074 if (NS_STYLE_POSITION_ABSOLUTE
== disp
->mPosition
) {
1075 id
= nsIFrame::kAbsoluteList
;
1076 } else if (NS_STYLE_POSITION_FIXED
== disp
->mPosition
) {
1077 if (nsLayoutUtils::IsReallyFixedPos(aChildFrame
)) {
1078 id
= nsIFrame::kFixedList
;
1080 id
= nsIFrame::kAbsoluteList
;
1083 } else if (NS_STYLE_DISPLAY_POPUP
== disp
->mDisplay
) {
1084 // Out-of-flows that are DISPLAY_POPUP must be kids of the root popup set
1086 nsIFrame
* parent
= aChildFrame
->GetParent();
1087 NS_ASSERTION(parent
&& parent
->GetType() == nsGkAtoms::popupSetFrame
,
1088 "Unexpected parent");
1091 id
= nsIFrame::kPopupList
;
1094 NS_ASSERTION(aChildFrame
->IsFloating(), "not a floated frame");
1095 id
= nsIFrame::kFloatList
;
1099 nsIAtom
* childType
= aChildFrame
->GetType();
1100 if (nsGkAtoms::menuPopupFrame
== childType
) {
1101 nsIFrame
* parent
= aChildFrame
->GetParent();
1102 MOZ_ASSERT(parent
, "nsMenuPopupFrame can't be the root frame");
1104 if (parent
->GetType() == nsGkAtoms::popupSetFrame
) {
1105 id
= nsIFrame::kPopupList
;
1107 nsIFrame
* firstPopup
= parent
->GetFirstChild(nsIFrame::kPopupList
);
1108 MOZ_ASSERT(!firstPopup
|| !firstPopup
->GetNextSibling(),
1109 "We assume popupList only has one child, but it has more.");
1110 id
= firstPopup
== aChildFrame
1111 ? nsIFrame::kPopupList
1112 : nsIFrame::kPrincipalList
;
1115 id
= nsIFrame::kPrincipalList
;
1117 } else if (nsGkAtoms::tableColGroupFrame
== childType
) {
1118 id
= nsIFrame::kColGroupList
;
1119 } else if (nsGkAtoms::tableCaptionFrame
== childType
) {
1120 id
= nsIFrame::kCaptionList
;
1122 id
= nsIFrame::kPrincipalList
;
1127 // Verify that the frame is actually in that child list or in the
1128 // corresponding overflow list.
1129 nsContainerFrame
* parent
= aChildFrame
->GetParent();
1130 bool found
= parent
->GetChildList(id
).ContainsFrame(aChildFrame
);
1132 if (!(aChildFrame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
)) {
1133 found
= parent
->GetChildList(nsIFrame::kOverflowList
)
1134 .ContainsFrame(aChildFrame
);
1136 else if (aChildFrame
->IsFloating()) {
1137 found
= parent
->GetChildList(nsIFrame::kOverflowOutOfFlowList
)
1138 .ContainsFrame(aChildFrame
);
1140 found
= parent
->GetChildList(nsIFrame::kPushedFloatsList
)
1141 .ContainsFrame(aChildFrame
);
1144 // else it's positioned and should have been on the 'id' child list.
1145 NS_POSTCONDITION(found
, "not in child list");
1154 nsLayoutUtils::GetBeforeFrame(nsIFrame
* aFrame
)
1156 NS_PRECONDITION(aFrame
, "NULL frame pointer");
1157 NS_ASSERTION(!aFrame
->GetPrevContinuation(),
1158 "aFrame must be first continuation");
1160 nsIFrame
* cif
= aFrame
->GetContentInsertionFrame();
1164 nsIFrame
* firstFrame
= GetFirstChildFrame(cif
, aFrame
->GetContent());
1165 if (firstFrame
&& IsGeneratedContentFor(nullptr, firstFrame
,
1166 nsCSSPseudoElements::before
)) {
1175 nsLayoutUtils::GetAfterFrame(nsIFrame
* aFrame
)
1177 NS_PRECONDITION(aFrame
, "NULL frame pointer");
1179 nsContainerFrame
* cif
= aFrame
->GetContentInsertionFrame();
1183 nsIFrame
* lastFrame
= GetLastChildFrame(cif
, aFrame
->GetContent());
1184 if (lastFrame
&& IsGeneratedContentFor(nullptr, lastFrame
,
1185 nsCSSPseudoElements::after
)) {
1194 nsLayoutUtils::GetClosestFrameOfType(nsIFrame
* aFrame
, nsIAtom
* aFrameType
)
1196 for (nsIFrame
* frame
= aFrame
; frame
; frame
= frame
->GetParent()) {
1197 if (frame
->GetType() == aFrameType
) {
1206 nsLayoutUtils::GetStyleFrame(nsIFrame
* aFrame
)
1208 if (aFrame
->GetType() == nsGkAtoms::tableOuterFrame
) {
1209 nsIFrame
* inner
= aFrame
->GetFirstPrincipalChild();
1210 NS_ASSERTION(inner
, "Outer table must have an inner");
1218 nsLayoutUtils::GetStyleFrame(const nsIContent
* aContent
)
1220 nsIFrame
*frame
= aContent
->GetPrimaryFrame();
1225 return nsLayoutUtils::GetStyleFrame(frame
);
1229 nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame
* aFrame
) {
1230 NS_ASSERTION(nsGkAtoms::placeholderFrame
== aFrame
->GetType(),
1231 "Must have a placeholder here");
1232 if (aFrame
->GetStateBits() & PLACEHOLDER_FOR_FLOAT
) {
1233 nsIFrame
*outOfFlowFrame
=
1234 nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame
);
1235 NS_ASSERTION(outOfFlowFrame
->IsFloating(),
1236 "How did that happen?");
1237 return outOfFlowFrame
;
1245 nsLayoutUtils::IsGeneratedContentFor(nsIContent
* aContent
,
1247 nsIAtom
* aPseudoElement
)
1249 NS_PRECONDITION(aFrame
, "Must have a frame");
1250 NS_PRECONDITION(aPseudoElement
, "Must have a pseudo name");
1252 if (!aFrame
->IsGeneratedContentFrame()) {
1255 nsIFrame
* parent
= aFrame
->GetParent();
1256 NS_ASSERTION(parent
, "Generated content can't be root frame");
1257 if (parent
->IsGeneratedContentFrame()) {
1258 // Not the root of the generated content
1262 if (aContent
&& parent
->GetContent() != aContent
) {
1266 return (aFrame
->GetContent()->Tag() == nsGkAtoms::mozgeneratedcontentbefore
) ==
1267 (aPseudoElement
== nsCSSPseudoElements::before
);
1272 nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame
* aFrame
,
1273 nsPoint
* aExtraOffset
)
1275 nsIFrame
* p
= aFrame
->GetParent();
1279 nsView
* v
= aFrame
->GetView();
1282 v
= v
->GetParent(); // anonymous inner view
1286 *aExtraOffset
+= v
->GetPosition();
1288 v
= v
->GetParent(); // subdocumentframe's view
1289 return v
? v
->GetFrame() : nullptr;
1294 nsLayoutUtils::IsProperAncestorFrameCrossDoc(nsIFrame
* aAncestorFrame
, nsIFrame
* aFrame
,
1295 nsIFrame
* aCommonAncestor
)
1297 if (aFrame
== aAncestorFrame
)
1299 return IsAncestorFrameCrossDoc(aAncestorFrame
, aFrame
, aCommonAncestor
);
1304 nsLayoutUtils::IsAncestorFrameCrossDoc(const nsIFrame
* aAncestorFrame
, const nsIFrame
* aFrame
,
1305 const nsIFrame
* aCommonAncestor
)
1307 for (const nsIFrame
* f
= aFrame
; f
!= aCommonAncestor
;
1308 f
= GetCrossDocParentFrame(f
)) {
1309 if (f
== aAncestorFrame
)
1312 return aCommonAncestor
== aAncestorFrame
;
1317 nsLayoutUtils::IsProperAncestorFrame(nsIFrame
* aAncestorFrame
, nsIFrame
* aFrame
,
1318 nsIFrame
* aCommonAncestor
)
1320 if (aFrame
== aAncestorFrame
)
1322 for (nsIFrame
* f
= aFrame
; f
!= aCommonAncestor
; f
= f
->GetParent()) {
1323 if (f
== aAncestorFrame
)
1326 return aCommonAncestor
== aAncestorFrame
;
1331 nsLayoutUtils::DoCompareTreePosition(nsIContent
* aContent1
,
1332 nsIContent
* aContent2
,
1333 int32_t aIf1Ancestor
,
1334 int32_t aIf2Ancestor
,
1335 const nsIContent
* aCommonAncestor
)
1337 NS_PRECONDITION(aContent1
, "aContent1 must not be null");
1338 NS_PRECONDITION(aContent2
, "aContent2 must not be null");
1340 nsAutoTArray
<nsINode
*, 32> content1Ancestors
;
1342 for (c1
= aContent1
; c1
&& c1
!= aCommonAncestor
; c1
= c1
->GetParentNode()) {
1343 content1Ancestors
.AppendElement(c1
);
1345 if (!c1
&& aCommonAncestor
) {
1346 // So, it turns out aCommonAncestor was not an ancestor of c1. Oops.
1347 // Never mind. We can continue as if aCommonAncestor was null.
1348 aCommonAncestor
= nullptr;
1351 nsAutoTArray
<nsINode
*, 32> content2Ancestors
;
1353 for (c2
= aContent2
; c2
&& c2
!= aCommonAncestor
; c2
= c2
->GetParentNode()) {
1354 content2Ancestors
.AppendElement(c2
);
1356 if (!c2
&& aCommonAncestor
) {
1357 // So, it turns out aCommonAncestor was not an ancestor of c2.
1358 // We need to retry with no common ancestor hint.
1359 return DoCompareTreePosition(aContent1
, aContent2
,
1360 aIf1Ancestor
, aIf2Ancestor
, nullptr);
1363 int last1
= content1Ancestors
.Length() - 1;
1364 int last2
= content2Ancestors
.Length() - 1;
1365 nsINode
* content1Ancestor
= nullptr;
1366 nsINode
* content2Ancestor
= nullptr;
1367 while (last1
>= 0 && last2
>= 0
1368 && ((content1Ancestor
= content1Ancestors
.ElementAt(last1
)) ==
1369 (content2Ancestor
= content2Ancestors
.ElementAt(last2
)))) {
1376 NS_ASSERTION(aContent1
== aContent2
, "internal error?");
1379 // aContent1 is an ancestor of aContent2
1380 return aIf1Ancestor
;
1384 // aContent2 is an ancestor of aContent1
1385 return aIf2Ancestor
;
1388 // content1Ancestor != content2Ancestor, so they must be siblings with the same parent
1389 nsINode
* parent
= content1Ancestor
->GetParentNode();
1391 // TODO: remove the uglyness, see bug 598468.
1392 NS_ASSERTION(gPreventAssertInCompareTreePosition
|| parent
,
1393 "no common ancestor at all???");
1395 if (!parent
) { // different documents??
1399 int32_t index1
= parent
->IndexOf(content1Ancestor
);
1400 int32_t index2
= parent
->IndexOf(content2Ancestor
);
1401 if (index1
< 0 || index2
< 0) {
1402 // one of them must be anonymous; we can't determine the order
1406 return index1
- index2
;
1411 nsLayoutUtils::FillAncestors(nsIFrame
* aFrame
,
1412 nsIFrame
* aStopAtAncestor
,
1413 nsTArray
<nsIFrame
*>* aAncestors
)
1415 while (aFrame
&& aFrame
!= aStopAtAncestor
) {
1416 aAncestors
->AppendElement(aFrame
);
1417 aFrame
= nsLayoutUtils::GetParentOrPlaceholderFor(aFrame
);
1422 // Return true if aFrame1 is after aFrame2
1423 static bool IsFrameAfter(nsIFrame
* aFrame1
, nsIFrame
* aFrame2
)
1425 nsIFrame
* f
= aFrame2
;
1427 f
= f
->GetNextSibling();
1436 nsLayoutUtils::DoCompareTreePosition(nsIFrame
* aFrame1
,
1438 int32_t aIf1Ancestor
,
1439 int32_t aIf2Ancestor
,
1440 nsIFrame
* aCommonAncestor
)
1442 NS_PRECONDITION(aFrame1
, "aFrame1 must not be null");
1443 NS_PRECONDITION(aFrame2
, "aFrame2 must not be null");
1445 nsAutoTArray
<nsIFrame
*,20> frame2Ancestors
;
1446 nsIFrame
* nonCommonAncestor
=
1447 FillAncestors(aFrame2
, aCommonAncestor
, &frame2Ancestors
);
1449 return DoCompareTreePosition(aFrame1
, aFrame2
, frame2Ancestors
,
1450 aIf1Ancestor
, aIf2Ancestor
,
1451 nonCommonAncestor
? aCommonAncestor
: nullptr);
1456 nsLayoutUtils::DoCompareTreePosition(nsIFrame
* aFrame1
,
1458 nsTArray
<nsIFrame
*>& aFrame2Ancestors
,
1459 int32_t aIf1Ancestor
,
1460 int32_t aIf2Ancestor
,
1461 nsIFrame
* aCommonAncestor
)
1463 NS_PRECONDITION(aFrame1
, "aFrame1 must not be null");
1464 NS_PRECONDITION(aFrame2
, "aFrame2 must not be null");
1466 nsPresContext
* presContext
= aFrame1
->PresContext();
1467 if (presContext
!= aFrame2
->PresContext()) {
1468 NS_ERROR("no common ancestor at all, different documents");
1472 nsAutoTArray
<nsIFrame
*,20> frame1Ancestors
;
1473 if (aCommonAncestor
&&
1474 !FillAncestors(aFrame1
, aCommonAncestor
, &frame1Ancestors
)) {
1475 // We reached the root of the frame tree ... if aCommonAncestor was set,
1477 return DoCompareTreePosition(aFrame1
, aFrame2
,
1478 aIf1Ancestor
, aIf2Ancestor
, nullptr);
1481 int32_t last1
= int32_t(frame1Ancestors
.Length()) - 1;
1482 int32_t last2
= int32_t(aFrame2Ancestors
.Length()) - 1;
1483 while (last1
>= 0 && last2
>= 0 &&
1484 frame1Ancestors
[last1
] == aFrame2Ancestors
[last2
]) {
1491 NS_ASSERTION(aFrame1
== aFrame2
, "internal error?");
1494 // aFrame1 is an ancestor of aFrame2
1495 return aIf1Ancestor
;
1499 // aFrame2 is an ancestor of aFrame1
1500 return aIf2Ancestor
;
1503 nsIFrame
* ancestor1
= frame1Ancestors
[last1
];
1504 nsIFrame
* ancestor2
= aFrame2Ancestors
[last2
];
1505 // Now we should be able to walk sibling chains to find which one is first
1506 if (IsFrameAfter(ancestor2
, ancestor1
))
1508 if (IsFrameAfter(ancestor1
, ancestor2
))
1510 NS_WARNING("Frames were in different child lists???");
1515 nsIFrame
* nsLayoutUtils::GetLastSibling(nsIFrame
* aFrame
) {
1521 while ((next
= aFrame
->GetNextSibling()) != nullptr) {
1529 nsLayoutUtils::FindSiblingViewFor(nsView
* aParentView
, nsIFrame
* aFrame
) {
1530 nsIFrame
* parentViewFrame
= aParentView
->GetFrame();
1531 nsIContent
* parentViewContent
= parentViewFrame
? parentViewFrame
->GetContent() : nullptr;
1532 for (nsView
* insertBefore
= aParentView
->GetFirstChild(); insertBefore
;
1533 insertBefore
= insertBefore
->GetNextSibling()) {
1534 nsIFrame
* f
= insertBefore
->GetFrame();
1536 // this view could be some anonymous view attached to a meaningful parent
1537 for (nsView
* searchView
= insertBefore
->GetParent(); searchView
;
1538 searchView
= searchView
->GetParent()) {
1539 f
= searchView
->GetFrame();
1544 NS_ASSERTION(f
, "Can't find a frame anywhere!");
1546 if (!f
|| !aFrame
->GetContent() || !f
->GetContent() ||
1547 CompareTreePosition(aFrame
->GetContent(), f
->GetContent(), parentViewContent
) > 0) {
1548 // aFrame's content is after f's content (or we just don't know),
1549 // so put our view before f's view
1550 return insertBefore
;
1558 nsLayoutUtils::GetScrollableFrameFor(const nsIFrame
*aScrolledFrame
)
1560 nsIFrame
*frame
= aScrolledFrame
->GetParent();
1561 nsIScrollableFrame
*sf
= do_QueryFrame(frame
);
1562 return (sf
&& sf
->GetScrolledFrame() == aScrolledFrame
) ? sf
: nullptr;
1566 nsLayoutUtils::SetFixedPositionLayerData(Layer
* aLayer
,
1567 const nsIFrame
* aViewportFrame
,
1568 const nsRect
& aAnchorRect
,
1569 const nsIFrame
* aFixedPosFrame
,
1570 nsPresContext
* aPresContext
,
1571 const ContainerLayerParameters
& aContainerParameters
) {
1572 // Find out the rect of the viewport frame relative to the reference frame.
1573 // This, in conjunction with the container scale, will correspond to the
1574 // coordinate-space of the built layer.
1575 float factor
= aPresContext
->AppUnitsPerDevPixel();
1576 Rect
anchorRect(NSAppUnitsToFloatPixels(aAnchorRect
.x
, factor
) *
1577 aContainerParameters
.mXScale
,
1578 NSAppUnitsToFloatPixels(aAnchorRect
.y
, factor
) *
1579 aContainerParameters
.mYScale
,
1580 NSAppUnitsToFloatPixels(aAnchorRect
.width
, factor
) *
1581 aContainerParameters
.mXScale
,
1582 NSAppUnitsToFloatPixels(aAnchorRect
.height
, factor
) *
1583 aContainerParameters
.mYScale
);
1584 // Need to transform anchorRect from the container layer's coordinate system
1585 // into aLayer's coordinate system.
1587 if (aLayer
->GetTransform().Is2D(&transform2d
)) {
1588 transform2d
.Invert();
1589 anchorRect
= transform2d
.TransformBounds(anchorRect
);
1591 NS_ERROR("3D transform found between fixedpos content and its viewport (should never happen)");
1592 anchorRect
= Rect(0,0,0,0);
1595 // Work out the anchor point for this fixed position layer. We assume that
1596 // any positioning set (left/top/right/bottom) indicates that the
1597 // corresponding side of its container should be the anchor point,
1598 // defaulting to top-left.
1599 LayerPoint
anchor(anchorRect
.x
, anchorRect
.y
);
1600 // Make sure the layer is aware of any fixed position margins that have
1602 nsMargin fixedMargins
= aPresContext
->PresShell()->GetContentDocumentFixedPositionMargins();
1603 LayerMargin
fixedLayerMargins(NSAppUnitsToFloatPixels(fixedMargins
.top
, factor
) *
1604 aContainerParameters
.mYScale
,
1605 NSAppUnitsToFloatPixels(fixedMargins
.right
, factor
) *
1606 aContainerParameters
.mXScale
,
1607 NSAppUnitsToFloatPixels(fixedMargins
.bottom
, factor
) *
1608 aContainerParameters
.mYScale
,
1609 NSAppUnitsToFloatPixels(fixedMargins
.left
, factor
) *
1610 aContainerParameters
.mXScale
);
1612 if (aFixedPosFrame
!= aViewportFrame
) {
1613 const nsStylePosition
* position
= aFixedPosFrame
->StylePosition();
1614 if (position
->mOffset
.GetRightUnit() != eStyleUnit_Auto
) {
1615 if (position
->mOffset
.GetLeftUnit() != eStyleUnit_Auto
) {
1616 anchor
.x
= anchorRect
.x
+ anchorRect
.width
/ 2.f
;
1618 anchor
.x
= anchorRect
.XMost();
1621 if (position
->mOffset
.GetBottomUnit() != eStyleUnit_Auto
) {
1622 if (position
->mOffset
.GetTopUnit() != eStyleUnit_Auto
) {
1623 anchor
.y
= anchorRect
.y
+ anchorRect
.height
/ 2.f
;
1625 anchor
.y
= anchorRect
.YMost();
1629 // If the frame is auto-positioned on either axis, set the top/left layer
1630 // margins to -1, to indicate to the compositor that this layer is
1631 // unaffected by fixed margins.
1632 if (position
->mOffset
.GetLeftUnit() == eStyleUnit_Auto
&&
1633 position
->mOffset
.GetRightUnit() == eStyleUnit_Auto
) {
1634 fixedLayerMargins
.left
= -1;
1636 if (position
->mOffset
.GetTopUnit() == eStyleUnit_Auto
&&
1637 position
->mOffset
.GetBottomUnit() == eStyleUnit_Auto
) {
1638 fixedLayerMargins
.top
= -1;
1642 aLayer
->SetFixedPositionAnchor(anchor
);
1643 aLayer
->SetFixedPositionMargins(fixedLayerMargins
);
1647 nsLayoutUtils::ViewportHasDisplayPort(nsPresContext
* aPresContext
, nsRect
* aDisplayPort
)
1649 nsIFrame
* rootScrollFrame
=
1650 aPresContext
->PresShell()->GetRootScrollFrame();
1651 return rootScrollFrame
&&
1652 nsLayoutUtils::GetDisplayPort(rootScrollFrame
->GetContent(), aDisplayPort
);
1656 nsLayoutUtils::IsFixedPosFrameInDisplayPort(const nsIFrame
* aFrame
, nsRect
* aDisplayPort
)
1658 // Fixed-pos frames are parented by the viewport frame or the page content frame.
1659 // We'll assume that printing/print preview don't have displayports for their
1661 nsIFrame
* parent
= aFrame
->GetParent();
1662 if (!parent
|| parent
->GetParent() ||
1663 aFrame
->StyleDisplay()->mPosition
!= NS_STYLE_POSITION_FIXED
) {
1666 return ViewportHasDisplayPort(aFrame
->PresContext(), aDisplayPort
);
1669 NS_DECLARE_FRAME_PROPERTY(ScrollbarThumbLayerized
, nullptr)
1672 nsLayoutUtils::SetScrollbarThumbLayerization(nsIFrame
* aThumbFrame
, bool aLayerize
)
1674 aThumbFrame
->Properties().Set(ScrollbarThumbLayerized(),
1675 reinterpret_cast<void*>(intptr_t(aLayerize
)));
1679 IsScrollbarThumbLayerized(nsIFrame
* aThumbFrame
)
1681 return reinterpret_cast<intptr_t>(aThumbFrame
->Properties().Get(ScrollbarThumbLayerized()));
1685 nsLayoutUtils::GetAnimatedGeometryRootForFrame(nsIFrame
* aFrame
,
1686 const nsIFrame
* aStopAtAncestor
)
1688 nsIFrame
* f
= aFrame
;
1689 nsIFrame
* stickyFrame
= nullptr;
1690 while (f
!= aStopAtAncestor
) {
1691 if (nsLayoutUtils::IsPopup(f
))
1693 if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(f
))
1695 if (!f
->GetParent() &&
1696 nsLayoutUtils::ViewportHasDisplayPort(f
->PresContext())) {
1697 // Viewport frames in a display port need to be animated geometry roots
1698 // for background-attachment:fixed elements.
1701 nsIFrame
* parent
= nsLayoutUtils::GetCrossDocParentFrame(f
);
1704 nsIAtom
* parentType
= parent
->GetType();
1705 // Treat the slider thumb as being as an active scrolled root when it wants
1706 // its own layer so that it can move without repainting.
1707 if (parentType
== nsGkAtoms::sliderFrame
&& IsScrollbarThumbLayerized(f
)) {
1710 // Sticky frames are active if their nearest scrollable frame
1711 // is also active, just keep a record of sticky frames that we
1712 // encounter for now.
1713 if (f
->StyleDisplay()->mPosition
== NS_STYLE_POSITION_STICKY
&&
1717 if (parentType
== nsGkAtoms::scrollFrame
) {
1718 nsIScrollableFrame
* sf
= do_QueryFrame(parent
);
1719 if (sf
->IsScrollingActive() && sf
->GetScrolledFrame() == f
) {
1720 // If we found a sticky frame inside this active scroll frame,
1721 // then use that. Otherwise use the scroll frame.
1727 stickyFrame
= nullptr;
1730 // Fixed-pos frames are parented by the viewport frame, which has no parent
1731 if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f
)) {
1740 nsLayoutUtils::GetAnimatedGeometryRootFor(nsDisplayItem
* aItem
,
1741 nsDisplayListBuilder
* aBuilder
,
1742 LayerManager
* aManager
)
1744 nsIFrame
* f
= aItem
->Frame();
1745 if (aItem
->GetType() == nsDisplayItem::TYPE_SCROLL_LAYER
) {
1746 nsDisplayScrollLayer
* scrollLayerItem
=
1747 static_cast<nsDisplayScrollLayer
*>(aItem
);
1748 nsIFrame
* scrolledFrame
= scrollLayerItem
->GetScrolledFrame();
1749 return GetAnimatedGeometryRootForFrame(scrolledFrame
,
1750 aBuilder
->FindReferenceFrameFor(scrolledFrame
));
1752 if (aItem
->ShouldFixToViewport(aManager
)) {
1753 // Make its active scrolled root be the active scrolled root of
1754 // the enclosing viewport, since it shouldn't be scrolled by scrolled
1755 // frames in its document. InvalidateFixedBackgroundFramesFromList in
1756 // nsGfxScrollFrame will not repaint this item when scrolling occurs.
1757 nsIFrame
* viewportFrame
=
1758 nsLayoutUtils::GetClosestFrameOfType(f
, nsGkAtoms::viewportFrame
);
1759 NS_ASSERTION(viewportFrame
, "no viewport???");
1760 return GetAnimatedGeometryRootForFrame(viewportFrame
,
1761 aBuilder
->FindReferenceFrameFor(viewportFrame
));
1763 return GetAnimatedGeometryRootForFrame(f
, aItem
->ReferenceFrame());
1768 nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame
* aFrame
,
1769 Direction aDirection
)
1771 NS_ASSERTION(aFrame
, "GetNearestScrollableFrameForDirection expects a non-null frame");
1772 for (nsIFrame
* f
= aFrame
; f
; f
= nsLayoutUtils::GetCrossDocParentFrame(f
)) {
1773 nsIScrollableFrame
* scrollableFrame
= do_QueryFrame(f
);
1774 if (scrollableFrame
) {
1775 ScrollbarStyles ss
= scrollableFrame
->GetScrollbarStyles();
1776 uint32_t directions
= scrollableFrame
->GetPerceivedScrollingDirections();
1777 if (aDirection
== eVertical
?
1778 (ss
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
&&
1779 (directions
& nsIScrollableFrame::VERTICAL
)) :
1780 (ss
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
&&
1781 (directions
& nsIScrollableFrame::HORIZONTAL
)))
1782 return scrollableFrame
;
1790 nsLayoutUtils::GetNearestScrollableFrame(nsIFrame
* aFrame
, uint32_t aFlags
)
1792 NS_ASSERTION(aFrame
, "GetNearestScrollableFrame expects a non-null frame");
1793 for (nsIFrame
* f
= aFrame
; f
; f
= (aFlags
& SCROLLABLE_SAME_DOC
) ?
1794 f
->GetParent() : nsLayoutUtils::GetCrossDocParentFrame(f
)) {
1795 nsIScrollableFrame
* scrollableFrame
= do_QueryFrame(f
);
1796 if (scrollableFrame
) {
1797 ScrollbarStyles ss
= scrollableFrame
->GetScrollbarStyles();
1798 if ((aFlags
& SCROLLABLE_INCLUDE_HIDDEN
) ||
1799 ss
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
||
1800 ss
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
)
1801 return scrollableFrame
;
1809 nsLayoutUtils::GetScrolledRect(nsIFrame
* aScrolledFrame
,
1810 const nsRect
& aScrolledFrameOverflowArea
,
1811 const nsSize
& aScrollPortSize
,
1814 nscoord x1
= aScrolledFrameOverflowArea
.x
,
1815 x2
= aScrolledFrameOverflowArea
.XMost(),
1816 y1
= aScrolledFrameOverflowArea
.y
,
1817 y2
= aScrolledFrameOverflowArea
.YMost();
1821 if (aDirection
!= NS_STYLE_DIRECTION_RTL
) {
1826 if (x2
> aScrollPortSize
.width
) {
1827 x2
= aScrollPortSize
.width
;
1829 // When the scrolled frame chooses a size larger than its available width (because
1830 // its padding alone is larger than the available width), we need to keep the
1831 // start-edge of the scroll frame anchored to the start-edge of the scrollport.
1832 // When the scrolled frame is RTL, this means moving it in our left-based
1833 // coordinate system, so we need to compensate for its extra width here by
1834 // effectively repositioning the frame.
1835 nscoord extraWidth
= std::max(0, aScrolledFrame
->GetSize().width
- aScrollPortSize
.width
);
1838 return nsRect(x1
, y1
, x2
- x1
, y2
- y1
);
1843 nsLayoutUtils::HasPseudoStyle(nsIContent
* aContent
,
1844 nsStyleContext
* aStyleContext
,
1845 nsCSSPseudoElements::Type aPseudoElement
,
1846 nsPresContext
* aPresContext
)
1848 NS_PRECONDITION(aPresContext
, "Must have a prescontext");
1850 nsRefPtr
<nsStyleContext
> pseudoContext
;
1852 pseudoContext
= aPresContext
->StyleSet()->
1853 ProbePseudoElementStyle(aContent
->AsElement(), aPseudoElement
,
1856 return pseudoContext
!= nullptr;
1860 nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(nsIDOMEvent
* aDOMEvent
, nsIFrame
* aFrame
)
1863 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1864 WidgetEvent
* event
= aDOMEvent
->GetInternalNSEvent();
1866 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1867 return GetEventCoordinatesRelativeTo(event
, aFrame
);
1871 nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent
* aEvent
,
1874 if (!aEvent
|| (aEvent
->mClass
!= eMouseEventClass
&&
1875 aEvent
->mClass
!= eMouseScrollEventClass
&&
1876 aEvent
->mClass
!= eWheelEventClass
&&
1877 aEvent
->mClass
!= eDragEventClass
&&
1878 aEvent
->mClass
!= eSimpleGestureEventClass
&&
1879 aEvent
->mClass
!= ePointerEventClass
&&
1880 aEvent
->mClass
!= eGestureNotifyEventClass
&&
1881 aEvent
->mClass
!= eTouchEventClass
&&
1882 aEvent
->mClass
!= eQueryContentEventClass
))
1883 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1885 return GetEventCoordinatesRelativeTo(aEvent
,
1886 LayoutDeviceIntPoint::ToUntyped(aEvent
->AsGUIEvent()->refPoint
),
1891 nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent
* aEvent
,
1892 const nsIntPoint aPoint
,
1896 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1899 nsIWidget
* widget
= aEvent
->AsGUIEvent()->widget
;
1901 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1904 return GetEventCoordinatesRelativeTo(widget
, aPoint
, aFrame
);
1908 nsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget
* aWidget
,
1909 const nsIntPoint aPoint
,
1912 if (!aFrame
|| !aWidget
) {
1913 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1916 nsView
* view
= aFrame
->GetView();
1918 nsIWidget
* frameWidget
= view
->GetWidget();
1919 if (frameWidget
&& frameWidget
== aWidget
) {
1920 // Special case this cause it happens a lot.
1921 // This also fixes bug 664707, events in the extra-special case of select
1922 // dropdown popups that are transformed.
1923 nsPresContext
* presContext
= aFrame
->PresContext();
1924 nsPoint
pt(presContext
->DevPixelsToAppUnits(aPoint
.x
),
1925 presContext
->DevPixelsToAppUnits(aPoint
.y
));
1926 return pt
- view
->ViewToWidgetOffset();
1930 /* If we walk up the frame tree and discover that any of the frames are
1931 * transformed, we need to do extra work to convert from the global
1932 * space to the local space.
1934 nsIFrame
* rootFrame
= aFrame
;
1935 bool transformFound
= false;
1936 for (nsIFrame
* f
= aFrame
; f
; f
= GetCrossDocParentFrame(f
)) {
1937 if (f
->IsTransformed()) {
1938 transformFound
= true;
1944 nsView
* rootView
= rootFrame
->GetView();
1946 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1949 nsPoint widgetToView
= TranslateWidgetToView(rootFrame
->PresContext(),
1950 aWidget
, aPoint
, rootView
);
1952 if (widgetToView
== nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
)) {
1953 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1956 // Convert from root document app units to app units of the document aFrame
1958 int32_t rootAPD
= rootFrame
->PresContext()->AppUnitsPerDevPixel();
1959 int32_t localAPD
= aFrame
->PresContext()->AppUnitsPerDevPixel();
1960 widgetToView
= widgetToView
.ConvertAppUnits(rootAPD
, localAPD
);
1962 /* If we encountered a transform, we can't do simple arithmetic to figure
1963 * out how to convert back to aFrame's coordinates and must use the CTM.
1965 if (transformFound
|| aFrame
->IsSVGText()) {
1966 return TransformRootPointToFrame(aFrame
, widgetToView
);
1969 /* Otherwise, all coordinate systems are translations of one another,
1970 * so we can just subtract out the difference.
1972 return widgetToView
- aFrame
->GetOffsetToCrossDoc(rootFrame
);
1976 nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext
* aPresContext
,
1977 const WidgetEvent
* aEvent
)
1980 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
1984 nsTArray
<nsIFrame
*> popups
;
1985 pm
->GetVisiblePopups(popups
);
1987 // Search from top to bottom
1988 for (i
= 0; i
< popups
.Length(); i
++) {
1989 nsIFrame
* popup
= popups
[i
];
1990 if (popup
->PresContext()->GetRootPresContext() == aPresContext
&&
1991 popup
->GetScrollableOverflowRect().Contains(
1992 GetEventCoordinatesRelativeTo(aEvent
, popup
))) {
2000 static void ConstrainToCoordValues(float& aStart
, float& aSize
)
2002 MOZ_ASSERT(aSize
>= 0);
2004 // Here we try to make sure that the resulting nsRect will continue to cover
2005 // as much of the area that was covered by the original gfx Rect as possible.
2007 // We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since
2008 // nsRect::X/Y() and nsRect::XMost/YMost() can't return values outwith this
2010 float end
= aStart
+ aSize
;
2011 aStart
= clamped(aStart
, float(nscoord_MIN
), float(nscoord_MAX
));
2012 end
= clamped(end
, float(nscoord_MIN
), float(nscoord_MAX
));
2014 aSize
= end
- aStart
;
2016 // We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height()
2017 // can't return a value greater than nscoord_MAX. If aSize is greater than
2018 // nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect
2020 if (aSize
> nscoord_MAX
) {
2021 float excess
= aSize
- nscoord_MAX
;
2024 aSize
= nscoord_MAX
;
2029 * Given a gfxFloat, constrains its value to be between nscoord_MIN and nscoord_MAX.
2031 * @param aVal The value to constrain (in/out)
2033 static void ConstrainToCoordValues(gfxFloat
& aVal
)
2035 if (aVal
<= nscoord_MIN
)
2037 else if (aVal
>= nscoord_MAX
)
2041 static void ConstrainToCoordValues(gfxFloat
& aStart
, gfxFloat
& aSize
)
2043 gfxFloat max
= aStart
+ aSize
;
2045 // Clamp the end points to within nscoord range
2046 ConstrainToCoordValues(aStart
);
2047 ConstrainToCoordValues(max
);
2049 aSize
= max
- aStart
;
2050 // If the width if still greater than the max nscoord, then bring both
2051 // endpoints in by the same amount until it fits.
2052 if (aSize
> nscoord_MAX
) {
2053 gfxFloat excess
= aSize
- nscoord_MAX
;
2057 aSize
= nscoord_MAX
;
2058 } else if (aSize
< nscoord_MIN
) {
2059 gfxFloat excess
= aSize
- nscoord_MIN
;
2063 aSize
= nscoord_MIN
;
2068 nsLayoutUtils::RoundGfxRectToAppRect(const Rect
&aRect
, float aFactor
)
2070 /* Get a new Rect whose units are app units by scaling by the specified factor. */
2071 Rect scaledRect
= aRect
;
2072 scaledRect
.ScaleRoundOut(aFactor
);
2074 /* We now need to constrain our results to the max and min values for coords. */
2075 ConstrainToCoordValues(scaledRect
.x
, scaledRect
.width
);
2076 ConstrainToCoordValues(scaledRect
.y
, scaledRect
.height
);
2078 /* Now typecast everything back. This is guaranteed to be safe. */
2079 return nsRect(nscoord(scaledRect
.X()), nscoord(scaledRect
.Y()),
2080 nscoord(scaledRect
.Width()), nscoord(scaledRect
.Height()));
2084 nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect
&aRect
, float aFactor
)
2086 /* Get a new gfxRect whose units are app units by scaling by the specified factor. */
2087 gfxRect scaledRect
= aRect
;
2088 scaledRect
.ScaleRoundOut(aFactor
);
2090 /* We now need to constrain our results to the max and min values for coords. */
2091 ConstrainToCoordValues(scaledRect
.x
, scaledRect
.width
);
2092 ConstrainToCoordValues(scaledRect
.y
, scaledRect
.height
);
2094 /* Now typecast everything back. This is guaranteed to be safe. */
2095 return nsRect(nscoord(scaledRect
.X()), nscoord(scaledRect
.Y()),
2096 nscoord(scaledRect
.Width()), nscoord(scaledRect
.Height()));
2101 nsLayoutUtils::RoundedRectIntersectRect(const nsRect
& aRoundedRect
,
2102 const nscoord aRadii
[8],
2103 const nsRect
& aContainedRect
)
2105 // rectFullHeight and rectFullWidth together will approximately contain
2106 // the total area of the frame minus the rounded corners.
2107 nsRect rectFullHeight
= aRoundedRect
;
2108 nscoord xDiff
= std::max(aRadii
[NS_CORNER_TOP_LEFT_X
], aRadii
[NS_CORNER_BOTTOM_LEFT_X
]);
2109 rectFullHeight
.x
+= xDiff
;
2110 rectFullHeight
.width
-= std::max(aRadii
[NS_CORNER_TOP_RIGHT_X
],
2111 aRadii
[NS_CORNER_BOTTOM_RIGHT_X
]) + xDiff
;
2113 r1
.IntersectRect(rectFullHeight
, aContainedRect
);
2115 nsRect rectFullWidth
= aRoundedRect
;
2116 nscoord yDiff
= std::max(aRadii
[NS_CORNER_TOP_LEFT_Y
], aRadii
[NS_CORNER_TOP_RIGHT_Y
]);
2117 rectFullWidth
.y
+= yDiff
;
2118 rectFullWidth
.height
-= std::max(aRadii
[NS_CORNER_BOTTOM_LEFT_Y
],
2119 aRadii
[NS_CORNER_BOTTOM_RIGHT_Y
]) + yDiff
;
2121 r2
.IntersectRect(rectFullWidth
, aContainedRect
);
2129 nsLayoutUtils::RoundedRectIntersectIntRect(const nsIntRect
& aRoundedRect
,
2130 const gfxCornerSizes
& aCorners
,
2131 const nsIntRect
& aContainedRect
)
2133 // rectFullHeight and rectFullWidth together will approximately contain
2134 // the total area of the frame minus the rounded corners.
2135 nsIntRect rectFullHeight
= aRoundedRect
;
2136 uint32_t xDiff
= std::max(aCorners
.TopLeft().width
, aCorners
.BottomLeft().width
);
2137 rectFullHeight
.x
+= xDiff
;
2138 rectFullHeight
.width
-= std::max(aCorners
.TopRight().width
,
2139 aCorners
.BottomRight().width
) + xDiff
;
2141 r1
.IntersectRect(rectFullHeight
, aContainedRect
);
2143 nsIntRect rectFullWidth
= aRoundedRect
;
2144 uint32_t yDiff
= std::max(aCorners
.TopLeft().height
, aCorners
.TopRight().height
);
2145 rectFullWidth
.y
+= yDiff
;
2146 rectFullWidth
.height
-= std::max(aCorners
.BottomLeft().height
,
2147 aCorners
.BottomRight().height
) + yDiff
;
2149 r2
.IntersectRect(rectFullWidth
, aContainedRect
);
2156 // Helper for RoundedRectIntersectsRect.
2158 CheckCorner(nscoord aXOffset
, nscoord aYOffset
,
2159 nscoord aXRadius
, nscoord aYRadius
)
2161 NS_ABORT_IF_FALSE(aXOffset
> 0 && aYOffset
> 0,
2162 "must not pass nonpositives to CheckCorner");
2163 NS_ABORT_IF_FALSE(aXRadius
>= 0 && aYRadius
>= 0,
2164 "must not pass negatives to CheckCorner");
2166 // Avoid floating point math unless we're either (1) within the
2167 // quarter-ellipse area at the rounded corner or (2) outside the
2169 if (aXOffset
>= aXRadius
|| aYOffset
>= aYRadius
)
2172 // Convert coordinates to a unit circle with (0,0) as the center of
2173 // curvature, and see if we're inside the circle or outside.
2174 float scaledX
= float(aXRadius
- aXOffset
) / float(aXRadius
);
2175 float scaledY
= float(aYRadius
- aYOffset
) / float(aYRadius
);
2176 return scaledX
* scaledX
+ scaledY
* scaledY
< 1.0f
;
2180 nsLayoutUtils::RoundedRectIntersectsRect(const nsRect
& aRoundedRect
,
2181 const nscoord aRadii
[8],
2182 const nsRect
& aTestRect
)
2184 if (!aTestRect
.Intersects(aRoundedRect
))
2187 // distances from this edge of aRoundedRect to opposite edge of aTestRect,
2188 // which we know are positive due to the Intersects check above.
2190 insets
.top
= aTestRect
.YMost() - aRoundedRect
.y
;
2191 insets
.right
= aRoundedRect
.XMost() - aTestRect
.x
;
2192 insets
.bottom
= aRoundedRect
.YMost() - aTestRect
.y
;
2193 insets
.left
= aTestRect
.XMost() - aRoundedRect
.x
;
2195 // Check whether the bottom-right corner of aTestRect is inside the
2196 // top left corner of aBounds when rounded by aRadii, etc. If any
2197 // corner is not, then fail; otherwise succeed.
2198 return CheckCorner(insets
.left
, insets
.top
,
2199 aRadii
[NS_CORNER_TOP_LEFT_X
],
2200 aRadii
[NS_CORNER_TOP_LEFT_Y
]) &&
2201 CheckCorner(insets
.right
, insets
.top
,
2202 aRadii
[NS_CORNER_TOP_RIGHT_X
],
2203 aRadii
[NS_CORNER_TOP_RIGHT_Y
]) &&
2204 CheckCorner(insets
.right
, insets
.bottom
,
2205 aRadii
[NS_CORNER_BOTTOM_RIGHT_X
],
2206 aRadii
[NS_CORNER_BOTTOM_RIGHT_Y
]) &&
2207 CheckCorner(insets
.left
, insets
.bottom
,
2208 aRadii
[NS_CORNER_BOTTOM_LEFT_X
],
2209 aRadii
[NS_CORNER_BOTTOM_LEFT_Y
]);
2213 nsLayoutUtils::MatrixTransformRectOut(const nsRect
&aBounds
,
2214 const gfx3DMatrix
&aMatrix
, float aFactor
)
2216 nsRect outside
= aBounds
;
2217 outside
.ScaleRoundOut(1/aFactor
);
2218 gfxRect image
= aMatrix
.TransformBounds(gfxRect(outside
.x
,
2222 return RoundGfxRectToAppRect(image
, aFactor
);
2226 nsLayoutUtils::MatrixTransformRect(const nsRect
&aBounds
,
2227 const gfx3DMatrix
&aMatrix
, float aFactor
)
2229 gfxRect image
= aMatrix
.TransformBounds(gfxRect(NSAppUnitsToDoublePixels(aBounds
.x
, aFactor
),
2230 NSAppUnitsToDoublePixels(aBounds
.y
, aFactor
),
2231 NSAppUnitsToDoublePixels(aBounds
.width
, aFactor
),
2232 NSAppUnitsToDoublePixels(aBounds
.height
, aFactor
)));
2234 return RoundGfxRectToAppRect(image
, aFactor
);
2238 nsLayoutUtils::MatrixTransformPoint(const nsPoint
&aPoint
,
2239 const gfx3DMatrix
&aMatrix
, float aFactor
)
2241 gfxPoint image
= aMatrix
.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPoint
.x
, aFactor
),
2242 NSAppUnitsToFloatPixels(aPoint
.y
, aFactor
)));
2243 return nsPoint(NSFloatPixelsToAppUnits(float(image
.x
), aFactor
),
2244 NSFloatPixelsToAppUnits(float(image
.y
), aFactor
));
2248 nsLayoutUtils::GetTransformToAncestor(nsIFrame
*aFrame
, const nsIFrame
*aAncestor
)
2252 if (aFrame
== aAncestor
) {
2255 ctm
= aFrame
->GetTransformMatrix(aAncestor
, &parent
);
2256 while (parent
&& parent
!= aAncestor
) {
2257 if (!parent
->Preserves3DChildren()) {
2260 ctm
= ctm
* parent
->GetTransformMatrix(aAncestor
, &parent
);
2266 FindNearestCommonAncestorFrame(nsIFrame
* aFrame1
, nsIFrame
* aFrame2
)
2268 nsAutoTArray
<nsIFrame
*,100> ancestors1
;
2269 nsAutoTArray
<nsIFrame
*,100> ancestors2
;
2270 nsIFrame
* commonAncestor
= nullptr;
2271 if (aFrame1
->PresContext() == aFrame2
->PresContext()) {
2272 commonAncestor
= aFrame1
->PresContext()->PresShell()->GetRootFrame();
2274 for (nsIFrame
* f
= aFrame1
; f
!= commonAncestor
;
2275 f
= nsLayoutUtils::GetCrossDocParentFrame(f
)) {
2276 ancestors1
.AppendElement(f
);
2278 for (nsIFrame
* f
= aFrame2
; f
!= commonAncestor
;
2279 f
= nsLayoutUtils::GetCrossDocParentFrame(f
)) {
2280 ancestors2
.AppendElement(f
);
2282 uint32_t minLengths
= std::min(ancestors1
.Length(), ancestors2
.Length());
2283 for (uint32_t i
= 1; i
<= minLengths
; ++i
) {
2284 if (ancestors1
[ancestors1
.Length() - i
] == ancestors2
[ancestors2
.Length() - i
]) {
2285 commonAncestor
= ancestors1
[ancestors1
.Length() - i
];
2290 return commonAncestor
;
2293 nsLayoutUtils::TransformResult
2294 nsLayoutUtils::TransformPoints(nsIFrame
* aFromFrame
, nsIFrame
* aToFrame
,
2295 uint32_t aPointCount
, CSSPoint
* aPoints
)
2297 nsIFrame
* nearestCommonAncestor
= FindNearestCommonAncestorFrame(aFromFrame
, aToFrame
);
2298 if (!nearestCommonAncestor
) {
2299 return NO_COMMON_ANCESTOR
;
2301 Matrix4x4 downToDest
= GetTransformToAncestor(aToFrame
, nearestCommonAncestor
);
2302 if (downToDest
.IsSingular()) {
2303 return NONINVERTIBLE_TRANSFORM
;
2305 downToDest
.Invert();
2306 Matrix4x4 upToAncestor
= GetTransformToAncestor(aFromFrame
, nearestCommonAncestor
);
2307 CSSToLayoutDeviceScale
devPixelsPerCSSPixelFromFrame(
2308 double(nsPresContext::AppUnitsPerCSSPixel())/
2309 aFromFrame
->PresContext()->AppUnitsPerDevPixel());
2310 CSSToLayoutDeviceScale
devPixelsPerCSSPixelToFrame(
2311 double(nsPresContext::AppUnitsPerCSSPixel())/
2312 aToFrame
->PresContext()->AppUnitsPerDevPixel());
2313 for (uint32_t i
= 0; i
< aPointCount
; ++i
) {
2314 LayoutDevicePoint devPixels
= aPoints
[i
] * devPixelsPerCSSPixelFromFrame
;
2315 // What should the behaviour be if some of the points aren't invertible
2316 // and others are? Just assume all points are for now.
2317 Point toDevPixels
= downToDest
.ProjectPoint(
2318 (upToAncestor
* Point(devPixels
.x
, devPixels
.y
))).As2DPoint();
2319 // Divide here so that when the devPixelsPerCSSPixels are the same, we get the correct
2320 // answer instead of some inaccuracy multiplying a number by its reciprocal.
2321 aPoints
[i
] = LayoutDevicePoint(toDevPixels
.x
, toDevPixels
.y
) /
2322 devPixelsPerCSSPixelToFrame
;
2324 return TRANSFORM_SUCCEEDED
;
2327 nsLayoutUtils::TransformResult
2328 nsLayoutUtils::TransformPoint(nsIFrame
* aFromFrame
, nsIFrame
* aToFrame
,
2331 nsIFrame
* nearestCommonAncestor
= FindNearestCommonAncestorFrame(aFromFrame
, aToFrame
);
2332 if (!nearestCommonAncestor
) {
2333 return NO_COMMON_ANCESTOR
;
2335 Matrix4x4 downToDest
= GetTransformToAncestor(aToFrame
, nearestCommonAncestor
);
2336 if (downToDest
.IsSingular()) {
2337 return NONINVERTIBLE_TRANSFORM
;
2339 downToDest
.Invert();
2340 Matrix4x4 upToAncestor
= GetTransformToAncestor(aFromFrame
, nearestCommonAncestor
);
2342 float devPixelsPerAppUnitFromFrame
=
2343 1.0f
/ aFromFrame
->PresContext()->AppUnitsPerDevPixel();
2344 float devPixelsPerAppUnitToFrame
=
2345 1.0f
/ aToFrame
->PresContext()->AppUnitsPerDevPixel();
2346 Point4D toDevPixels
= downToDest
.ProjectPoint(
2347 upToAncestor
* Point(aPoint
.x
* devPixelsPerAppUnitFromFrame
,
2348 aPoint
.y
* devPixelsPerAppUnitFromFrame
));
2349 if (!toDevPixels
.HasPositiveWCoord()) {
2350 // Not strictly true, but we failed to get a valid point in this
2351 // coordinate space.
2352 return NONINVERTIBLE_TRANSFORM
;
2354 aPoint
.x
= toDevPixels
.x
/ devPixelsPerAppUnitToFrame
;
2355 aPoint
.y
= toDevPixels
.y
/ devPixelsPerAppUnitToFrame
;
2356 return TRANSFORM_SUCCEEDED
;
2359 nsLayoutUtils::TransformResult
2360 nsLayoutUtils::TransformRect(nsIFrame
* aFromFrame
, nsIFrame
* aToFrame
,
2363 nsIFrame
* nearestCommonAncestor
= FindNearestCommonAncestorFrame(aFromFrame
, aToFrame
);
2364 if (!nearestCommonAncestor
) {
2365 return NO_COMMON_ANCESTOR
;
2367 Matrix4x4 downToDest
= GetTransformToAncestor(aToFrame
, nearestCommonAncestor
);
2368 if (downToDest
.IsSingular()) {
2369 return NONINVERTIBLE_TRANSFORM
;
2371 downToDest
.Invert();
2372 Matrix4x4 upToAncestor
= GetTransformToAncestor(aFromFrame
, nearestCommonAncestor
);
2374 float devPixelsPerAppUnitFromFrame
=
2375 1.0f
/ aFromFrame
->PresContext()->AppUnitsPerDevPixel();
2376 float devPixelsPerAppUnitToFrame
=
2377 1.0f
/ aToFrame
->PresContext()->AppUnitsPerDevPixel();
2378 gfx::Rect toDevPixels
= downToDest
.ProjectRectBounds(
2379 upToAncestor
.ProjectRectBounds(
2380 gfx::Rect(aRect
.x
* devPixelsPerAppUnitFromFrame
,
2381 aRect
.y
* devPixelsPerAppUnitFromFrame
,
2382 aRect
.width
* devPixelsPerAppUnitFromFrame
,
2383 aRect
.height
* devPixelsPerAppUnitFromFrame
)));
2384 aRect
.x
= toDevPixels
.x
/ devPixelsPerAppUnitToFrame
;
2385 aRect
.y
= toDevPixels
.y
/ devPixelsPerAppUnitToFrame
;
2386 aRect
.width
= toDevPixels
.width
/ devPixelsPerAppUnitToFrame
;
2387 aRect
.height
= toDevPixels
.height
/ devPixelsPerAppUnitToFrame
;
2388 return TRANSFORM_SUCCEEDED
;
2392 nsLayoutUtils::GetLayerTransformForFrame(nsIFrame
* aFrame
,
2393 Matrix4x4
* aTransform
)
2395 // FIXME/bug 796690: we can sometimes compute a transform in these
2396 // cases, it just increases complexity considerably. Punt for now.
2397 if (aFrame
->Preserves3DChildren() || aFrame
->HasTransformGetter()) {
2401 nsIFrame
* root
= nsLayoutUtils::GetDisplayRootFrame(aFrame
);
2402 if (root
->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE
)) {
2403 // Content may have been invalidated, so we can't reliably compute
2404 // the "layer transform" in general.
2407 // If the caller doesn't care about the value, early-return to skip
2413 nsDisplayListBuilder
builder(root
, nsDisplayListBuilder::OTHER
,
2414 false/*don't build caret*/);
2416 nsDisplayTransform
* item
=
2417 new (&builder
) nsDisplayTransform(&builder
, aFrame
, &list
, nsRect());
2419 *aTransform
= item
->GetTransform();
2420 item
->~nsDisplayTransform();
2426 TransformGfxPointFromAncestor(nsIFrame
*aFrame
,
2427 const Point
&aPoint
,
2428 nsIFrame
*aAncestor
,
2431 Matrix4x4 ctm
= nsLayoutUtils::GetTransformToAncestor(aFrame
, aAncestor
);
2433 Point4D point
= ctm
.ProjectPoint(aPoint
);
2434 if (!point
.HasPositiveWCoord()) {
2437 *aOut
= point
.As2DPoint();
2442 TransformGfxRectToAncestor(nsIFrame
*aFrame
,
2444 const nsIFrame
*aAncestor
,
2445 bool* aPreservesAxisAlignedRectangles
= nullptr)
2447 Matrix4x4 ctm
= nsLayoutUtils::GetTransformToAncestor(aFrame
, aAncestor
);
2448 if (aPreservesAxisAlignedRectangles
) {
2450 *aPreservesAxisAlignedRectangles
=
2451 ctm
.Is2D(&matrix2d
) && matrix2d
.PreservesAxisAlignedRectangles();
2453 return ctm
.TransformBounds(aRect
);
2456 static SVGTextFrame
*
2457 GetContainingSVGTextFrame(nsIFrame
* aFrame
)
2459 if (!aFrame
->IsSVGText()) {
2463 return static_cast<SVGTextFrame
*>
2464 (nsLayoutUtils::GetClosestFrameOfType(aFrame
->GetParent(),
2465 nsGkAtoms::svgTextFrame
));
2469 nsLayoutUtils::TransformAncestorPointToFrame(nsIFrame
* aFrame
,
2470 const nsPoint
& aPoint
,
2471 nsIFrame
* aAncestor
)
2473 SVGTextFrame
* text
= GetContainingSVGTextFrame(aFrame
);
2475 float factor
= aFrame
->PresContext()->AppUnitsPerDevPixel();
2476 Point
result(NSAppUnitsToFloatPixels(aPoint
.x
, factor
),
2477 NSAppUnitsToFloatPixels(aPoint
.y
, factor
));
2480 if (!TransformGfxPointFromAncestor(text
, result
, aAncestor
, &result
)) {
2481 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
2483 result
= text
->TransformFramePointToTextChild(result
, aFrame
);
2485 if (!TransformGfxPointFromAncestor(aFrame
, result
, nullptr, &result
)) {
2486 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
2490 return nsPoint(NSFloatPixelsToAppUnits(float(result
.x
), factor
),
2491 NSFloatPixelsToAppUnits(float(result
.y
), factor
));
2495 nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame
* aFrame
,
2496 const nsRect
& aRect
,
2497 const nsIFrame
* aAncestor
,
2498 bool* aPreservesAxisAlignedRectangles
/* = nullptr */)
2500 SVGTextFrame
* text
= GetContainingSVGTextFrame(aFrame
);
2502 float srcAppUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
2506 result
= ToRect(text
->TransformFrameRectFromTextChild(aRect
, aFrame
));
2507 result
= TransformGfxRectToAncestor(text
, result
, aAncestor
);
2508 // TransformFrameRectFromTextChild could involve any kind of transform, we
2509 // could drill down into it to get an answer out of it but we don't yet.
2510 if (aPreservesAxisAlignedRectangles
)
2511 *aPreservesAxisAlignedRectangles
= false;
2513 result
= Rect(NSAppUnitsToFloatPixels(aRect
.x
, srcAppUnitsPerDevPixel
),
2514 NSAppUnitsToFloatPixels(aRect
.y
, srcAppUnitsPerDevPixel
),
2515 NSAppUnitsToFloatPixels(aRect
.width
, srcAppUnitsPerDevPixel
),
2516 NSAppUnitsToFloatPixels(aRect
.height
, srcAppUnitsPerDevPixel
));
2517 result
= TransformGfxRectToAncestor(aFrame
, result
, aAncestor
, aPreservesAxisAlignedRectangles
);
2520 float destAppUnitsPerDevPixel
= aAncestor
->PresContext()->AppUnitsPerDevPixel();
2521 return nsRect(NSFloatPixelsToAppUnits(float(result
.x
), destAppUnitsPerDevPixel
),
2522 NSFloatPixelsToAppUnits(float(result
.y
), destAppUnitsPerDevPixel
),
2523 NSFloatPixelsToAppUnits(float(result
.width
), destAppUnitsPerDevPixel
),
2524 NSFloatPixelsToAppUnits(float(result
.height
), destAppUnitsPerDevPixel
));
2527 static nsIntPoint
GetWidgetOffset(nsIWidget
* aWidget
, nsIWidget
*& aRootWidget
) {
2528 nsIntPoint
offset(0, 0);
2529 while ((aWidget
->WindowType() == eWindowType_child
||
2530 aWidget
->WindowType() == eWindowType_plugin
)) {
2531 nsIWidget
* parent
= aWidget
->GetParent();
2536 aWidget
->GetBounds(bounds
);
2537 offset
+= bounds
.TopLeft();
2540 aRootWidget
= aWidget
;
2545 nsLayoutUtils::TranslateWidgetToView(nsPresContext
* aPresContext
,
2546 nsIWidget
* aWidget
, nsIntPoint aPt
,
2550 nsIWidget
* viewWidget
= aView
->GetNearestWidget(&viewOffset
);
2552 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
2555 nsIWidget
* fromRoot
;
2556 nsIntPoint fromOffset
= GetWidgetOffset(aWidget
, fromRoot
);
2558 nsIntPoint toOffset
= GetWidgetOffset(viewWidget
, toRoot
);
2560 nsIntPoint widgetPoint
;
2561 if (fromRoot
== toRoot
) {
2562 widgetPoint
= aPt
+ fromOffset
- toOffset
;
2564 nsIntPoint screenPoint
= aWidget
->WidgetToScreenOffset();
2565 widgetPoint
= aPt
+ screenPoint
- viewWidget
->WidgetToScreenOffset();
2568 nsPoint
widgetAppUnits(aPresContext
->DevPixelsToAppUnits(widgetPoint
.x
),
2569 aPresContext
->DevPixelsToAppUnits(widgetPoint
.y
));
2570 return widgetAppUnits
- viewOffset
;
2573 // Combine aNewBreakType with aOrigBreakType, but limit the break types
2574 // to NS_STYLE_CLEAR_LEFT, RIGHT, BOTH.
2576 nsLayoutUtils::CombineBreakType(uint8_t aOrigBreakType
,
2577 uint8_t aNewBreakType
)
2579 uint8_t breakType
= aOrigBreakType
;
2581 case NS_STYLE_CLEAR_LEFT
:
2582 if (NS_STYLE_CLEAR_RIGHT
== aNewBreakType
||
2583 NS_STYLE_CLEAR_BOTH
== aNewBreakType
) {
2584 breakType
= NS_STYLE_CLEAR_BOTH
;
2587 case NS_STYLE_CLEAR_RIGHT
:
2588 if (NS_STYLE_CLEAR_LEFT
== aNewBreakType
||
2589 NS_STYLE_CLEAR_BOTH
== aNewBreakType
) {
2590 breakType
= NS_STYLE_CLEAR_BOTH
;
2593 case NS_STYLE_CLEAR_NONE
:
2594 if (NS_STYLE_CLEAR_LEFT
== aNewBreakType
||
2595 NS_STYLE_CLEAR_RIGHT
== aNewBreakType
||
2596 NS_STYLE_CLEAR_BOTH
== aNewBreakType
) {
2597 breakType
= aNewBreakType
;
2603 #ifdef MOZ_DUMP_PAINTING
2606 static bool gDumpEventList
= false;
2607 int gPaintCount
= 0;
2611 nsLayoutUtils::GetFrameForPoint(nsIFrame
* aFrame
, nsPoint aPt
, uint32_t aFlags
)
2613 PROFILER_LABEL("nsLayoutUtils", "GetFrameForPoint",
2614 js::ProfileEntry::Category::GRAPHICS
);
2617 nsAutoTArray
<nsIFrame
*,8> outFrames
;
2618 rv
= GetFramesForArea(aFrame
, nsRect(aPt
, nsSize(1, 1)), outFrames
, aFlags
);
2619 NS_ENSURE_SUCCESS(rv
, nullptr);
2620 return outFrames
.Length() ? outFrames
.ElementAt(0) : nullptr;
2624 nsLayoutUtils::GetFramesForArea(nsIFrame
* aFrame
, const nsRect
& aRect
,
2625 nsTArray
<nsIFrame
*> &aOutFrames
,
2628 PROFILER_LABEL("nsLayoutUtils", "GetFramesForArea",
2629 js::ProfileEntry::Category::GRAPHICS
);
2631 nsDisplayListBuilder
builder(aFrame
, nsDisplayListBuilder::EVENT_DELIVERY
,
2634 nsRect
target(aRect
);
2636 if (aFlags
& IGNORE_PAINT_SUPPRESSION
) {
2637 builder
.IgnorePaintSuppression();
2640 if (aFlags
& IGNORE_ROOT_SCROLL_FRAME
) {
2641 nsIFrame
* rootScrollFrame
=
2642 aFrame
->PresContext()->PresShell()->GetRootScrollFrame();
2643 if (rootScrollFrame
) {
2644 builder
.SetIgnoreScrollFrame(rootScrollFrame
);
2647 if (aFlags
& IGNORE_CROSS_DOC
) {
2648 builder
.SetDescendIntoSubdocuments(false);
2651 builder
.EnterPresShell(aFrame
, target
);
2652 aFrame
->BuildDisplayListForStackingContext(&builder
, target
, &list
);
2653 builder
.LeavePresShell(aFrame
, target
);
2655 #ifdef MOZ_DUMP_PAINTING
2656 if (gDumpEventList
) {
2657 fprintf_stderr(stderr
, "Event handling --- (%d,%d):\n", aRect
.x
, aRect
.y
);
2659 std::stringstream ss
;
2660 nsFrame::PrintDisplayList(&builder
, list
, ss
);
2665 nsDisplayItem::HitTestState hitTestState
;
2666 list
.HitTest(&builder
, target
, &hitTestState
, &aOutFrames
);
2671 // This function is only used on B2G, and some compilers complain about
2672 // unused static functions, so we need to #ifdef it.
2673 #ifdef MOZ_WIDGET_GONK
2674 // aScrollFrame and aScrollFrameAsScrollable must be non-nullptr
2676 CalculateFrameMetricsForDisplayPort(nsIFrame
* aScrollFrame
,
2677 nsIScrollableFrame
* aScrollFrameAsScrollable
) {
2678 // Calculate the metrics necessary for calculating the displayport.
2679 // This code has a lot in common with the code in RecordFrameMetrics();
2680 // we may want to refactor this at some point.
2681 FrameMetrics metrics
;
2682 nsPresContext
* presContext
= aScrollFrame
->PresContext();
2683 nsIPresShell
* presShell
= presContext
->PresShell();
2684 CSSToLayoutDeviceScale
deviceScale(float(nsPresContext::AppUnitsPerCSSPixel())
2685 / presContext
->AppUnitsPerDevPixel());
2686 ParentLayerToLayerScale resolution
;
2687 if (aScrollFrame
== presShell
->GetRootScrollFrame()) {
2688 // Only the root scrollable frame for a given presShell should pick up
2689 // the presShell's resolution. All the other frames are 1.0.
2690 resolution
= ParentLayerToLayerScale(presShell
->GetXResolution(),
2691 presShell
->GetYResolution());
2693 LayoutDeviceToLayerScale
cumulativeResolution(presShell
->GetCumulativeResolution().width
);
2695 metrics
.mDevPixelsPerCSSPixel
= deviceScale
;
2696 metrics
.mResolution
= resolution
;
2697 metrics
.mCumulativeResolution
= cumulativeResolution
;
2698 metrics
.SetZoom(deviceScale
* cumulativeResolution
* LayerToScreenScale(1));
2700 // Only the size of the composition bounds is relevant to the
2701 // displayport calculation, not its origin.
2702 nsSize compositionSize
= nsLayoutUtils::CalculateCompositionSizeForFrame(aScrollFrame
);
2703 metrics
.mCompositionBounds
2704 = LayoutDeviceRect::FromAppUnits(nsRect(nsPoint(0, 0), compositionSize
),
2705 presContext
->AppUnitsPerDevPixel())
2706 * (cumulativeResolution
/ resolution
);
2708 // This function is used for setting a display port for subframes, so
2709 // aScrollFrame will not be the root content document's root scroll frame.
2710 metrics
.SetRootCompositionSize(
2711 nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame
, false, metrics
));
2713 metrics
.SetScrollOffset(CSSPoint::FromAppUnits(
2714 aScrollFrameAsScrollable
->GetScrollPosition()));
2716 metrics
.mScrollableRect
= CSSRect::FromAppUnits(
2717 nsLayoutUtils::CalculateScrollableRectForFrame(aScrollFrameAsScrollable
, nullptr));
2724 nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder
& aBuilder
,
2725 nsIFrame
* aScrollFrame
,
2726 nsRect aDisplayPortBase
,
2727 nsRect
* aOutDisplayport
) {
2728 nsIContent
* content
= aScrollFrame
->GetContent();
2729 nsIScrollableFrame
* scrollableFrame
= do_QueryFrame(aScrollFrame
);
2730 if (!content
|| !scrollableFrame
) {
2734 // Set the base rect. Note that this will not influence 'haveDisplayPort',
2735 // which is based on either the whole rect or margins being set, but it
2736 // will affect what is returned in 'aOutDisplayPort' if margins are set.
2737 SetDisplayPortBase(content
, aDisplayPortBase
);
2739 bool haveDisplayPort
= GetDisplayPort(content
, aOutDisplayport
);
2741 #ifdef MOZ_WIDGET_GONK
2742 // On B2G, we perform an optimization where we ensure that at least one
2743 // async-scrollable frame (i.e. one that WantsAsyncScroll()) has a displayport.
2744 // If that's not the case yet, and we are async-scrollable, we will get a
2746 // Note: we only do this in processes where we do subframe scrolling to
2747 // begin with (i.e., not in the parent process on B2G).
2748 if (aBuilder
.IsPaintingToWindow() && WantSubAPZC() &&
2749 !aBuilder
.HaveScrollableDisplayPort() &&
2750 scrollableFrame
->WantAsyncScroll()) {
2752 // If we don't already have a displayport, calculate and set one.
2753 if (!haveDisplayPort
) {
2754 FrameMetrics metrics
= CalculateFrameMetricsForDisplayPort(aScrollFrame
, scrollableFrame
);
2755 LayerMargin displayportMargins
= AsyncPanZoomController::CalculatePendingDisplayPort(
2756 metrics
, ScreenPoint(0.0f
, 0.0f
), 0.0);
2757 nsIPresShell
* presShell
= aScrollFrame
->PresContext()->GetPresShell();
2758 gfx::IntSize alignment
= gfxPrefs::LayersTilesEnabled()
2759 ? gfx::IntSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()) :
2761 nsLayoutUtils::SetDisplayPortMargins(
2762 content
, presShell
, displayportMargins
, alignment
.width
,
2763 alignment
.height
, 0, nsLayoutUtils::RepaintMode::DoNotRepaint
);
2764 haveDisplayPort
= GetDisplayPort(content
, aOutDisplayport
);
2765 NS_ASSERTION(haveDisplayPort
, "should have a displayport after having just set it");
2768 // Record that the we now have a scrollable display port.
2769 aBuilder
.SetHaveScrollableDisplayPort();
2773 return haveDisplayPort
;
2777 nsLayoutUtils::PaintFrame(nsRenderingContext
* aRenderingContext
, nsIFrame
* aFrame
,
2778 const nsRegion
& aDirtyRegion
, nscolor aBackstop
,
2781 PROFILER_LABEL("nsLayoutUtils", "PaintFrame",
2782 js::ProfileEntry::Category::GRAPHICS
);
2784 if (aFlags
& PAINT_WIDGET_LAYERS
) {
2785 nsView
* view
= aFrame
->GetView();
2786 if (!(view
&& view
->GetWidget() && GetDisplayRootFrame(aFrame
) == aFrame
)) {
2787 aFlags
&= ~PAINT_WIDGET_LAYERS
;
2788 NS_ASSERTION(aRenderingContext
, "need a rendering context");
2792 nsPresContext
* presContext
= aFrame
->PresContext();
2793 nsIPresShell
* presShell
= presContext
->PresShell();
2794 nsRootPresContext
* rootPresContext
= presContext
->GetRootPresContext();
2795 if (!rootPresContext
) {
2799 nsDisplayListBuilder
builder(aFrame
, nsDisplayListBuilder::PAINTING
,
2800 !(aFlags
& PAINT_HIDE_CARET
));
2802 nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame();
2803 bool usingDisplayPort
= false;
2805 if (rootScrollFrame
&& !aFrame
->GetParent() &&
2806 (aFlags
& (PAINT_WIDGET_LAYERS
| PAINT_TO_WINDOW
))) {
2807 nsRect
displayportBase(
2809 nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame
));
2810 usingDisplayPort
= nsLayoutUtils::GetOrMaybeCreateDisplayPort(
2811 builder
, rootScrollFrame
, displayportBase
, &displayport
);
2814 nsRegion visibleRegion
;
2815 if (aFlags
& PAINT_WIDGET_LAYERS
) {
2816 // This layer tree will be reused, so we'll need to calculate it
2817 // for the whole "visible" area of the window
2819 // |ignoreViewportScrolling| and |usingDisplayPort| are persistent
2820 // document-rendering state. We rely on PresShell to flush
2821 // retained layers as needed when that persistent state changes.
2822 if (!usingDisplayPort
) {
2823 visibleRegion
= aFrame
->GetVisualOverflowRectRelativeToSelf();
2825 visibleRegion
= displayport
;
2828 visibleRegion
= aDirtyRegion
;
2831 // If we're going to display something different from what we'd normally
2832 // paint in a window then we will flush out any retained layer trees before
2833 // *and after* we draw.
2834 bool willFlushRetainedLayers
= (aFlags
& PAINT_HIDE_CARET
) != 0;
2837 if (aFlags
& PAINT_IN_TRANSFORM
) {
2838 builder
.SetInTransform(true);
2840 if (aFlags
& PAINT_SYNC_DECODE_IMAGES
) {
2841 builder
.SetSyncDecodeImages(true);
2843 if (aFlags
& (PAINT_WIDGET_LAYERS
| PAINT_TO_WINDOW
)) {
2844 builder
.SetPaintingToWindow(true);
2846 if (aFlags
& PAINT_IGNORE_SUPPRESSION
) {
2847 builder
.IgnorePaintSuppression();
2849 // Windowed plugins aren't allowed in popups
2850 if ((aFlags
& PAINT_WIDGET_LAYERS
) &&
2851 !willFlushRetainedLayers
&&
2852 !(aFlags
& PAINT_DOCUMENT_RELATIVE
) &&
2853 rootPresContext
->NeedToComputePluginGeometryUpdates()) {
2854 builder
.SetWillComputePluginGeometry(true);
2856 nsRect
canvasArea(nsPoint(0, 0), aFrame
->GetSize());
2858 bool ignoreViewportScrolling
=
2859 aFrame
->GetParent() ? false : presShell
->IgnoringViewportScrolling();
2860 if (ignoreViewportScrolling
&& rootScrollFrame
) {
2861 nsIScrollableFrame
* rootScrollableFrame
=
2862 presShell
->GetRootScrollFrameAsScrollable();
2863 if (aFlags
& PAINT_DOCUMENT_RELATIVE
) {
2864 // Make visibleRegion and aRenderingContext relative to the
2865 // scrolled frame instead of the root frame.
2866 nsPoint pos
= rootScrollableFrame
->GetScrollPosition();
2867 visibleRegion
.MoveBy(-pos
);
2868 if (aRenderingContext
) {
2869 aRenderingContext
->Translate(pos
);
2872 builder
.SetIgnoreScrollFrame(rootScrollFrame
);
2874 nsCanvasFrame
* canvasFrame
=
2875 do_QueryFrame(rootScrollableFrame
->GetScrolledFrame());
2877 // Use UnionRect here to ensure that areas where the scrollbars
2878 // were are still filled with the background color.
2879 canvasArea
.UnionRect(canvasArea
,
2880 canvasFrame
->CanvasArea() + builder
.ToReferenceFrame(canvasFrame
));
2884 nsRect dirtyRect
= visibleRegion
.GetBounds();
2885 builder
.EnterPresShell(aFrame
, dirtyRect
);
2887 // If a scrollable container layer is created in nsDisplayList::PaintForFrame,
2888 // it will be the scroll parent for display items that are built in the
2889 // BuildDisplayListForStackingContext call below. We need to set the scroll
2890 // parent on the display list builder while we build those items, so that they
2891 // can pick up their scroll parent's id.
2892 ViewID id
= FrameMetrics::NULL_SCROLL_ID
;
2893 if (ignoreViewportScrolling
&& presContext
->IsRootContentDocument()) {
2894 if (nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame()) {
2895 if (nsIContent
* content
= rootScrollFrame
->GetContent()) {
2896 id
= nsLayoutUtils::FindOrCreateIDFor(content
);
2900 nsDisplayListBuilder::AutoCurrentScrollParentIdSetter
idSetter(&builder
, id
);
2902 PROFILER_LABEL("nsLayoutUtils", "PaintFrame::BuildDisplayList",
2903 js::ProfileEntry::Category::GRAPHICS
);
2905 aFrame
->BuildDisplayListForStackingContext(&builder
, dirtyRect
, &list
);
2907 const bool paintAllContinuations
= aFlags
& PAINT_ALL_CONTINUATIONS
;
2908 NS_ASSERTION(!paintAllContinuations
|| !aFrame
->GetPrevContinuation(),
2909 "If painting all continuations, the frame must be "
2910 "first-continuation");
2912 nsIAtom
* frameType
= aFrame
->GetType();
2914 if (paintAllContinuations
) {
2915 nsIFrame
* currentFrame
= aFrame
;
2916 while ((currentFrame
= currentFrame
->GetNextContinuation()) != nullptr) {
2917 PROFILER_LABEL("nsLayoutUtils", "PaintFrame::ContinuationsBuildDisplayList",
2918 js::ProfileEntry::Category::GRAPHICS
);
2920 nsRect frameDirty
= dirtyRect
- builder
.ToReferenceFrame(currentFrame
);
2921 currentFrame
->BuildDisplayListForStackingContext(&builder
,
2926 // For the viewport frame in print preview/page layout we want to paint
2927 // the grey background behind the page, not the canvas color.
2928 if (frameType
== nsGkAtoms::viewportFrame
&&
2929 nsLayoutUtils::NeedsPrintPreviewBackground(presContext
)) {
2930 nsRect bounds
= nsRect(builder
.ToReferenceFrame(aFrame
),
2932 presShell
->AddPrintPreviewBackgroundItem(builder
, list
, aFrame
, bounds
);
2933 } else if (frameType
!= nsGkAtoms::pageFrame
) {
2934 // For printing, this function is first called on an nsPageFrame, which
2935 // creates a display list with a PageContent item. The PageContent item's
2936 // paint function calls this function on the nsPageFrame's child which is
2937 // an nsPageContentFrame. We only want to add the canvas background color
2938 // item once, for the nsPageContentFrame.
2940 // Add the canvas background color to the bottom of the list. This
2941 // happens after we've built the list so that AddCanvasBackgroundColorItem
2942 // can monkey with the contents if necessary.
2943 canvasArea
.IntersectRect(canvasArea
, visibleRegion
.GetBounds());
2944 presShell
->AddCanvasBackgroundColorItem(
2945 builder
, list
, aFrame
, canvasArea
, aBackstop
);
2947 // If the passed in backstop color makes us draw something different from
2948 // normal, we need to flush layers.
2949 if ((aFlags
& PAINT_WIDGET_LAYERS
) && !willFlushRetainedLayers
) {
2950 nsView
* view
= aFrame
->GetView();
2952 nscolor backstop
= presShell
->ComputeBackstopColor(view
);
2953 // The PresShell's canvas background color doesn't get updated until
2954 // EnterPresShell, so this check has to be done after that.
2955 nscolor canvasColor
= presShell
->GetCanvasBackground();
2956 if (NS_ComposeColors(aBackstop
, canvasColor
) !=
2957 NS_ComposeColors(backstop
, canvasColor
)) {
2958 willFlushRetainedLayers
= true;
2964 builder
.LeavePresShell(aFrame
, dirtyRect
);
2966 if (builder
.GetHadToIgnorePaintSuppression()) {
2967 willFlushRetainedLayers
= true;
2970 #ifdef MOZ_DUMP_PAINTING
2971 FILE* savedDumpFile
= gfxUtils::sDumpPaintFile
;
2973 std::stringstream ss
;
2974 if (gfxUtils::DumpPaintList() || gfxUtils::sDumpPainting
) {
2975 if (gfxUtils::sDumpPaintingToFile
) {
2976 nsCString
string("dump-");
2977 string
.AppendInt(gPaintCount
);
2978 string
.AppendLiteral(".html");
2979 gfxUtils::sDumpPaintFile
= fopen(string
.BeginReading(), "w");
2981 gfxUtils::sDumpPaintFile
= stderr
;
2983 if (gfxUtils::sDumpPaintingToFile
) {
2984 ss
<< "<html><head><script>var array = {}; function ViewImage(index) { window.location = array[index]; }</script></head><body>";
2986 ss
<< nsPrintfCString("Painting --- before optimization (dirty %d,%d,%d,%d):\n",
2987 dirtyRect
.x
, dirtyRect
.y
, dirtyRect
.width
, dirtyRect
.height
).get();
2988 nsFrame::PrintDisplayList(&builder
, list
, ss
, gfxUtils::sDumpPaintingToFile
);
2989 if (gfxUtils::sDumpPaintingToFile
) {
2995 uint32_t flags
= nsDisplayList::PAINT_DEFAULT
;
2996 if (aFlags
& PAINT_WIDGET_LAYERS
) {
2997 flags
|= nsDisplayList::PAINT_USE_WIDGET_LAYERS
;
2998 if (willFlushRetainedLayers
) {
2999 // The caller wanted to paint from retained layers, but set up
3000 // the paint in such a way that we can't use them. We're going
3001 // to display something different from what we'd normally paint
3002 // in a window, so make sure we flush out any retained layer
3003 // trees before *and after* we draw. Callers should be fixed to
3005 NS_WARNING("Flushing retained layers!");
3006 flags
|= nsDisplayList::PAINT_FLUSH_LAYERS
;
3007 } else if (!(aFlags
& PAINT_DOCUMENT_RELATIVE
)) {
3008 nsIWidget
*widget
= aFrame
->GetNearestWidget();
3010 // If we're finished building display list items for painting of the outermost
3011 // pres shell, notify the widget about any toolbars we've encountered.
3012 widget
->UpdateThemeGeometries(builder
.GetThemeGeometries());
3016 if (aFlags
& PAINT_EXISTING_TRANSACTION
) {
3017 flags
|= nsDisplayList::PAINT_EXISTING_TRANSACTION
;
3019 if (aFlags
& PAINT_NO_COMPOSITE
) {
3020 flags
|= nsDisplayList::PAINT_NO_COMPOSITE
;
3022 if (aFlags
& PAINT_COMPRESSED
) {
3023 flags
|= nsDisplayList::PAINT_COMPRESSED
;
3026 list
.PaintRoot(&builder
, aRenderingContext
, flags
);
3028 #ifdef MOZ_DUMP_PAINTING
3029 if (gfxUtils::DumpPaintList() || gfxUtils::sDumpPainting
) {
3030 if (gfxUtils::sDumpPaintingToFile
) {
3033 ss
<< "Painting --- after optimization:\n";
3034 nsFrame::PrintDisplayList(&builder
, list
, ss
, gfxUtils::sDumpPaintingToFile
);
3036 ss
<< "Painting --- retained layer tree:\n";
3037 nsIWidget
* widget
= aFrame
->GetNearestWidget();
3039 nsRefPtr
<LayerManager
> layerManager
= widget
->GetLayerManager();
3041 FrameLayerBuilder::DumpRetainedLayerTree(layerManager
, ss
,
3042 gfxUtils::sDumpPaintingToFile
);
3045 if (gfxUtils::sDumpPaintingToFile
) {
3046 ss
<< "</body></html>";
3049 fprint_stderr(gfxUtils::sDumpPaintFile
, ss
);
3051 if (gfxUtils::sDumpPaintingToFile
) {
3052 fclose(gfxUtils::sDumpPaintFile
);
3054 gfxUtils::sDumpPaintFile
= savedDumpFile
;
3059 // Update the widget's opaque region information. This sets
3060 // glass boundaries on Windows. Also set up plugin clip regions and bounds.
3061 if ((aFlags
& PAINT_WIDGET_LAYERS
) &&
3062 !willFlushRetainedLayers
&&
3063 !(aFlags
& PAINT_DOCUMENT_RELATIVE
)) {
3064 nsIWidget
*widget
= aFrame
->GetNearestWidget();
3066 nsRegion opaqueRegion
;
3067 opaqueRegion
.And(builder
.GetWindowExcludeGlassRegion(), builder
.GetWindowOpaqueRegion());
3068 widget
->UpdateOpaqueRegion(opaqueRegion
.ToNearestPixels(presContext
->AppUnitsPerDevPixel()));
3072 if (builder
.WillComputePluginGeometry()) {
3073 nsRefPtr
<LayerManager
> layerManager
;
3074 nsIWidget
* widget
= aFrame
->GetNearestWidget();
3076 layerManager
= widget
->GetLayerManager();
3079 rootPresContext
->ComputePluginGeometryUpdates(aFrame
, &builder
, &list
);
3081 // We're not going to get a WillPaintWindow event here if we didn't do
3082 // widget invalidation, so just apply the plugin geometry update here instead.
3083 // We could instead have the compositor send back an equivalent to WillPaintWindow,
3084 // but it should be close enough to now not to matter.
3085 if (layerManager
&& !layerManager
->NeedsWidgetInvalidation()) {
3086 rootPresContext
->ApplyPluginGeometryUpdates();
3089 // We told the compositor thread not to composite when it received the transaction because
3090 // we wanted to update plugins first. Schedule the composite now.
3092 layerManager
->Composite();
3097 // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
3103 * Uses a binary search for find where the cursor falls in the line of text
3104 * It also keeps track of the part of the string that has already been measured
3105 * so it doesn't have to keep measuring the same text over and over
3107 * @param "aBaseWidth" contains the width in twips of the portion
3108 * of the text that has already been measured, and aBaseInx contains
3109 * the index of the text that has already been measured.
3111 * @param aTextWidth returns the (in twips) the length of the text that falls
3112 * before the cursor aIndex contains the index of the text where the cursor falls
3115 nsLayoutUtils::BinarySearchForPosition(nsRenderingContext
* aRendContext
,
3116 const char16_t
* aText
,
3123 int32_t& aTextWidth
)
3125 int32_t range
= aEndInx
- aStartInx
;
3126 if ((range
== 1) || (range
== 2 && NS_IS_HIGH_SURROGATE(aText
[aStartInx
]))) {
3127 aIndex
= aStartInx
+ aBaseInx
;
3128 aTextWidth
= aRendContext
->GetWidth(aText
, aIndex
);
3132 int32_t inx
= aStartInx
+ (range
/ 2);
3134 // Make sure we don't leave a dangling low surrogate
3135 if (NS_IS_HIGH_SURROGATE(aText
[inx
-1]))
3138 int32_t textWidth
= aRendContext
->GetWidth(aText
, inx
);
3140 int32_t fullWidth
= aBaseWidth
+ textWidth
;
3141 if (fullWidth
== aCursorPos
) {
3142 aTextWidth
= textWidth
;
3145 } else if (aCursorPos
< fullWidth
) {
3146 aTextWidth
= aBaseWidth
;
3147 if (BinarySearchForPosition(aRendContext
, aText
, aBaseWidth
, aBaseInx
, aStartInx
, inx
, aCursorPos
, aIndex
, aTextWidth
)) {
3151 aTextWidth
= fullWidth
;
3152 if (BinarySearchForPosition(aRendContext
, aText
, aBaseWidth
, aBaseInx
, inx
, aEndInx
, aCursorPos
, aIndex
, aTextWidth
)) {
3160 AddBoxesForFrame(nsIFrame
* aFrame
,
3161 nsLayoutUtils::BoxCallback
* aCallback
)
3163 nsIAtom
* pseudoType
= aFrame
->StyleContext()->GetPseudo();
3165 if (pseudoType
== nsCSSAnonBoxes::tableOuter
) {
3166 AddBoxesForFrame(aFrame
->GetFirstPrincipalChild(), aCallback
);
3167 nsIFrame
* kid
= aFrame
->GetFirstChild(nsIFrame::kCaptionList
);
3169 AddBoxesForFrame(kid
, aCallback
);
3171 } else if (pseudoType
== nsCSSAnonBoxes::mozAnonymousBlock
||
3172 pseudoType
== nsCSSAnonBoxes::mozAnonymousPositionedBlock
||
3173 pseudoType
== nsCSSAnonBoxes::mozMathMLAnonymousBlock
||
3174 pseudoType
== nsCSSAnonBoxes::mozXULAnonymousBlock
) {
3175 for (nsIFrame
* kid
= aFrame
->GetFirstPrincipalChild(); kid
; kid
= kid
->GetNextSibling()) {
3176 AddBoxesForFrame(kid
, aCallback
);
3179 aCallback
->AddBox(aFrame
);
3184 nsLayoutUtils::GetAllInFlowBoxes(nsIFrame
* aFrame
, BoxCallback
* aCallback
)
3187 AddBoxesForFrame(aFrame
, aCallback
);
3188 aFrame
= nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame
);
3193 nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame
* aFrame
)
3196 nsIAtom
* pseudoType
= aFrame
->StyleContext()->GetPseudo();
3198 if (pseudoType
== nsCSSAnonBoxes::tableOuter
) {
3199 nsIFrame
* f
= GetFirstNonAnonymousFrame(aFrame
->GetFirstPrincipalChild());
3203 nsIFrame
* kid
= aFrame
->GetFirstChild(nsIFrame::kCaptionList
);
3205 f
= GetFirstNonAnonymousFrame(kid
);
3210 } else if (pseudoType
== nsCSSAnonBoxes::mozAnonymousBlock
||
3211 pseudoType
== nsCSSAnonBoxes::mozAnonymousPositionedBlock
||
3212 pseudoType
== nsCSSAnonBoxes::mozMathMLAnonymousBlock
||
3213 pseudoType
== nsCSSAnonBoxes::mozXULAnonymousBlock
) {
3214 for (nsIFrame
* kid
= aFrame
->GetFirstPrincipalChild(); kid
; kid
= kid
->GetNextSibling()) {
3215 nsIFrame
* f
= GetFirstNonAnonymousFrame(kid
);
3224 aFrame
= nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame
);
3229 struct BoxToRect
: public nsLayoutUtils::BoxCallback
{
3230 nsIFrame
* mRelativeTo
;
3231 nsLayoutUtils::RectCallback
* mCallback
;
3234 BoxToRect(nsIFrame
* aRelativeTo
, nsLayoutUtils::RectCallback
* aCallback
,
3236 : mRelativeTo(aRelativeTo
), mCallback(aCallback
), mFlags(aFlags
) {}
3238 virtual void AddBox(nsIFrame
* aFrame
) MOZ_OVERRIDE
{
3240 nsIFrame
* outer
= nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame
, &r
);
3243 switch (mFlags
& nsLayoutUtils::RECTS_WHICH_BOX_MASK
) {
3244 case nsLayoutUtils::RECTS_USE_CONTENT_BOX
:
3245 r
= aFrame
->GetContentRectRelativeToSelf();
3247 case nsLayoutUtils::RECTS_USE_PADDING_BOX
:
3248 r
= aFrame
->GetPaddingRectRelativeToSelf();
3250 case nsLayoutUtils::RECTS_USE_MARGIN_BOX
:
3251 r
= aFrame
->GetMarginRectRelativeToSelf();
3253 default: // Use the border box
3254 r
= aFrame
->GetRectRelativeToSelf();
3257 if (mFlags
& nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS
) {
3258 r
= nsLayoutUtils::TransformFrameRectToAncestor(outer
, r
, mRelativeTo
);
3260 r
+= outer
->GetOffsetTo(mRelativeTo
);
3262 mCallback
->AddRect(r
);
3267 nsLayoutUtils::GetAllInFlowRects(nsIFrame
* aFrame
, nsIFrame
* aRelativeTo
,
3268 RectCallback
* aCallback
, uint32_t aFlags
)
3270 BoxToRect
converter(aRelativeTo
, aCallback
, aFlags
);
3271 GetAllInFlowBoxes(aFrame
, &converter
);
3274 nsLayoutUtils::RectAccumulator::RectAccumulator() : mSeenFirstRect(false) {}
3276 void nsLayoutUtils::RectAccumulator::AddRect(const nsRect
& aRect
) {
3277 mResultRect
.UnionRect(mResultRect
, aRect
);
3278 if (!mSeenFirstRect
) {
3279 mSeenFirstRect
= true;
3284 nsLayoutUtils::RectListBuilder::RectListBuilder(DOMRectList
* aList
)
3289 void nsLayoutUtils::RectListBuilder::AddRect(const nsRect
& aRect
) {
3290 nsRefPtr
<DOMRect
> rect
= new DOMRect(mRectList
);
3292 rect
->SetLayoutRect(aRect
);
3293 mRectList
->Append(rect
);
3296 nsLayoutUtils::FirstAndLastRectCollector::FirstAndLastRectCollector()
3297 : mSeenFirstRect(false)
3301 void nsLayoutUtils::FirstAndLastRectCollector::AddRect(const nsRect
& aRect
) {
3302 if (!mSeenFirstRect
) {
3303 mSeenFirstRect
= true;
3310 nsIFrame
* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame
* aFrame
)
3312 return aFrame
->PresContext()->PresShell()->GetRootFrame();
3316 nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame
* aFrame
, nsIFrame
* aRelativeTo
,
3318 RectAccumulator accumulator
;
3319 GetAllInFlowRects(aFrame
, aRelativeTo
, &accumulator
, aFlags
);
3320 return accumulator
.mResultRect
.IsEmpty() ? accumulator
.mFirstRect
3321 : accumulator
.mResultRect
;
3325 nsLayoutUtils::GetTextShadowRectsUnion(const nsRect
& aTextAndDecorationsRect
,
3329 const nsStyleText
* textStyle
= aFrame
->StyleText();
3330 if (!textStyle
->HasTextShadow())
3331 return aTextAndDecorationsRect
;
3333 nsRect resultRect
= aTextAndDecorationsRect
;
3334 int32_t A2D
= aFrame
->PresContext()->AppUnitsPerDevPixel();
3335 for (uint32_t i
= 0; i
< textStyle
->mTextShadow
->Length(); ++i
) {
3336 nsCSSShadowItem
* shadow
= textStyle
->mTextShadow
->ShadowAt(i
);
3337 nsMargin blur
= nsContextBoxBlur::GetBlurRadiusMargin(shadow
->mRadius
, A2D
);
3338 if ((aFlags
& EXCLUDE_BLUR_SHADOWS
) && blur
!= nsMargin(0, 0, 0, 0))
3341 nsRect
tmpRect(aTextAndDecorationsRect
);
3343 tmpRect
.MoveBy(nsPoint(shadow
->mXOffset
, shadow
->mYOffset
));
3344 tmpRect
.Inflate(blur
);
3346 resultRect
.UnionRect(resultRect
, tmpRect
);
3352 nsLayoutUtils::GetFontMetricsForFrame(const nsIFrame
* aFrame
,
3353 nsFontMetrics
** aFontMetrics
,
3356 return nsLayoutUtils::GetFontMetricsForStyleContext(aFrame
->StyleContext(),
3362 nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext
* aStyleContext
,
3363 nsFontMetrics
** aFontMetrics
,
3366 // pass the user font set object into the device context to pass along to CreateFontGroup
3367 nsPresContext
* pc
= aStyleContext
->PresContext();
3368 gfxUserFontSet
* fs
= pc
->GetUserFontSet();
3369 gfxTextPerfMetrics
* tp
= pc
->GetTextPerfMetrics();
3371 nsFont font
= aStyleContext
->StyleFont()->mFont
;
3372 // We need to not run font.size through floats when it's large since
3373 // doing so would be lossy. Fortunately, in such cases, aInflation is
3374 // guaranteed to be 1.0f.
3375 if (aInflation
!= 1.0f
) {
3376 font
.size
= NSToCoordRound(font
.size
* aInflation
);
3378 return pc
->DeviceContext()->GetMetricsFor(
3379 font
, aStyleContext
->StyleFont()->mLanguage
,
3380 fs
, tp
, *aFontMetrics
);
3384 nsLayoutUtils::FindChildContainingDescendant(nsIFrame
* aParent
, nsIFrame
* aDescendantFrame
)
3386 nsIFrame
* result
= aDescendantFrame
;
3389 nsIFrame
* parent
= result
->GetParent();
3390 if (parent
== aParent
) {
3394 // The frame is not an immediate child of aParent so walk up another level
3402 nsLayoutUtils::GetAsBlock(nsIFrame
* aFrame
)
3404 nsBlockFrame
* block
= do_QueryFrame(aFrame
);
3409 nsLayoutUtils::FindNearestBlockAncestor(nsIFrame
* aFrame
)
3411 nsIFrame
* nextAncestor
;
3412 for (nextAncestor
= aFrame
->GetParent(); nextAncestor
;
3413 nextAncestor
= nextAncestor
->GetParent()) {
3414 nsBlockFrame
* block
= GetAsBlock(nextAncestor
);
3422 nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame
* aFrame
)
3424 if (!(aFrame
->GetStateBits() & NS_FRAME_GENERATED_CONTENT
))
3427 nsIFrame
* f
= aFrame
;
3429 f
= GetParentOrPlaceholderFor(f
);
3430 } while (f
->GetStateBits() & NS_FRAME_GENERATED_CONTENT
);
3435 nsLayoutUtils::GetParentOrPlaceholderFor(nsIFrame
* aFrame
)
3437 if ((aFrame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
)
3438 && !aFrame
->GetPrevInFlow()) {
3439 return aFrame
->PresContext()->PresShell()->FrameManager()->
3440 GetPlaceholderFrameFor(aFrame
);
3442 return aFrame
->GetParent();
3446 nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(nsIFrame
* aFrame
)
3448 nsIFrame
* f
= GetParentOrPlaceholderFor(aFrame
);
3451 return GetCrossDocParentFrame(aFrame
);
3455 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(nsIFrame
*aFrame
)
3457 nsIFrame
*result
= aFrame
->GetNextContinuation();
3461 if ((aFrame
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
) != 0) {
3462 // We only store the ib-split sibling annotation with the first
3463 // frame in the continuation chain. Walk back to find that frame now.
3464 aFrame
= aFrame
->FirstContinuation();
3466 void* value
= aFrame
->Properties().Get(nsIFrame::IBSplitSibling());
3467 return static_cast<nsIFrame
*>(value
);
3474 nsLayoutUtils::FirstContinuationOrIBSplitSibling(nsIFrame
*aFrame
)
3476 nsIFrame
*result
= aFrame
->FirstContinuation();
3477 if (result
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
) {
3479 nsIFrame
*f
= static_cast<nsIFrame
*>
3480 (result
->Properties().Get(nsIFrame::IBSplitPrevSibling()));
3491 nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(nsIFrame
*aFrame
)
3493 if (aFrame
->GetPrevContinuation()) {
3496 if ((aFrame
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
) &&
3497 aFrame
->Properties().Get(nsIFrame::IBSplitPrevSibling())) {
3505 nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame
* aFrame
)
3510 nsIFrame
* rootScrollFrame
=
3511 aFrame
->PresContext()->PresShell()->GetRootScrollFrame();
3512 if (!rootScrollFrame
)
3515 nsIScrollableFrame
* rootScrollableFrame
= do_QueryFrame(rootScrollFrame
);
3516 NS_ASSERTION(rootScrollableFrame
, "The root scorollable frame is null");
3518 if (!IsProperAncestorFrame(rootScrollFrame
, aFrame
))
3521 nsIFrame
* rootScrolledFrame
= rootScrollableFrame
->GetScrolledFrame();
3522 return !(rootScrolledFrame
== aFrame
||
3523 IsProperAncestorFrame(rootScrolledFrame
, aFrame
));
3526 static nscoord
AddPercents(nsLayoutUtils::IntrinsicISizeType aType
,
3527 nscoord aCurrent
, float aPercent
)
3529 nscoord result
= aCurrent
;
3530 if (aPercent
> 0.0f
&& aType
== nsLayoutUtils::PREF_ISIZE
) {
3531 // XXX Should we also consider percentages for min widths, up to a
3533 if (aPercent
>= 1.0f
)
3534 result
= nscoord_MAX
;
3536 result
= NSToCoordRound(float(result
) / (1.0f
- aPercent
));
3541 // Use only for widths/heights (or their min/max), since it clamps
3542 // negative calc() results to 0.
3543 static bool GetAbsoluteCoord(const nsStyleCoord
& aStyle
, nscoord
& aResult
)
3545 if (aStyle
.IsCalcUnit()) {
3546 if (aStyle
.CalcHasPercent()) {
3549 // If it has no percents, we can pass 0 for the percentage basis.
3550 aResult
= nsRuleNode::ComputeComputedCalc(aStyle
, 0);
3556 if (eStyleUnit_Coord
!= aStyle
.GetUnit())
3559 aResult
= aStyle
.GetCoordValue();
3560 NS_ASSERTION(aResult
>= 0, "negative widths not allowed");
3564 // Only call on style coords for which GetAbsoluteCoord returned false.
3566 GetPercentHeight(const nsStyleCoord
& aStyle
,
3570 if (eStyleUnit_Percent
!= aStyle
.GetUnit() &&
3571 !aStyle
.IsCalcUnit())
3574 MOZ_ASSERT(!aStyle
.IsCalcUnit() || aStyle
.CalcHasPercent(),
3575 "GetAbsoluteCoord should have handled this");
3577 nsIFrame
*f
= aFrame
->GetContainingBlock();
3579 NS_NOTREACHED("top of frame tree not a containing block");
3583 // During reflow, nsHTMLScrollFrame::ReflowScrolledFrame uses
3584 // SetComputedHeight on the reflow state for its child to propagate its
3585 // computed height to the scrolled content. So here we skip to the scroll
3586 // frame that contains this scrolled content in order to get the same
3587 // behavior as layout when computing percentage heights.
3588 if (f
->StyleContext()->GetPseudo() == nsCSSAnonBoxes::scrolledContent
) {
3592 const nsStylePosition
*pos
= f
->StylePosition();
3594 if (!GetAbsoluteCoord(pos
->mHeight
, h
) &&
3595 !GetPercentHeight(pos
->mHeight
, f
, h
)) {
3596 NS_ASSERTION(pos
->mHeight
.GetUnit() == eStyleUnit_Auto
||
3597 pos
->mHeight
.HasPercent(),
3598 "unknown height unit");
3599 nsIAtom
* fType
= f
->GetType();
3600 if (fType
!= nsGkAtoms::viewportFrame
&& fType
!= nsGkAtoms::canvasFrame
&&
3601 fType
!= nsGkAtoms::pageContentFrame
) {
3602 // There's no basis for the percentage height, so it acts like auto.
3603 // Should we consider a max-height < min-height pair a basis for
3604 // percentage heights? The spec is somewhat unclear, and not doing
3605 // so is simpler and avoids troubling discontinuities in behavior,
3606 // so I'll choose not to. -LDB
3610 NS_ASSERTION(pos
->mHeight
.GetUnit() == eStyleUnit_Auto
,
3611 "Unexpected height unit for viewport or canvas or page-content");
3612 // For the viewport, canvas, and page-content kids, the percentage
3613 // basis is just the parent height.
3614 h
= f
->GetSize().height
;
3615 if (h
== NS_UNCONSTRAINEDSIZE
) {
3616 // We don't have a percentage basis after all
3622 if (GetAbsoluteCoord(pos
->mMaxHeight
, maxh
) ||
3623 GetPercentHeight(pos
->mMaxHeight
, f
, maxh
)) {
3627 NS_ASSERTION(pos
->mMaxHeight
.GetUnit() == eStyleUnit_None
||
3628 pos
->mMaxHeight
.HasPercent(),
3629 "unknown max-height unit");
3633 if (GetAbsoluteCoord(pos
->mMinHeight
, minh
) ||
3634 GetPercentHeight(pos
->mMinHeight
, f
, minh
)) {
3638 NS_ASSERTION(pos
->mMinHeight
.HasPercent() ||
3639 pos
->mMinHeight
.GetUnit() == eStyleUnit_Auto
,
3640 "unknown min-height unit");
3643 if (aStyle
.IsCalcUnit()) {
3644 aResult
= std::max(nsRuleNode::ComputeComputedCalc(aStyle
, h
), 0);
3648 aResult
= NSToCoordRound(aStyle
.GetPercentValue() * h
);
3652 // Handles only -moz-max-content and -moz-min-content, and
3653 // -moz-fit-content for min-width and max-width, since the others
3654 // (-moz-fit-content for width, and -moz-available) have no effect on
3655 // intrinsic widths.
3656 enum eWidthProperty
{ PROP_WIDTH
, PROP_MAX_WIDTH
, PROP_MIN_WIDTH
};
3658 GetIntrinsicCoord(const nsStyleCoord
& aStyle
,
3659 nsRenderingContext
* aRenderingContext
,
3661 eWidthProperty aProperty
,
3664 NS_PRECONDITION(aProperty
== PROP_WIDTH
|| aProperty
== PROP_MAX_WIDTH
||
3665 aProperty
== PROP_MIN_WIDTH
, "unexpected property");
3666 if (aStyle
.GetUnit() != eStyleUnit_Enumerated
)
3668 int32_t val
= aStyle
.GetIntValue();
3669 NS_ASSERTION(val
== NS_STYLE_WIDTH_MAX_CONTENT
||
3670 val
== NS_STYLE_WIDTH_MIN_CONTENT
||
3671 val
== NS_STYLE_WIDTH_FIT_CONTENT
||
3672 val
== NS_STYLE_WIDTH_AVAILABLE
,
3673 "unexpected enumerated value for width property");
3674 if (val
== NS_STYLE_WIDTH_AVAILABLE
)
3676 if (val
== NS_STYLE_WIDTH_FIT_CONTENT
) {
3677 if (aProperty
== PROP_WIDTH
)
3678 return false; // handle like 'width: auto'
3679 if (aProperty
== PROP_MAX_WIDTH
)
3680 // constrain large 'width' values down to -moz-max-content
3681 val
= NS_STYLE_WIDTH_MAX_CONTENT
;
3683 // constrain small 'width' or 'max-width' values up to -moz-min-content
3684 val
= NS_STYLE_WIDTH_MIN_CONTENT
;
3687 NS_ASSERTION(val
== NS_STYLE_WIDTH_MAX_CONTENT
||
3688 val
== NS_STYLE_WIDTH_MIN_CONTENT
,
3689 "should have reduced everything remaining to one of these");
3691 // If aFrame is a container for font size inflation, then shrink
3692 // wrapping inside of it should not apply font size inflation.
3693 AutoMaybeDisableFontInflation
an(aFrame
);
3695 if (val
== NS_STYLE_WIDTH_MAX_CONTENT
)
3696 aResult
= aFrame
->GetPrefISize(aRenderingContext
);
3698 aResult
= aFrame
->GetMinISize(aRenderingContext
);
3702 #undef DEBUG_INTRINSIC_WIDTH
3704 #ifdef DEBUG_INTRINSIC_WIDTH
3705 static int32_t gNoiseIndent
= 0;
3708 /* static */ nscoord
3709 nsLayoutUtils::IntrinsicForContainer(nsRenderingContext
*aRenderingContext
,
3711 IntrinsicISizeType aType
,
3714 NS_PRECONDITION(aFrame
, "null frame");
3715 NS_PRECONDITION(aType
== MIN_ISIZE
|| aType
== PREF_ISIZE
, "bad type");
3717 #ifdef DEBUG_INTRINSIC_WIDTH
3718 nsFrame::IndentBy(stderr
, gNoiseIndent
);
3719 static_cast<nsFrame
*>(aFrame
)->ListTag(stderr
);
3720 printf_stderr(" %s intrinsic width for container:\n",
3721 aType
== MIN_ISIZE
? "min" : "pref");
3724 // If aFrame is a container for font size inflation, then shrink
3725 // wrapping inside of it should not apply font size inflation.
3726 AutoMaybeDisableFontInflation
an(aFrame
);
3728 nsIFrame::IntrinsicISizeOffsetData offsets
=
3729 aFrame
->IntrinsicISizeOffsets(aRenderingContext
);
3731 const nsStylePosition
*stylePos
= aFrame
->StylePosition();
3732 uint8_t boxSizing
= stylePos
->mBoxSizing
;
3733 const nsStyleCoord
&styleWidth
= stylePos
->mWidth
;
3734 const nsStyleCoord
&styleMinWidth
= stylePos
->mMinWidth
;
3735 const nsStyleCoord
&styleMaxWidth
= stylePos
->mMaxWidth
;
3737 // We build up two values starting with the content box, and then
3738 // adding padding, border and margin. The result is normally
3739 // |result|. Then, when we handle 'width', 'min-width', and
3740 // 'max-width', we use the results we've been building in |min| as a
3741 // minimum, overriding 'min-width'. This ensures two things:
3742 // * that we don't let a value of 'box-sizing' specifying a width
3743 // smaller than the padding/border inside the box-sizing box give
3744 // a content width less than zero
3745 // * that we prevent tables from becoming smaller than their
3746 // intrinsic minimum width
3747 nscoord result
= 0, min
= 0;
3750 bool haveFixedMaxWidth
= GetAbsoluteCoord(styleMaxWidth
, maxw
);
3753 // Treat "min-width: auto" as 0.
3754 bool haveFixedMinWidth
;
3755 if (eStyleUnit_Auto
== styleMinWidth
.GetUnit()) {
3756 // NOTE: Technically, "auto" is supposed to behave like "min-content" on
3757 // flex items. However, we don't need to worry about that here, because
3758 // flex items' min-sizes are intentionally ignored until the flex
3759 // container explicitly considers them during space distribution.
3761 haveFixedMinWidth
= true;
3763 haveFixedMinWidth
= GetAbsoluteCoord(styleMinWidth
, minw
);
3766 // If we have a specified width (or a specified 'min-width' greater
3767 // than the specified 'max-width', which works out to the same thing),
3768 // don't even bother getting the frame's intrinsic width, because in
3769 // this case GetAbsoluteCoord(styleWidth, w) will always succeed, so
3770 // we'll never need the intrinsic dimensions.
3771 if (styleWidth
.GetUnit() == eStyleUnit_Enumerated
&&
3772 (styleWidth
.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT
||
3773 styleWidth
.GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT
)) {
3774 // -moz-fit-content and -moz-available enumerated widths compute intrinsic
3775 // widths just like auto.
3776 // For -moz-max-content and -moz-min-content, we handle them like
3777 // specified widths, but ignore box-sizing.
3778 boxSizing
= NS_STYLE_BOX_SIZING_CONTENT
;
3779 } else if (!styleWidth
.ConvertsToLength() &&
3780 !(haveFixedMinWidth
&& haveFixedMaxWidth
&& maxw
<= minw
)) {
3781 #ifdef DEBUG_INTRINSIC_WIDTH
3784 if (aType
== MIN_ISIZE
)
3785 result
= aFrame
->GetMinISize(aRenderingContext
);
3787 result
= aFrame
->GetPrefISize(aRenderingContext
);
3788 #ifdef DEBUG_INTRINSIC_WIDTH
3790 nsFrame::IndentBy(stderr
, gNoiseIndent
);
3791 static_cast<nsFrame
*>(aFrame
)->ListTag(stderr
);
3792 printf_stderr(" %s intrinsic width from frame is %d.\n",
3793 aType
== MIN_ISIZE
? "min" : "pref", result
);
3796 // Handle elements with an intrinsic ratio (or size) and a specified
3797 // height, min-height, or max-height.
3798 // NOTE: We treat "min-height:auto" as "0" for the purpose of this code,
3799 // since that's what it means in all cases except for on flex items -- and
3800 // even there, we're supposed to ignore it (i.e. treat it as 0) until the
3801 // flex container explicitly considers it.
3802 const nsStyleCoord
&styleHeight
= stylePos
->mHeight
;
3803 const nsStyleCoord
&styleMinHeight
= stylePos
->mMinHeight
;
3804 const nsStyleCoord
&styleMaxHeight
= stylePos
->mMaxHeight
;
3806 if (styleHeight
.GetUnit() != eStyleUnit_Auto
||
3807 !(styleMinHeight
.GetUnit() == eStyleUnit_Auto
||
3808 (styleMinHeight
.GetUnit() == eStyleUnit_Coord
&&
3809 styleMinHeight
.GetCoordValue() == 0)) ||
3810 styleMaxHeight
.GetUnit() != eStyleUnit_None
) {
3812 nsSize ratio
= aFrame
->GetIntrinsicRatio();
3814 if (ratio
.height
!= 0) {
3815 nscoord heightTakenByBoxSizing
= 0;
3816 switch (boxSizing
) {
3817 case NS_STYLE_BOX_SIZING_BORDER
: {
3818 const nsStyleBorder
* styleBorder
= aFrame
->StyleBorder();
3819 heightTakenByBoxSizing
+=
3820 styleBorder
->GetComputedBorder().TopBottom();
3823 case NS_STYLE_BOX_SIZING_PADDING
: {
3824 if (!(aFlags
& IGNORE_PADDING
)) {
3825 const nsStylePadding
* stylePadding
= aFrame
->StylePadding();
3827 if (GetAbsoluteCoord(stylePadding
->mPadding
.GetTop(), pad
) ||
3828 GetPercentHeight(stylePadding
->mPadding
.GetTop(), aFrame
, pad
)) {
3829 heightTakenByBoxSizing
+= pad
;
3831 if (GetAbsoluteCoord(stylePadding
->mPadding
.GetBottom(), pad
) ||
3832 GetPercentHeight(stylePadding
->mPadding
.GetBottom(), aFrame
, pad
)) {
3833 heightTakenByBoxSizing
+= pad
;
3838 case NS_STYLE_BOX_SIZING_CONTENT
:
3844 if (GetAbsoluteCoord(styleHeight
, h
) ||
3845 GetPercentHeight(styleHeight
, aFrame
, h
)) {
3846 h
= std::max(0, h
- heightTakenByBoxSizing
);
3847 result
= NSCoordMulDiv(h
, ratio
.width
, ratio
.height
);
3850 if (GetAbsoluteCoord(styleMaxHeight
, h
) ||
3851 GetPercentHeight(styleMaxHeight
, aFrame
, h
)) {
3852 h
= std::max(0, h
- heightTakenByBoxSizing
);
3853 nscoord maxWidth
= NSCoordMulDiv(h
, ratio
.width
, ratio
.height
);
3854 if (maxWidth
< result
)
3858 if (GetAbsoluteCoord(styleMinHeight
, h
) ||
3859 GetPercentHeight(styleMinHeight
, aFrame
, h
)) {
3860 h
= std::max(0, h
- heightTakenByBoxSizing
);
3861 nscoord minWidth
= NSCoordMulDiv(h
, ratio
.width
, ratio
.height
);
3862 if (minWidth
> result
)
3869 if (aFrame
->GetType() == nsGkAtoms::tableFrame
) {
3870 // Tables can't shrink smaller than their intrinsic minimum width,
3872 min
= aFrame
->GetMinISize(aRenderingContext
);
3875 // We also need to track what has been added on outside of the box
3876 // (controlled by 'box-sizing') where 'width', 'min-width' and
3877 // 'max-width' are applied. We have to account for these properties
3878 // after getting all the offsets (margin, border, padding) because
3879 // percentages do not operate linearly.
3880 // Doing this is ok because although percentages aren't handled
3881 // linearly, they are handled monotonically.
3882 nscoord coordOutsideWidth
= 0;
3883 float pctOutsideWidth
= 0;
3884 float pctTotal
= 0.0f
;
3886 if (!(aFlags
& IGNORE_PADDING
)) {
3887 coordOutsideWidth
+= offsets
.hPadding
;
3888 pctOutsideWidth
+= offsets
.hPctPadding
;
3890 if (boxSizing
== NS_STYLE_BOX_SIZING_PADDING
) {
3891 min
+= coordOutsideWidth
;
3892 result
= NSCoordSaturatingAdd(result
, coordOutsideWidth
);
3893 pctTotal
+= pctOutsideWidth
;
3895 coordOutsideWidth
= 0;
3896 pctOutsideWidth
= 0.0f
;
3900 coordOutsideWidth
+= offsets
.hBorder
;
3902 if (boxSizing
== NS_STYLE_BOX_SIZING_BORDER
) {
3903 min
+= coordOutsideWidth
;
3904 result
= NSCoordSaturatingAdd(result
, coordOutsideWidth
);
3905 pctTotal
+= pctOutsideWidth
;
3907 coordOutsideWidth
= 0;
3908 pctOutsideWidth
= 0.0f
;
3911 coordOutsideWidth
+= offsets
.hMargin
;
3912 pctOutsideWidth
+= offsets
.hPctMargin
;
3914 min
+= coordOutsideWidth
;
3915 result
= NSCoordSaturatingAdd(result
, coordOutsideWidth
);
3916 pctTotal
+= pctOutsideWidth
;
3919 if (GetAbsoluteCoord(styleWidth
, w
) ||
3920 GetIntrinsicCoord(styleWidth
, aRenderingContext
, aFrame
,
3922 result
= AddPercents(aType
, w
+ coordOutsideWidth
, pctOutsideWidth
);
3924 else if (aType
== MIN_ISIZE
&&
3925 // The only cases of coord-percent-calc() units that
3926 // GetAbsoluteCoord didn't handle are percent and calc()s
3927 // containing percent.
3928 styleWidth
.IsCoordPercentCalcUnit() &&
3929 aFrame
->IsFrameOfType(nsIFrame::eReplaced
)) {
3930 // A percentage width on replaced elements means they can shrink to 0.
3931 result
= 0; // let |min| handle padding/border/margin
3934 // NOTE: We could really do a lot better for percents and for some
3935 // cases of calc() containing percent (certainly including any where
3936 // the coefficient on the percent is positive and there are no max()
3937 // expressions). However, doing better for percents wouldn't be
3938 // backwards compatible.
3939 result
= AddPercents(aType
, result
, pctTotal
);
3942 if (haveFixedMaxWidth
||
3943 GetIntrinsicCoord(styleMaxWidth
, aRenderingContext
, aFrame
,
3944 PROP_MAX_WIDTH
, maxw
)) {
3945 maxw
= AddPercents(aType
, maxw
+ coordOutsideWidth
, pctOutsideWidth
);
3950 if (haveFixedMinWidth
||
3951 GetIntrinsicCoord(styleMinWidth
, aRenderingContext
, aFrame
,
3952 PROP_MIN_WIDTH
, minw
)) {
3953 minw
= AddPercents(aType
, minw
+ coordOutsideWidth
, pctOutsideWidth
);
3958 min
= AddPercents(aType
, min
, pctTotal
);
3962 const nsStyleDisplay
*disp
= aFrame
->StyleDisplay();
3963 if (aFrame
->IsThemed(disp
)) {
3964 nsIntSize
size(0, 0);
3965 bool canOverride
= true;
3966 nsPresContext
*presContext
= aFrame
->PresContext();
3967 presContext
->GetTheme()->
3968 GetMinimumWidgetSize(presContext
, aFrame
, disp
->mAppearance
,
3969 &size
, &canOverride
);
3971 nscoord themeWidth
= presContext
->DevPixelsToAppUnits(size
.width
);
3973 // GMWS() returns a border-box width
3974 themeWidth
+= offsets
.hMargin
;
3975 themeWidth
= AddPercents(aType
, themeWidth
, offsets
.hPctMargin
);
3977 if (themeWidth
> result
|| !canOverride
)
3978 result
= themeWidth
;
3981 #ifdef DEBUG_INTRINSIC_WIDTH
3982 nsFrame::IndentBy(stderr
, gNoiseIndent
);
3983 static_cast<nsFrame
*>(aFrame
)->ListTag(stderr
);
3984 printf_stderr(" %s intrinsic width for container is %d twips.\n",
3985 aType
== MIN_ISIZE
? "min" : "pref", result
);
3991 /* static */ nscoord
3992 nsLayoutUtils::ComputeCBDependentValue(nscoord aPercentBasis
,
3993 const nsStyleCoord
& aCoord
)
3995 NS_WARN_IF_FALSE(aPercentBasis
!= NS_UNCONSTRAINEDSIZE
,
3996 "have unconstrained width or height; this should only "
3997 "result from very large sizes, not attempts at intrinsic "
3998 "size calculation");
4000 if (aCoord
.IsCoordPercentCalcUnit()) {
4001 return nsRuleNode::ComputeCoordPercentCalc(aCoord
, aPercentBasis
);
4003 NS_ASSERTION(aCoord
.GetUnit() == eStyleUnit_None
||
4004 aCoord
.GetUnit() == eStyleUnit_Auto
,
4005 "unexpected width value");
4009 /* static */ nscoord
4010 nsLayoutUtils::ComputeWidthValue(
4011 nsRenderingContext
* aRenderingContext
,
4013 nscoord aContainingBlockWidth
,
4014 nscoord aContentEdgeToBoxSizing
,
4015 nscoord aBoxSizingToMarginEdge
,
4016 const nsStyleCoord
& aCoord
)
4018 NS_PRECONDITION(aFrame
, "non-null frame expected");
4019 NS_PRECONDITION(aRenderingContext
, "non-null rendering context expected");
4020 NS_WARN_IF_FALSE(aContainingBlockWidth
!= NS_UNCONSTRAINEDSIZE
,
4021 "have unconstrained width; this should only result from "
4022 "very large sizes, not attempts at intrinsic width "
4024 NS_PRECONDITION(aContainingBlockWidth
>= 0,
4025 "width less than zero");
4028 if (aCoord
.IsCoordPercentCalcUnit()) {
4029 result
= nsRuleNode::ComputeCoordPercentCalc(aCoord
,
4030 aContainingBlockWidth
);
4031 // The result of a calc() expression might be less than 0; we
4032 // should clamp at runtime (below). (Percentages and coords that
4033 // are less than 0 have already been dropped by the parser.)
4034 result
-= aContentEdgeToBoxSizing
;
4036 MOZ_ASSERT(eStyleUnit_Enumerated
== aCoord
.GetUnit());
4037 // If aFrame is a container for font size inflation, then shrink
4038 // wrapping inside of it should not apply font size inflation.
4039 AutoMaybeDisableFontInflation
an(aFrame
);
4041 int32_t val
= aCoord
.GetIntValue();
4043 case NS_STYLE_WIDTH_MAX_CONTENT
:
4044 result
= aFrame
->GetPrefISize(aRenderingContext
);
4045 NS_ASSERTION(result
>= 0, "width less than zero");
4047 case NS_STYLE_WIDTH_MIN_CONTENT
:
4048 result
= aFrame
->GetMinISize(aRenderingContext
);
4049 NS_ASSERTION(result
>= 0, "width less than zero");
4051 case NS_STYLE_WIDTH_FIT_CONTENT
:
4053 nscoord pref
= aFrame
->GetPrefISize(aRenderingContext
),
4054 min
= aFrame
->GetMinISize(aRenderingContext
),
4055 fill
= aContainingBlockWidth
-
4056 (aBoxSizingToMarginEdge
+ aContentEdgeToBoxSizing
);
4057 result
= std::max(min
, std::min(pref
, fill
));
4058 NS_ASSERTION(result
>= 0, "width less than zero");
4061 case NS_STYLE_WIDTH_AVAILABLE
:
4062 result
= aContainingBlockWidth
-
4063 (aBoxSizingToMarginEdge
+ aContentEdgeToBoxSizing
);
4067 return std::max(0, result
);
4070 /* static */ nscoord
4071 nsLayoutUtils::ComputeHeightDependentValue(
4072 nscoord aContainingBlockHeight
,
4073 const nsStyleCoord
& aCoord
)
4075 // XXXldb Some callers explicitly check aContainingBlockHeight
4076 // against NS_AUTOHEIGHT *and* unit against eStyleUnit_Percent or
4077 // calc()s containing percents before calling this function.
4078 // However, it would be much more likely to catch problems without
4079 // the unit conditions.
4080 // XXXldb Many callers pass a non-'auto' containing block height when
4081 // according to CSS2.1 they should be passing 'auto'.
4082 NS_PRECONDITION(NS_AUTOHEIGHT
!= aContainingBlockHeight
||
4083 !aCoord
.HasPercent(),
4084 "unexpected containing block height");
4086 if (aCoord
.IsCoordPercentCalcUnit()) {
4087 return nsRuleNode::ComputeCoordPercentCalc(aCoord
, aContainingBlockHeight
);
4090 NS_ASSERTION(aCoord
.GetUnit() == eStyleUnit_None
||
4091 aCoord
.GetUnit() == eStyleUnit_Auto
,
4092 "unexpected height value");
4097 nsLayoutUtils::MarkDescendantsDirty(nsIFrame
*aSubtreeRoot
)
4099 nsAutoTArray
<nsIFrame
*, 4> subtrees
;
4100 subtrees
.AppendElement(aSubtreeRoot
);
4102 // dirty descendants, iterating over subtrees that may include
4103 // additional subtrees associated with placeholders
4105 nsIFrame
*subtreeRoot
= subtrees
.ElementAt(subtrees
.Length() - 1);
4106 subtrees
.RemoveElementAt(subtrees
.Length() - 1);
4108 // Mark all descendants dirty (using an nsTArray stack rather than
4110 // Note that nsHTMLReflowState::InitResizeFlags has some similar
4111 // code; see comments there for how and why it differs.
4112 nsAutoTArray
<nsIFrame
*, 32> stack
;
4113 stack
.AppendElement(subtreeRoot
);
4116 nsIFrame
*f
= stack
.ElementAt(stack
.Length() - 1);
4117 stack
.RemoveElementAt(stack
.Length() - 1);
4119 f
->MarkIntrinsicISizesDirty();
4121 if (f
->GetType() == nsGkAtoms::placeholderFrame
) {
4122 nsIFrame
*oof
= nsPlaceholderFrame::GetRealFrameForPlaceholder(f
);
4123 if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot
, oof
)) {
4124 // We have another distinct subtree we need to mark.
4125 subtrees
.AppendElement(oof
);
4129 nsIFrame::ChildListIterator
lists(f
);
4130 for (; !lists
.IsDone(); lists
.Next()) {
4131 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
4132 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
4133 nsIFrame
* kid
= childFrames
.get();
4134 stack
.AppendElement(kid
);
4137 } while (stack
.Length() != 0);
4138 } while (subtrees
.Length() != 0);
4143 nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(WritingMode aWM
,
4144 nsRenderingContext
* aRenderingContext
, nsIFrame
* aFrame
,
4145 const IntrinsicSize
& aIntrinsicSize
,
4146 nsSize aIntrinsicRatio
,
4147 const mozilla::LogicalSize
& aCBSize
,
4148 const mozilla::LogicalSize
& aMargin
,
4149 const mozilla::LogicalSize
& aBorder
,
4150 const mozilla::LogicalSize
& aPadding
)
4152 const nsStylePosition
* stylePos
= aFrame
->StylePosition();
4154 // If we're a flex item, we'll compute our size a bit differently.
4155 const nsStyleCoord
* inlineStyleCoord
=
4156 aWM
.IsVertical() ? &stylePos
->mHeight
: &stylePos
->mWidth
;
4157 const nsStyleCoord
* blockStyleCoord
=
4158 aWM
.IsVertical() ? &stylePos
->mWidth
: &stylePos
->mHeight
;
4160 bool isFlexItem
= aFrame
->IsFlexItem();
4161 bool isInlineFlexItem
= false;
4164 // Flex items use their "flex-basis" property in place of their main-size
4165 // property (e.g. "width") for sizing purposes, *unless* they have
4166 // "flex-basis:auto", in which case they use their main-size property after
4168 uint32_t flexDirection
=
4169 aFrame
->GetParent()->StylePosition()->mFlexDirection
;
4171 flexDirection
== NS_STYLE_FLEX_DIRECTION_ROW
||
4172 flexDirection
== NS_STYLE_FLEX_DIRECTION_ROW_REVERSE
;
4174 // NOTE: The logic here should match the similar chunk for determining
4175 // inlineStyleCoord and blockStyleCoord in nsFrame::ComputeSize().
4176 const nsStyleCoord
* flexBasis
= &(stylePos
->mFlexBasis
);
4177 if (flexBasis
->GetUnit() != eStyleUnit_Auto
) {
4178 if (isInlineFlexItem
) {
4179 inlineStyleCoord
= flexBasis
;
4181 // One caveat for vertical flex items: We don't support enumerated
4182 // values (e.g. "max-content") for height properties yet. So, if our
4183 // computed flex-basis is an enumerated value, we'll just behave as if
4184 // it were "auto", which means "use the main-size property after all"
4185 // (which is "height", in this case).
4186 // NOTE: Once we support intrinsic sizing keywords for "height",
4187 // we should remove this check.
4188 if (flexBasis
->GetUnit() != eStyleUnit_Enumerated
) {
4189 blockStyleCoord
= flexBasis
;
4195 // Handle intrinsic sizes and their interaction with
4196 // {min-,max-,}{width,height} according to the rules in
4197 // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
4199 // Note: throughout the following section of the function, I avoid
4200 // a * (b / c) because of its reduced accuracy relative to a * b / c
4201 // or (a * b) / c (which are equivalent).
4203 const bool isAutoISize
= inlineStyleCoord
->GetUnit() == eStyleUnit_Auto
;
4204 const bool isAutoBSize
= IsAutoHeight(*blockStyleCoord
, aCBSize
.BSize(aWM
));
4206 LogicalSize
boxSizingAdjust(aWM
);
4207 switch (stylePos
->mBoxSizing
) {
4208 case NS_STYLE_BOX_SIZING_BORDER
:
4209 boxSizingAdjust
+= aBorder
;
4211 case NS_STYLE_BOX_SIZING_PADDING
:
4212 boxSizingAdjust
+= aPadding
;
4214 nscoord boxSizingToMarginEdgeISize
=
4215 aMargin
.ISize(aWM
) + aBorder
.ISize(aWM
) + aPadding
.ISize(aWM
) -
4216 boxSizingAdjust
.ISize(aWM
);
4218 nscoord iSize
, minISize
, maxISize
, bSize
, minBSize
, maxBSize
;
4221 iSize
= nsLayoutUtils::ComputeWidthValue(aRenderingContext
,
4222 aFrame
, aCBSize
.ISize(aWM
), boxSizingAdjust
.ISize(aWM
),
4223 boxSizingToMarginEdgeISize
, *inlineStyleCoord
);
4226 if (stylePos
->mMaxWidth
.GetUnit() != eStyleUnit_None
&&
4227 !(isFlexItem
&& isInlineFlexItem
)) {
4228 maxISize
= nsLayoutUtils::ComputeWidthValue(aRenderingContext
,
4229 aFrame
, aCBSize
.ISize(aWM
), boxSizingAdjust
.ISize(aWM
),
4230 boxSizingToMarginEdgeISize
, stylePos
->mMaxWidth
);
4232 maxISize
= nscoord_MAX
;
4235 // NOTE: Flex items ignore their min & max sizing properties in their
4236 // flex container's main-axis. (Those properties get applied later in
4237 // the flexbox algorithm.)
4238 if (stylePos
->mMinWidth
.GetUnit() != eStyleUnit_Auto
&&
4239 !(isFlexItem
&& isInlineFlexItem
)) {
4240 minISize
= nsLayoutUtils::ComputeWidthValue(aRenderingContext
,
4241 aFrame
, aCBSize
.ISize(aWM
), boxSizingAdjust
.ISize(aWM
),
4242 boxSizingToMarginEdgeISize
, stylePos
->mMinWidth
);
4244 // Treat "min-width: auto" as 0.
4245 // NOTE: Technically, "auto" is supposed to behave like "min-content" on
4246 // flex items. However, we don't need to worry about that here, because
4247 // flex items' min-sizes are intentionally ignored until the flex
4248 // container explicitly considers them during space distribution.
4253 bSize
= nsLayoutUtils::ComputeHeightValue(aCBSize
.BSize(aWM
),
4254 boxSizingAdjust
.BSize(aWM
),
4258 if (!IsAutoHeight(stylePos
->mMaxHeight
, aCBSize
.BSize(aWM
)) &&
4259 !(isFlexItem
&& !isInlineFlexItem
)) {
4260 maxBSize
= nsLayoutUtils::ComputeHeightValue(aCBSize
.BSize(aWM
),
4261 boxSizingAdjust
.BSize(aWM
),
4262 stylePos
->mMaxHeight
);
4264 maxBSize
= nscoord_MAX
;
4267 if (!IsAutoHeight(stylePos
->mMinHeight
, aCBSize
.BSize(aWM
)) &&
4268 !(isFlexItem
&& !isInlineFlexItem
)) {
4269 minBSize
= nsLayoutUtils::ComputeHeightValue(aCBSize
.BSize(aWM
),
4270 boxSizingAdjust
.BSize(aWM
),
4271 stylePos
->mMinHeight
);
4276 // Resolve percentage intrinsic iSize/bSize as necessary:
4278 NS_ASSERTION(aCBSize
.ISize(aWM
) != NS_UNCONSTRAINEDSIZE
,
4279 "Our containing block must not have unconstrained inline-size!");
4281 const nsStyleCoord
& isizeCoord(aWM
.IsVertical() ?
4282 aIntrinsicSize
.height
: aIntrinsicSize
.width
);
4283 const nsStyleCoord
& bsizeCoord(aWM
.IsVertical() ?
4284 aIntrinsicSize
.width
: aIntrinsicSize
.height
);
4286 bool hasIntrinsicISize
, hasIntrinsicBSize
;
4287 nscoord intrinsicISize
, intrinsicBSize
;
4289 if (isizeCoord
.GetUnit() == eStyleUnit_Coord
) {
4290 hasIntrinsicISize
= true;
4291 intrinsicISize
= isizeCoord
.GetCoordValue();
4292 if (intrinsicISize
< 0)
4295 NS_ASSERTION(isizeCoord
.GetUnit() == eStyleUnit_None
,
4297 hasIntrinsicISize
= false;
4301 if (bsizeCoord
.GetUnit() == eStyleUnit_Coord
) {
4302 hasIntrinsicBSize
= true;
4303 intrinsicBSize
= bsizeCoord
.GetCoordValue();
4304 if (intrinsicBSize
< 0)
4307 NS_ASSERTION(bsizeCoord
.GetUnit() == eStyleUnit_None
,
4309 hasIntrinsicBSize
= false;
4313 NS_ASSERTION(aIntrinsicRatio
.width
>= 0 && aIntrinsicRatio
.height
>= 0,
4314 "Intrinsic ratio has a negative component!");
4315 LogicalSize
logicalRatio(aWM
, aIntrinsicRatio
);
4317 // Now calculate the used values for iSize and bSize:
4322 // 'auto' iSize, 'auto' bSize
4324 // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2:
4326 nscoord tentISize
, tentBSize
;
4328 if (hasIntrinsicISize
) {
4329 tentISize
= intrinsicISize
;
4330 } else if (hasIntrinsicBSize
&& logicalRatio
.BSize(aWM
) > 0) {
4331 tentISize
= NSCoordMulDiv(intrinsicBSize
, logicalRatio
.ISize(aWM
), logicalRatio
.BSize(aWM
));
4332 } else if (logicalRatio
.ISize(aWM
) > 0) {
4333 tentISize
= aCBSize
.ISize(aWM
) - boxSizingToMarginEdgeISize
; // XXX scrollbar?
4334 if (tentISize
< 0) tentISize
= 0;
4336 tentISize
= nsPresContext::CSSPixelsToAppUnits(300);
4339 if (hasIntrinsicBSize
) {
4340 tentBSize
= intrinsicBSize
;
4341 } else if (logicalRatio
.ISize(aWM
) > 0) {
4342 tentBSize
= NSCoordMulDiv(tentISize
, logicalRatio
.BSize(aWM
), logicalRatio
.ISize(aWM
));
4344 tentBSize
= nsPresContext::CSSPixelsToAppUnits(150);
4348 ComputeAutoSizeWithIntrinsicDimensions(minISize
, minBSize
,
4350 tentISize
, tentBSize
);
4351 // The nsSize that ComputeAutoSizeWithIntrinsicDimensions returns will
4352 // actually contain logical values if the parameters passed to it were
4353 // logical coordinates, so we do NOT perform a physical-to-logical
4354 // conversion here, but just assign the fields directly to our result.
4355 return LogicalSize(aWM
, autoSize
.width
, autoSize
.height
);
4358 // 'auto' iSize, non-'auto' bSize
4359 bSize
= NS_CSS_MINMAX(bSize
, minBSize
, maxBSize
);
4360 if (logicalRatio
.BSize(aWM
) > 0) {
4361 iSize
= NSCoordMulDiv(bSize
, logicalRatio
.ISize(aWM
), logicalRatio
.BSize(aWM
));
4362 } else if (hasIntrinsicISize
) {
4363 iSize
= intrinsicISize
;
4365 iSize
= nsPresContext::CSSPixelsToAppUnits(300);
4367 iSize
= NS_CSS_MINMAX(iSize
, minISize
, maxISize
);
4373 // non-'auto' iSize, 'auto' bSize
4374 iSize
= NS_CSS_MINMAX(iSize
, minISize
, maxISize
);
4375 if (logicalRatio
.ISize(aWM
) > 0) {
4376 bSize
= NSCoordMulDiv(iSize
, logicalRatio
.BSize(aWM
), logicalRatio
.ISize(aWM
));
4377 } else if (hasIntrinsicBSize
) {
4378 bSize
= intrinsicBSize
;
4380 bSize
= nsPresContext::CSSPixelsToAppUnits(150);
4382 bSize
= NS_CSS_MINMAX(bSize
, minBSize
, maxBSize
);
4386 // non-'auto' iSize, non-'auto' bSize
4387 iSize
= NS_CSS_MINMAX(iSize
, minISize
, maxISize
);
4388 bSize
= NS_CSS_MINMAX(bSize
, minBSize
, maxBSize
);
4393 return LogicalSize(aWM
, iSize
, bSize
);
4397 nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(nscoord minWidth
, nscoord minHeight
,
4398 nscoord maxWidth
, nscoord maxHeight
,
4399 nscoord tentWidth
, nscoord tentHeight
)
4401 // Now apply min/max-width/height - CSS 2.1 sections 10.4 and 10.7:
4403 if (minWidth
> maxWidth
)
4404 maxWidth
= minWidth
;
4405 if (minHeight
> maxHeight
)
4406 maxHeight
= minHeight
;
4408 nscoord heightAtMaxWidth
, heightAtMinWidth
,
4409 widthAtMaxHeight
, widthAtMinHeight
;
4411 if (tentWidth
> 0) {
4412 heightAtMaxWidth
= NSCoordMulDiv(maxWidth
, tentHeight
, tentWidth
);
4413 if (heightAtMaxWidth
< minHeight
)
4414 heightAtMaxWidth
= minHeight
;
4415 heightAtMinWidth
= NSCoordMulDiv(minWidth
, tentHeight
, tentWidth
);
4416 if (heightAtMinWidth
> maxHeight
)
4417 heightAtMinWidth
= maxHeight
;
4419 heightAtMaxWidth
= heightAtMinWidth
= NS_CSS_MINMAX(tentHeight
, minHeight
, maxHeight
);
4422 if (tentHeight
> 0) {
4423 widthAtMaxHeight
= NSCoordMulDiv(maxHeight
, tentWidth
, tentHeight
);
4424 if (widthAtMaxHeight
< minWidth
)
4425 widthAtMaxHeight
= minWidth
;
4426 widthAtMinHeight
= NSCoordMulDiv(minHeight
, tentWidth
, tentHeight
);
4427 if (widthAtMinHeight
> maxWidth
)
4428 widthAtMinHeight
= maxWidth
;
4430 widthAtMaxHeight
= widthAtMinHeight
= NS_CSS_MINMAX(tentWidth
, minWidth
, maxWidth
);
4433 // The table at http://www.w3.org/TR/CSS21/visudet.html#min-max-widths :
4435 nscoord width
, height
;
4437 if (tentWidth
> maxWidth
) {
4438 if (tentHeight
> maxHeight
) {
4439 if (int64_t(maxWidth
) * int64_t(tentHeight
) <=
4440 int64_t(maxHeight
) * int64_t(tentWidth
)) {
4442 height
= heightAtMaxWidth
;
4444 width
= widthAtMaxHeight
;
4448 // This also covers "(w > max-width) and (h < min-height)" since in
4449 // that case (max-width/w < 1), and with (h < min-height):
4450 // max(max-width * h/w, min-height) == min-height
4452 height
= heightAtMaxWidth
;
4454 } else if (tentWidth
< minWidth
) {
4455 if (tentHeight
< minHeight
) {
4456 if (int64_t(minWidth
) * int64_t(tentHeight
) <=
4457 int64_t(minHeight
) * int64_t(tentWidth
)) {
4458 width
= widthAtMinHeight
;
4462 height
= heightAtMinWidth
;
4465 // This also covers "(w < min-width) and (h > max-height)" since in
4466 // that case (min-width/w > 1), and with (h > max-height):
4467 // min(min-width * h/w, max-height) == max-height
4469 height
= heightAtMinWidth
;
4472 if (tentHeight
> maxHeight
) {
4473 width
= widthAtMaxHeight
;
4475 } else if (tentHeight
< minHeight
) {
4476 width
= widthAtMinHeight
;
4480 height
= tentHeight
;
4484 return nsSize(width
, height
);
4487 /* static */ nscoord
4488 nsLayoutUtils::MinISizeFromInline(nsIFrame
* aFrame
,
4489 nsRenderingContext
* aRenderingContext
)
4491 NS_ASSERTION(!aFrame
->IsContainerForFontSizeInflation(),
4492 "should not be container for font size inflation");
4494 nsIFrame::InlineMinISizeData data
;
4495 DISPLAY_MIN_WIDTH(aFrame
, data
.prevLines
);
4496 aFrame
->AddInlineMinISize(aRenderingContext
, &data
);
4497 data
.ForceBreak(aRenderingContext
);
4498 return data
.prevLines
;
4501 /* static */ nscoord
4502 nsLayoutUtils::PrefISizeFromInline(nsIFrame
* aFrame
,
4503 nsRenderingContext
* aRenderingContext
)
4505 NS_ASSERTION(!aFrame
->IsContainerForFontSizeInflation(),
4506 "should not be container for font size inflation");
4508 nsIFrame::InlinePrefISizeData data
;
4509 DISPLAY_PREF_WIDTH(aFrame
, data
.prevLines
);
4510 aFrame
->AddInlinePrefISize(aRenderingContext
, &data
);
4511 data
.ForceBreak(aRenderingContext
);
4512 return data
.prevLines
;
4516 DarkenColor(nscolor aColor
)
4518 uint16_t hue
, sat
, value
;
4521 // convert the RBG to HSV so we can get the lightness (which is the v)
4522 NS_RGB2HSV(aColor
, hue
, sat
, value
, alpha
);
4524 // The goal here is to send white to black while letting colored
4525 // stuff stay colored... So we adopt the following approach.
4526 // Something with sat = 0 should end up with value = 0. Something
4527 // with a high sat can end up with a high value and it's ok.... At
4528 // the same time, we don't want to make things lighter. Do
4529 // something simple, since it seems to work.
4532 // convert this color back into the RGB color space.
4533 NS_HSV2RGB(aColor
, hue
, sat
, value
, alpha
);
4538 // Check whether we should darken text/decoration colors. We need to do this if
4539 // background images and colors are being suppressed, because that means
4540 // light text will not be visible against the (presumed light-colored) background.
4542 ShouldDarkenColors(nsPresContext
* aPresContext
)
4544 return !aPresContext
->GetBackgroundColorDraw() &&
4545 !aPresContext
->GetBackgroundImageDraw();
4549 nsLayoutUtils::GetColor(nsIFrame
* aFrame
, nsCSSProperty aProperty
)
4551 nscolor color
= aFrame
->GetVisitedDependentColor(aProperty
);
4552 if (ShouldDarkenColors(aFrame
->PresContext())) {
4553 color
= DarkenColor(color
);
4559 nsLayoutUtils::GetSnappedBaselineY(nsIFrame
* aFrame
, gfxContext
* aContext
,
4560 nscoord aY
, nscoord aAscent
)
4562 gfxFloat appUnitsPerDevUnit
= aFrame
->PresContext()->AppUnitsPerDevPixel();
4563 gfxFloat baseline
= gfxFloat(aY
) + aAscent
;
4564 gfxRect
putativeRect(0, baseline
/appUnitsPerDevUnit
, 1, 1);
4565 if (!aContext
->UserToDevicePixelSnapped(putativeRect
, true))
4567 return aContext
->DeviceToUser(putativeRect
.TopLeft()).y
* appUnitsPerDevUnit
;
4571 nsLayoutUtils::DrawString(const nsIFrame
* aFrame
,
4572 nsRenderingContext
* aContext
,
4573 const char16_t
* aString
,
4576 nsStyleContext
* aStyleContext
)
4578 nsresult rv
= NS_ERROR_FAILURE
;
4579 nsPresContext
* presContext
= aFrame
->PresContext();
4580 if (presContext
->BidiEnabled()) {
4582 nsBidiPresUtils::BidiLevelFromStyle(aStyleContext
?
4583 aStyleContext
: aFrame
->StyleContext());
4584 rv
= nsBidiPresUtils::RenderText(aString
, aLength
, level
,
4585 presContext
, *aContext
, *aContext
,
4586 aPoint
.x
, aPoint
.y
);
4590 aContext
->SetTextRunRTL(false);
4591 aContext
->DrawString(aString
, aLength
, aPoint
.x
, aPoint
.y
);
4596 nsLayoutUtils::GetStringWidth(const nsIFrame
* aFrame
,
4597 nsRenderingContext
* aContext
,
4598 const char16_t
* aString
,
4601 nsPresContext
* presContext
= aFrame
->PresContext();
4602 if (presContext
->BidiEnabled()) {
4604 nsBidiPresUtils::BidiLevelFromStyle(aFrame
->StyleContext());
4605 return nsBidiPresUtils::MeasureTextWidth(aString
, aLength
,
4606 level
, presContext
, *aContext
);
4608 aContext
->SetTextRunRTL(false);
4609 return aContext
->GetWidth(aString
, aLength
);
4613 nsLayoutUtils::PaintTextShadow(const nsIFrame
* aFrame
,
4614 nsRenderingContext
* aContext
,
4615 const nsRect
& aTextRect
,
4616 const nsRect
& aDirtyRect
,
4617 const nscolor
& aForegroundColor
,
4618 TextShadowCallback aCallback
,
4619 void* aCallbackData
)
4621 const nsStyleText
* textStyle
= aFrame
->StyleText();
4622 if (!textStyle
->HasTextShadow())
4625 // Text shadow happens with the last value being painted at the back,
4626 // ie. it is painted first.
4627 gfxContext
* aDestCtx
= aContext
->ThebesContext();
4628 for (uint32_t i
= textStyle
->mTextShadow
->Length(); i
> 0; --i
) {
4629 nsCSSShadowItem
* shadowDetails
= textStyle
->mTextShadow
->ShadowAt(i
- 1);
4630 nsPoint
shadowOffset(shadowDetails
->mXOffset
,
4631 shadowDetails
->mYOffset
);
4632 nscoord blurRadius
= std::max(shadowDetails
->mRadius
, 0);
4634 nsRect
shadowRect(aTextRect
);
4635 shadowRect
.MoveBy(shadowOffset
);
4637 nsPresContext
* presCtx
= aFrame
->PresContext();
4638 nsContextBoxBlur contextBoxBlur
;
4639 gfxContext
* shadowContext
= contextBoxBlur
.Init(shadowRect
, 0, blurRadius
,
4640 presCtx
->AppUnitsPerDevPixel(),
4641 aDestCtx
, aDirtyRect
, nullptr);
4645 nscolor shadowColor
;
4646 if (shadowDetails
->mHasColor
)
4647 shadowColor
= shadowDetails
->mColor
;
4649 shadowColor
= aForegroundColor
;
4651 // Conjure an nsRenderingContext from a gfxContext for drawing the text
4653 nsRefPtr
<nsRenderingContext
> renderingContext
= new nsRenderingContext();
4654 renderingContext
->Init(presCtx
->DeviceContext(), shadowContext
);
4657 aDestCtx
->NewPath();
4658 aDestCtx
->SetColor(gfxRGBA(shadowColor
));
4660 // The callback will draw whatever we want to blur as a shadow.
4661 aCallback(renderingContext
, shadowOffset
, shadowColor
, aCallbackData
);
4663 contextBoxBlur
.DoPaint();
4664 aDestCtx
->Restore();
4668 /* static */ nscoord
4669 nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics
* aFontMetrics
,
4670 nscoord aLineHeight
)
4672 nscoord fontAscent
= aFontMetrics
->MaxAscent();
4673 nscoord fontHeight
= aFontMetrics
->MaxHeight();
4675 nscoord leading
= aLineHeight
- fontHeight
;
4676 return fontAscent
+ leading
/2;
4681 nsLayoutUtils::GetFirstLineBaseline(WritingMode aWritingMode
,
4682 const nsIFrame
* aFrame
, nscoord
* aResult
)
4684 LinePosition position
;
4685 if (!GetFirstLinePosition(aWritingMode
, aFrame
, &position
))
4687 *aResult
= position
.mBaseline
;
4692 nsLayoutUtils::GetFirstLinePosition(WritingMode aWM
,
4693 const nsIFrame
* aFrame
,
4694 LinePosition
* aResult
)
4696 const nsBlockFrame
* block
= nsLayoutUtils::GetAsBlock(const_cast<nsIFrame
*>(aFrame
));
4698 // For the first-line baseline we also have to check for a table, and if
4699 // so, use the baseline of its first row.
4700 nsIAtom
* fType
= aFrame
->GetType();
4701 if (fType
== nsGkAtoms::tableOuterFrame
) {
4702 aResult
->mBStart
= 0;
4703 aResult
->mBaseline
= aFrame
->GetLogicalBaseline(aWM
);
4704 // This is what we want for the list bullet caller; not sure if
4705 // other future callers will want the same.
4706 aResult
->mBEnd
= aFrame
->BSize(aWM
);
4710 // For first-line baselines, we have to consider scroll frames.
4711 if (fType
== nsGkAtoms::scrollFrame
) {
4712 nsIScrollableFrame
*sFrame
= do_QueryFrame(const_cast<nsIFrame
*>(aFrame
));
4714 NS_NOTREACHED("not scroll frame");
4716 LinePosition kidPosition
;
4717 if (GetFirstLinePosition(aWM
,
4718 sFrame
->GetScrolledFrame(), &kidPosition
)) {
4719 // Consider only the border and padding that contributes to the
4720 // kid's position, not the scrolling, so we get the initial
4722 *aResult
= kidPosition
+
4723 aFrame
->GetLogicalUsedBorderAndPadding(aWM
).BStart(aWM
);
4729 if (fType
== nsGkAtoms::fieldSetFrame
) {
4730 LinePosition kidPosition
;
4731 nsIFrame
* kid
= aFrame
->GetFirstPrincipalChild();
4732 // kid might be a legend frame here, but that's ok.
4733 if (GetFirstLinePosition(aWM
, kid
, &kidPosition
)) {
4734 *aResult
= kidPosition
+
4735 kid
->GetLogicalNormalPosition(aWM
, aFrame
->GetSize().width
).B(aWM
);
4745 for (nsBlockFrame::const_line_iterator line
= block
->begin_lines(),
4746 line_end
= block
->end_lines();
4747 line
!= line_end
; ++line
) {
4748 if (line
->IsBlock()) {
4749 nsIFrame
*kid
= line
->mFirstChild
;
4750 LinePosition kidPosition
;
4751 if (GetFirstLinePosition(aWM
, kid
, &kidPosition
)) {
4752 //XXX Not sure if this is the correct value to use for container
4753 // width here. It will only be used in vertical-rl layout,
4754 // which we don't have full support and testing for yet.
4755 nscoord containerWidth
= line
->mContainerWidth
;
4756 *aResult
= kidPosition
+
4757 kid
->GetLogicalNormalPosition(aWM
, containerWidth
).B(aWM
);
4761 // XXX Is this the right test? We have some bogus empty lines
4762 // floating around, but IsEmpty is perhaps too weak.
4763 if (line
->BSize() != 0 || !line
->IsEmpty()) {
4764 nscoord bStart
= line
->BStart();
4765 aResult
->mBStart
= bStart
;
4766 aResult
->mBaseline
= bStart
+ line
->GetLogicalAscent();
4767 aResult
->mBEnd
= bStart
+ line
->BSize();
4776 nsLayoutUtils::GetLastLineBaseline(WritingMode aWM
,
4777 const nsIFrame
* aFrame
, nscoord
* aResult
)
4779 const nsBlockFrame
* block
= nsLayoutUtils::GetAsBlock(const_cast<nsIFrame
*>(aFrame
));
4781 // No baseline. (We intentionally don't descend into scroll frames.)
4784 for (nsBlockFrame::const_reverse_line_iterator line
= block
->rbegin_lines(),
4785 line_end
= block
->rend_lines();
4786 line
!= line_end
; ++line
) {
4787 if (line
->IsBlock()) {
4788 nsIFrame
*kid
= line
->mFirstChild
;
4789 nscoord kidBaseline
;
4790 nscoord containerWidth
= line
->mContainerWidth
;
4791 if (GetLastLineBaseline(aWM
, kid
, &kidBaseline
)) {
4792 // Ignore relative positioning for baseline calculations
4793 *aResult
= kidBaseline
+
4794 kid
->GetLogicalNormalPosition(aWM
, containerWidth
).B(aWM
);
4796 } else if (kid
->GetType() == nsGkAtoms::scrollFrame
) {
4797 // Use the bottom of the scroll frame.
4798 // XXX CSS2.1 really doesn't say what to do here.
4799 *aResult
= kid
->GetLogicalNormalPosition(aWM
, containerWidth
).B(aWM
) +
4804 // XXX Is this the right test? We have some bogus empty lines
4805 // floating around, but IsEmpty is perhaps too weak.
4806 if (line
->BSize() != 0 || !line
->IsEmpty()) {
4807 *aResult
= line
->BStart() + line
->GetLogicalAscent();
4816 CalculateBlockContentBEnd(WritingMode aWM
, nsBlockFrame
* aFrame
)
4818 NS_PRECONDITION(aFrame
, "null ptr");
4820 nscoord contentBEnd
= 0;
4822 for (nsBlockFrame::line_iterator line
= aFrame
->begin_lines(),
4823 line_end
= aFrame
->end_lines();
4824 line
!= line_end
; ++line
) {
4825 if (line
->IsBlock()) {
4826 nsIFrame
* child
= line
->mFirstChild
;
4827 nscoord containerWidth
= line
->mContainerWidth
;
4829 child
->GetLogicalNormalPosition(aWM
, containerWidth
).B(aWM
);
4831 std::max(contentBEnd
,
4832 nsLayoutUtils::CalculateContentBEnd(aWM
, child
) + offset
);
4835 contentBEnd
= std::max(contentBEnd
, line
->BEnd());
4841 /* static */ nscoord
4842 nsLayoutUtils::CalculateContentBEnd(WritingMode aWM
, nsIFrame
* aFrame
)
4844 NS_PRECONDITION(aFrame
, "null ptr");
4846 nscoord contentBEnd
= aFrame
->BSize(aWM
);
4848 // We want scrollable overflow rather than visual because this
4849 // calculation is intended to affect layout.
4850 LogicalSize
overflowSize(aWM
, aFrame
->GetScrollableOverflowRect().Size());
4851 if (overflowSize
.BSize(aWM
) > contentBEnd
) {
4852 nsIFrame::ChildListIDs
skip(nsIFrame::kOverflowList
|
4853 nsIFrame::kExcessOverflowContainersList
|
4854 nsIFrame::kOverflowOutOfFlowList
);
4855 nsBlockFrame
* blockFrame
= GetAsBlock(aFrame
);
4858 std::max(contentBEnd
, CalculateBlockContentBEnd(aWM
, blockFrame
));
4859 skip
|= nsIFrame::kPrincipalList
;
4861 nsIFrame::ChildListIterator
lists(aFrame
);
4862 for (; !lists
.IsDone(); lists
.Next()) {
4863 if (!skip
.Contains(lists
.CurrentID())) {
4864 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
4865 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
4866 nsIFrame
* child
= childFrames
.get();
4868 child
->GetLogicalNormalPosition(aWM
,
4869 aFrame
->GetSize().width
).B(aWM
);
4870 contentBEnd
= std::max(contentBEnd
,
4871 CalculateContentBEnd(aWM
, child
) + offset
);
4879 /* static */ nsIFrame
*
4880 nsLayoutUtils::GetClosestLayer(nsIFrame
* aFrame
)
4883 for (layer
= aFrame
; layer
; layer
= layer
->GetParent()) {
4884 if (layer
->IsPositioned() ||
4885 (layer
->GetParent() &&
4886 layer
->GetParent()->GetType() == nsGkAtoms::scrollFrame
))
4891 return aFrame
->PresContext()->PresShell()->FrameManager()->GetRootFrame();
4895 nsLayoutUtils::GetGraphicsFilterForFrame(nsIFrame
* aForFrame
)
4897 GraphicsFilter defaultFilter
= GraphicsFilter::FILTER_GOOD
;
4899 if (nsCSSRendering::IsCanvasFrame(aForFrame
)) {
4900 nsCSSRendering::FindBackground(aForFrame
, &sc
);
4902 sc
= aForFrame
->StyleContext();
4905 switch (sc
->StyleSVG()->mImageRendering
) {
4906 case NS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED
:
4907 return GraphicsFilter::FILTER_FAST
;
4908 case NS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY
:
4909 return GraphicsFilter::FILTER_BEST
;
4910 case NS_STYLE_IMAGE_RENDERING_CRISPEDGES
:
4911 return GraphicsFilter::FILTER_NEAREST
;
4913 return defaultFilter
;
4918 * Given an image being drawn into an appunit coordinate system, and
4919 * a point in that coordinate system, map the point back into image
4921 * @param aSize the size of the image, in pixels
4922 * @param aDest the rectangle that the image is being mapped into
4923 * @param aPt a point in the same coordinate system as the rectangle
4926 MapToFloatImagePixels(const gfxSize
& aSize
,
4927 const gfxRect
& aDest
, const gfxPoint
& aPt
)
4929 return gfxPoint(((aPt
.x
- aDest
.X())*aSize
.width
)/aDest
.Width(),
4930 ((aPt
.y
- aDest
.Y())*aSize
.height
)/aDest
.Height());
4934 * Given an image being drawn into an pixel-based coordinate system, and
4935 * a point in image space, map the point into the pixel-based coordinate
4937 * @param aSize the size of the image, in pixels
4938 * @param aDest the rectangle that the image is being mapped into
4939 * @param aPt a point in image space
4942 MapToFloatUserPixels(const gfxSize
& aSize
,
4943 const gfxRect
& aDest
, const gfxPoint
& aPt
)
4945 return gfxPoint(aPt
.x
*aDest
.Width()/aSize
.width
+ aDest
.X(),
4946 aPt
.y
*aDest
.Height()/aSize
.height
+ aDest
.Y());
4949 /* static */ gfxRect
4950 nsLayoutUtils::RectToGfxRect(const nsRect
& aRect
, int32_t aAppUnitsPerDevPixel
)
4952 return gfxRect(gfxFloat(aRect
.x
) / aAppUnitsPerDevPixel
,
4953 gfxFloat(aRect
.y
) / aAppUnitsPerDevPixel
,
4954 gfxFloat(aRect
.width
) / aAppUnitsPerDevPixel
,
4955 gfxFloat(aRect
.height
) / aAppUnitsPerDevPixel
);
4958 struct SnappedImageDrawingParameters
{
4959 // A transform from image space to device space.
4960 gfxMatrix imageSpaceToDeviceSpace
;
4961 // The size at which the image should be drawn (which may not be its
4962 // intrinsic size due to, for example, HQ scaling).
4964 // The region in tiled image space which will be drawn, with an associated
4965 // region to which sampling should be restricted.
4967 // Whether there's anything to draw at all.
4970 SnappedImageDrawingParameters()
4971 : region(ImageRegion::Empty())
4975 SnappedImageDrawingParameters(const gfxMatrix
& aImageSpaceToDeviceSpace
,
4976 const nsIntSize
& aSize
,
4977 const ImageRegion
& aRegion
)
4978 : imageSpaceToDeviceSpace(aImageSpaceToDeviceSpace
)
4986 * Given two axis-aligned rectangles, returns the transformation that maps the
4987 * first onto the second.
4989 * @param aFrom The rect to be transformed.
4990 * @param aTo The rect that aFrom should be mapped onto by the transformation.
4993 TransformBetweenRects(const gfxRect
& aFrom
, const gfxRect
& aTo
)
4995 gfxSize
scale(aTo
.width
/ aFrom
.width
,
4996 aTo
.height
/ aFrom
.height
);
4997 gfxPoint
translation(aTo
.x
- aFrom
.x
* scale
.width
,
4998 aTo
.y
- aFrom
.y
* scale
.height
);
4999 return gfxMatrix(scale
.width
, 0, 0, scale
.height
,
5000 translation
.x
, translation
.y
);
5004 TileNearRect(const nsRect
& aAnyTile
, const nsRect
& aTargetRect
)
5006 nsPoint distance
= aTargetRect
.TopLeft() - aAnyTile
.TopLeft();
5007 return aAnyTile
+ nsPoint(distance
.x
/ aAnyTile
.width
* aAnyTile
.width
,
5008 distance
.y
/ aAnyTile
.height
* aAnyTile
.height
);
5012 * Given a set of input parameters, compute certain output parameters
5013 * for drawing an image with the image snapping algorithm.
5014 * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
5016 * @see nsLayoutUtils::DrawImage() for the descriptions of input parameters
5018 static SnappedImageDrawingParameters
5019 ComputeSnappedImageDrawingParameters(gfxContext
* aCtx
,
5020 int32_t aAppUnitsPerDevPixel
,
5023 const nsPoint aAnchor
,
5024 const nsRect aDirty
,
5025 imgIContainer
* aImage
,
5026 GraphicsFilter aGraphicsFilter
,
5027 uint32_t aImageFlags
)
5029 if (aDest
.IsEmpty() || aFill
.IsEmpty())
5030 return SnappedImageDrawingParameters();
5032 // Avoid unnecessarily large offsets.
5033 bool doTile
= !aDest
.Contains(aFill
);
5034 nsRect dest
= doTile
? TileNearRect(aDest
, aFill
.Intersect(aDirty
)) : aDest
;
5035 nsPoint anchor
= aAnchor
+ (dest
.TopLeft() - aDest
.TopLeft());
5037 gfxRect devPixelDest
=
5038 nsLayoutUtils::RectToGfxRect(dest
, aAppUnitsPerDevPixel
);
5039 gfxRect devPixelFill
=
5040 nsLayoutUtils::RectToGfxRect(aFill
, aAppUnitsPerDevPixel
);
5041 gfxRect devPixelDirty
=
5042 nsLayoutUtils::RectToGfxRect(aDirty
, aAppUnitsPerDevPixel
);
5044 gfxMatrix currentMatrix
= aCtx
->CurrentMatrix();
5045 gfxRect fill
= devPixelFill
;
5047 // Snap even if we have a scale in the context. But don't snap if
5048 // we have something that's not translation+scale, or if the scale flips in
5049 // the X or Y direction, because snapped image drawing can't handle that yet.
5050 if (!currentMatrix
.HasNonAxisAlignedTransform() &&
5051 currentMatrix
._11
> 0.0 && currentMatrix
._22
> 0.0 &&
5052 aCtx
->UserToDevicePixelSnapped(fill
, true)) {
5054 if (fill
.IsEmpty()) {
5055 return SnappedImageDrawingParameters();
5059 fill
= devPixelFill
;
5062 gfxSize destScale
= didSnap
? gfxSize(currentMatrix
._11
, currentMatrix
._22
)
5063 : gfxSize(1.0, 1.0);
5064 gfxSize
appUnitScaledDest(dest
.width
* destScale
.width
,
5065 dest
.height
* destScale
.height
);
5066 gfxSize scaledDest
= appUnitScaledDest
/ aAppUnitsPerDevPixel
;
5067 gfxSize snappedScaledDest
=
5068 gfxSize(NSAppUnitsToIntPixels(appUnitScaledDest
.width
, aAppUnitsPerDevPixel
),
5069 NSAppUnitsToIntPixels(appUnitScaledDest
.height
, aAppUnitsPerDevPixel
));
5071 if (scaledDest
.IsEmpty() || snappedScaledDest
.IsEmpty()) {
5072 return SnappedImageDrawingParameters();
5075 nsIntSize intImageSize
=
5076 aImage
->OptimalImageSizeForDest(snappedScaledDest
,
5077 imgIContainer::FRAME_CURRENT
,
5078 aGraphicsFilter
, aImageFlags
);
5079 gfxSize
imageSize(intImageSize
.width
, intImageSize
.height
);
5081 // Compute the set of pixels that would be sampled by an ideal rendering
5082 gfxPoint subimageTopLeft
=
5083 MapToFloatImagePixels(imageSize
, devPixelDest
, devPixelFill
.TopLeft());
5084 gfxPoint subimageBottomRight
=
5085 MapToFloatImagePixels(imageSize
, devPixelDest
, devPixelFill
.BottomRight());
5087 subimage
.MoveTo(NSToIntFloor(subimageTopLeft
.x
),
5088 NSToIntFloor(subimageTopLeft
.y
));
5089 subimage
.SizeTo(NSToIntCeil(subimageBottomRight
.x
) - subimage
.x
,
5090 NSToIntCeil(subimageBottomRight
.y
) - subimage
.y
);
5092 gfxMatrix transform
;
5093 gfxMatrix invTransform
;
5095 bool anchorAtUpperLeft
= anchor
.x
== dest
.x
&& anchor
.y
== dest
.y
;
5096 bool exactlyOneImageCopy
= aFill
.IsEqualEdges(dest
);
5097 if (anchorAtUpperLeft
&& exactlyOneImageCopy
) {
5098 // The simple case: we can ignore the anchor point and compute the
5099 // transformation from the sampled region (the subimage) to the fill rect.
5100 // This approach is preferable when it works since it tends to produce
5101 // less numerical error.
5102 transform
= TransformBetweenRects(subimage
, fill
);
5103 invTransform
= TransformBetweenRects(fill
, subimage
);
5105 // The more complicated case: we compute the transformation from the
5106 // image rect positioned at the image space anchor point to the dest rect
5107 // positioned at the device space anchor point.
5109 // Compute the anchor point in both device space and image space. This
5110 // code assumes that pixel-based devices have one pixel per device unit!
5111 gfxPoint
anchorPoint(gfxFloat(anchor
.x
)/aAppUnitsPerDevPixel
,
5112 gfxFloat(anchor
.y
)/aAppUnitsPerDevPixel
);
5113 gfxPoint imageSpaceAnchorPoint
=
5114 MapToFloatImagePixels(imageSize
, devPixelDest
, anchorPoint
);
5117 imageSpaceAnchorPoint
.Round();
5118 anchorPoint
= imageSpaceAnchorPoint
;
5119 anchorPoint
= MapToFloatUserPixels(imageSize
, devPixelDest
, anchorPoint
);
5120 anchorPoint
= currentMatrix
.Transform(anchorPoint
);
5121 anchorPoint
.Round();
5124 gfxRect
anchoredDestRect(anchorPoint
, scaledDest
);
5125 gfxRect
anchoredImageRect(imageSpaceAnchorPoint
, imageSize
);
5126 transform
= TransformBetweenRects(anchoredImageRect
, anchoredDestRect
);
5127 invTransform
= TransformBetweenRects(anchoredDestRect
, anchoredImageRect
);
5130 // If the transform is not a straight translation by integers, then
5131 // filtering will occur, and restricting the fill rect to the dirty rect
5132 // would change the values computed for edge pixels, which we can't allow.
5133 // Also, if 'didSnap' is false then rounding out 'devPixelDirty' might not
5134 // produce pixel-aligned coordinates, which would also break the values
5135 // computed for edge pixels.
5136 if (didSnap
&& !invTransform
.HasNonIntegerTranslation()) {
5137 // This form of Transform is safe to call since non-axis-aligned
5138 // transforms wouldn't be snapped.
5139 devPixelDirty
= currentMatrix
.Transform(devPixelDirty
);
5140 devPixelDirty
.RoundOut();
5141 fill
= fill
.Intersect(devPixelDirty
);
5144 return SnappedImageDrawingParameters();
5146 gfxRect
imageSpaceFill(didSnap
? invTransform
.Transform(fill
)
5147 : invTransform
.TransformBounds(fill
));
5149 // If we didn't snap, we need to post-multiply the matrix on the context to
5150 // get the final matrix we'll draw with, because we didn't take it into
5151 // account when computing the matrices above.
5153 transform
= transform
* currentMatrix
;
5156 ImageRegion region
=
5157 ImageRegion::CreateWithSamplingRestriction(imageSpaceFill
, subimage
);
5158 return SnappedImageDrawingParameters(transform
, intImageSize
, region
);
5163 DrawImageInternal(nsRenderingContext
* aRenderingContext
,
5164 nsPresContext
* aPresContext
,
5165 imgIContainer
* aImage
,
5166 GraphicsFilter aGraphicsFilter
,
5167 const nsRect
& aDest
,
5168 const nsRect
& aFill
,
5169 const nsPoint
& aAnchor
,
5170 const nsRect
& aDirty
,
5171 const SVGImageContext
* aSVGContext
,
5172 uint32_t aImageFlags
)
5174 if (aPresContext
->Type() == nsPresContext::eContext_Print
) {
5175 // We want vector images to be passed on as vector commands, not a raster
5177 aImageFlags
|= imgIContainer::FLAG_BYPASS_SURFACE_CACHE
;
5179 if (aDest
.Contains(aFill
)) {
5180 aImageFlags
|= imgIContainer::FLAG_CLAMP
;
5182 int32_t appUnitsPerDevPixel
=
5183 aPresContext
->AppUnitsPerDevPixel();
5184 gfxContext
* ctx
= aRenderingContext
->ThebesContext();
5186 SnappedImageDrawingParameters params
=
5187 ComputeSnappedImageDrawingParameters(ctx
, appUnitsPerDevPixel
, aDest
,
5188 aFill
, aAnchor
, aDirty
, aImage
,
5189 aGraphicsFilter
, aImageFlags
);
5191 if (!params
.shouldDraw
)
5194 gfxContextMatrixAutoSaveRestore
contextMatrixRestorer(ctx
);
5195 ctx
->SetMatrix(params
.imageSpaceToDeviceSpace
);
5197 aImage
->Draw(ctx
, params
.size
, params
.region
, imgIContainer::FRAME_CURRENT
,
5198 aGraphicsFilter
, ToMaybe(aSVGContext
), aImageFlags
);
5203 /* static */ nsresult
5204 nsLayoutUtils::DrawSingleUnscaledImage(nsRenderingContext
* aRenderingContext
,
5205 nsPresContext
* aPresContext
,
5206 imgIContainer
* aImage
,
5207 GraphicsFilter aGraphicsFilter
,
5208 const nsPoint
& aDest
,
5209 const nsRect
* aDirty
,
5210 uint32_t aImageFlags
,
5211 const nsRect
* aSourceArea
)
5213 nsIntSize imageSize
;
5214 aImage
->GetWidth(&imageSize
.width
);
5215 aImage
->GetHeight(&imageSize
.height
);
5216 NS_ENSURE_TRUE(imageSize
.width
> 0 && imageSize
.height
> 0, NS_ERROR_FAILURE
);
5218 nscoord appUnitsPerCSSPixel
= nsDeviceContext::AppUnitsPerCSSPixel();
5219 nsSize
size(imageSize
.width
*appUnitsPerCSSPixel
,
5220 imageSize
.height
*appUnitsPerCSSPixel
);
5224 source
= *aSourceArea
;
5226 source
.SizeTo(size
);
5229 nsRect
dest(aDest
- source
.TopLeft(), size
);
5230 nsRect
fill(aDest
, source
.Size());
5231 // Ensure that only a single image tile is drawn. If aSourceArea extends
5232 // outside the image bounds, we want to honor the aSourceArea-to-aDest
5233 // translation but we don't want to actually tile the image.
5234 fill
.IntersectRect(fill
, dest
);
5235 return DrawImageInternal(aRenderingContext
, aPresContext
,
5236 aImage
, aGraphicsFilter
,
5237 dest
, fill
, aDest
, aDirty
? *aDirty
: dest
,
5238 nullptr, aImageFlags
);
5241 /* static */ nsresult
5242 nsLayoutUtils::DrawSingleImage(nsRenderingContext
* aRenderingContext
,
5243 nsPresContext
* aPresContext
,
5244 imgIContainer
* aImage
,
5245 GraphicsFilter aGraphicsFilter
,
5246 const nsRect
& aDest
,
5247 const nsRect
& aDirty
,
5248 const SVGImageContext
* aSVGContext
,
5249 uint32_t aImageFlags
,
5250 const nsRect
* aSourceArea
)
5252 nsIntSize
imageSize(ComputeSizeForDrawingWithFallback(aImage
, aDest
.Size()));
5253 NS_ENSURE_TRUE(imageSize
.width
> 0 && imageSize
.height
> 0, NS_ERROR_FAILURE
);
5256 nsCOMPtr
<imgIContainer
> image
;
5258 source
= *aSourceArea
;
5259 nsIntRect
subRect(source
.x
, source
.y
, source
.width
, source
.height
);
5260 subRect
.ScaleInverseRoundOut(nsDeviceContext::AppUnitsPerCSSPixel());
5261 image
= ImageOps::Clip(aImage
, subRect
);
5263 nscoord appUnitsPerCSSPixel
= nsDeviceContext::AppUnitsPerCSSPixel();
5264 source
.SizeTo(imageSize
.width
*appUnitsPerCSSPixel
,
5265 imageSize
.height
*appUnitsPerCSSPixel
);
5269 nsRect dest
= nsLayoutUtils::GetWholeImageDestination(imageSize
, source
,
5271 // Ensure that only a single image tile is drawn. If aSourceArea extends
5272 // outside the image bounds, we want to honor the aSourceArea-to-aDest
5273 // transform but we don't want to actually tile the image.
5275 fill
.IntersectRect(aDest
, dest
);
5276 return DrawImageInternal(aRenderingContext
, aPresContext
, aImage
,
5277 aGraphicsFilter
, dest
, fill
, fill
.TopLeft(),
5278 aDirty
, aSVGContext
, aImageFlags
);
5282 nsLayoutUtils::ComputeSizeForDrawing(imgIContainer
*aImage
,
5283 nsIntSize
& aImageSize
, /*outparam*/
5284 nsSize
& aIntrinsicRatio
, /*outparam*/
5285 bool& aGotWidth
, /*outparam*/
5286 bool& aGotHeight
/*outparam*/)
5288 aGotWidth
= NS_SUCCEEDED(aImage
->GetWidth(&aImageSize
.width
));
5289 aGotHeight
= NS_SUCCEEDED(aImage
->GetHeight(&aImageSize
.height
));
5290 bool gotRatio
= NS_SUCCEEDED(aImage
->GetIntrinsicRatio(&aIntrinsicRatio
));
5292 if (!(aGotWidth
&& aGotHeight
) && !gotRatio
) {
5293 // We hit an error (say, because the image failed to load or couldn't be
5294 // decoded) and should return zero size.
5295 aGotWidth
= aGotHeight
= true;
5296 aImageSize
= nsIntSize(0, 0);
5297 aIntrinsicRatio
= nsSize(0, 0);
5301 /* static */ nsIntSize
5302 nsLayoutUtils::ComputeSizeForDrawingWithFallback(imgIContainer
* aImage
,
5303 const nsSize
& aFallbackSize
)
5305 nsIntSize imageSize
;
5307 bool gotHeight
, gotWidth
;
5308 ComputeSizeForDrawing(aImage
, imageSize
, imageRatio
, gotWidth
, gotHeight
);
5310 // If we didn't get both width and height, try to compute them using the
5311 // intrinsic ratio of the image.
5312 if (gotWidth
!= gotHeight
) {
5314 if (imageRatio
.height
!= 0) {
5316 NSCoordSaturatingNonnegativeMultiply(imageSize
.height
,
5317 float(imageRatio
.width
) /
5318 float(imageRatio
.height
));
5322 if (imageRatio
.width
!= 0) {
5324 NSCoordSaturatingNonnegativeMultiply(imageSize
.width
,
5325 float(imageRatio
.height
) /
5326 float(imageRatio
.width
));
5332 // If we still don't have a width or height, just use the fallback size the
5335 imageSize
.width
= nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize
.width
);
5338 imageSize
.height
= nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize
.height
);
5344 /* static */ nsresult
5345 nsLayoutUtils::DrawBackgroundImage(nsRenderingContext
* aRenderingContext
,
5346 nsPresContext
* aPresContext
,
5347 imgIContainer
* aImage
,
5348 const nsIntSize
& aImageSize
,
5349 GraphicsFilter aGraphicsFilter
,
5350 const nsRect
& aDest
,
5351 const nsRect
& aFill
,
5352 const nsPoint
& aAnchor
,
5353 const nsRect
& aDirty
,
5354 uint32_t aImageFlags
)
5356 PROFILER_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage",
5357 js::ProfileEntry::Category::GRAPHICS
);
5359 if (UseBackgroundNearestFiltering()) {
5360 aGraphicsFilter
= GraphicsFilter::FILTER_NEAREST
;
5363 SVGImageContext
svgContext(aImageSize
, Nothing());
5365 return DrawImageInternal(aRenderingContext
, aPresContext
, aImage
,
5366 aGraphicsFilter
, aDest
, aFill
, aAnchor
,
5367 aDirty
, &svgContext
, aImageFlags
);
5370 /* static */ nsresult
5371 nsLayoutUtils::DrawImage(nsRenderingContext
* aRenderingContext
,
5372 nsPresContext
* aPresContext
,
5373 imgIContainer
* aImage
,
5374 GraphicsFilter aGraphicsFilter
,
5375 const nsRect
& aDest
,
5376 const nsRect
& aFill
,
5377 const nsPoint
& aAnchor
,
5378 const nsRect
& aDirty
,
5379 uint32_t aImageFlags
)
5381 return DrawImageInternal(aRenderingContext
, aPresContext
, aImage
,
5382 aGraphicsFilter
, aDest
, aFill
, aAnchor
,
5383 aDirty
, nullptr, aImageFlags
);
5387 nsLayoutUtils::GetWholeImageDestination(const nsIntSize
& aWholeImageSize
,
5388 const nsRect
& aImageSourceArea
,
5389 const nsRect
& aDestArea
)
5391 double scaleX
= double(aDestArea
.width
)/aImageSourceArea
.width
;
5392 double scaleY
= double(aDestArea
.height
)/aImageSourceArea
.height
;
5393 nscoord destOffsetX
= NSToCoordRound(aImageSourceArea
.x
*scaleX
);
5394 nscoord destOffsetY
= NSToCoordRound(aImageSourceArea
.y
*scaleY
);
5395 nscoord appUnitsPerCSSPixel
= nsDeviceContext::AppUnitsPerCSSPixel();
5396 nscoord wholeSizeX
= NSToCoordRound(aWholeImageSize
.width
*appUnitsPerCSSPixel
*scaleX
);
5397 nscoord wholeSizeY
= NSToCoordRound(aWholeImageSize
.height
*appUnitsPerCSSPixel
*scaleY
);
5398 return nsRect(aDestArea
.TopLeft() - nsPoint(destOffsetX
, destOffsetY
),
5399 nsSize(wholeSizeX
, wholeSizeY
));
5402 /* static */ already_AddRefed
<imgIContainer
>
5403 nsLayoutUtils::OrientImage(imgIContainer
* aContainer
,
5404 const nsStyleImageOrientation
& aOrientation
)
5406 MOZ_ASSERT(aContainer
, "Should have an image container");
5407 nsCOMPtr
<imgIContainer
> img(aContainer
);
5409 if (aOrientation
.IsFromImage()) {
5410 img
= ImageOps::Orient(img
, img
->GetOrientation());
5411 } else if (!aOrientation
.IsDefault()) {
5412 Angle angle
= aOrientation
.Angle();
5413 Flip flip
= aOrientation
.IsFlipped() ? Flip::Horizontal
5415 img
= ImageOps::Orient(img
, Orientation(angle
, flip
));
5418 return img
.forget();
5421 static bool NonZeroStyleCoord(const nsStyleCoord
& aCoord
)
5423 if (aCoord
.IsCoordPercentCalcUnit()) {
5424 // Since negative results are clamped to 0, check > 0.
5425 return nsRuleNode::ComputeCoordPercentCalc(aCoord
, nscoord_MAX
) > 0 ||
5426 nsRuleNode::ComputeCoordPercentCalc(aCoord
, 0) > 0;
5433 nsLayoutUtils::HasNonZeroCorner(const nsStyleCorners
& aCorners
)
5435 NS_FOR_CSS_HALF_CORNERS(corner
) {
5436 if (NonZeroStyleCoord(aCorners
.Get(corner
)))
5442 // aCorner is a "full corner" value, i.e. NS_CORNER_TOP_LEFT etc
5443 static bool IsCornerAdjacentToSide(uint8_t aCorner
, css::Side aSide
)
5445 PR_STATIC_ASSERT((int)NS_SIDE_TOP
== NS_CORNER_TOP_LEFT
);
5446 PR_STATIC_ASSERT((int)NS_SIDE_RIGHT
== NS_CORNER_TOP_RIGHT
);
5447 PR_STATIC_ASSERT((int)NS_SIDE_BOTTOM
== NS_CORNER_BOTTOM_RIGHT
);
5448 PR_STATIC_ASSERT((int)NS_SIDE_LEFT
== NS_CORNER_BOTTOM_LEFT
);
5449 PR_STATIC_ASSERT((int)NS_SIDE_TOP
== ((NS_CORNER_TOP_RIGHT
- 1)&3));
5450 PR_STATIC_ASSERT((int)NS_SIDE_RIGHT
== ((NS_CORNER_BOTTOM_RIGHT
- 1)&3));
5451 PR_STATIC_ASSERT((int)NS_SIDE_BOTTOM
== ((NS_CORNER_BOTTOM_LEFT
- 1)&3));
5452 PR_STATIC_ASSERT((int)NS_SIDE_LEFT
== ((NS_CORNER_TOP_LEFT
- 1)&3));
5454 return aSide
== aCorner
|| aSide
== ((aCorner
- 1)&3);
5458 nsLayoutUtils::HasNonZeroCornerOnSide(const nsStyleCorners
& aCorners
,
5461 PR_STATIC_ASSERT(NS_CORNER_TOP_LEFT_X
/2 == NS_CORNER_TOP_LEFT
);
5462 PR_STATIC_ASSERT(NS_CORNER_TOP_LEFT_Y
/2 == NS_CORNER_TOP_LEFT
);
5463 PR_STATIC_ASSERT(NS_CORNER_TOP_RIGHT_X
/2 == NS_CORNER_TOP_RIGHT
);
5464 PR_STATIC_ASSERT(NS_CORNER_TOP_RIGHT_Y
/2 == NS_CORNER_TOP_RIGHT
);
5465 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_RIGHT_X
/2 == NS_CORNER_BOTTOM_RIGHT
);
5466 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_RIGHT_Y
/2 == NS_CORNER_BOTTOM_RIGHT
);
5467 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_LEFT_X
/2 == NS_CORNER_BOTTOM_LEFT
);
5468 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_LEFT_Y
/2 == NS_CORNER_BOTTOM_LEFT
);
5470 NS_FOR_CSS_HALF_CORNERS(corner
) {
5471 // corner is a "half corner" value, so dividing by two gives us a
5472 // "full corner" value.
5473 if (NonZeroStyleCoord(aCorners
.Get(corner
)) &&
5474 IsCornerAdjacentToSide(corner
/2, aSide
))
5480 /* static */ nsTransparencyMode
5481 nsLayoutUtils::GetFrameTransparency(nsIFrame
* aBackgroundFrame
,
5482 nsIFrame
* aCSSRootFrame
) {
5483 if (aCSSRootFrame
->StyleDisplay()->mOpacity
< 1.0f
)
5484 return eTransparencyTransparent
;
5486 if (HasNonZeroCorner(aCSSRootFrame
->StyleBorder()->mBorderRadius
))
5487 return eTransparencyTransparent
;
5489 if (aCSSRootFrame
->StyleDisplay()->mAppearance
== NS_THEME_WIN_GLASS
)
5490 return eTransparencyGlass
;
5492 if (aCSSRootFrame
->StyleDisplay()->mAppearance
== NS_THEME_WIN_BORDERLESS_GLASS
)
5493 return eTransparencyBorderlessGlass
;
5495 nsITheme::Transparency transparency
;
5496 if (aCSSRootFrame
->IsThemed(&transparency
))
5497 return transparency
== nsITheme::eTransparent
5498 ? eTransparencyTransparent
5499 : eTransparencyOpaque
;
5501 // We need an uninitialized window to be treated as opaque because
5502 // doing otherwise breaks window display effects on some platforms,
5503 // specifically Vista. (bug 450322)
5504 if (aBackgroundFrame
->GetType() == nsGkAtoms::viewportFrame
&&
5505 !aBackgroundFrame
->GetFirstPrincipalChild()) {
5506 return eTransparencyOpaque
;
5509 nsStyleContext
* bgSC
;
5510 if (!nsCSSRendering::FindBackground(aBackgroundFrame
, &bgSC
)) {
5511 return eTransparencyTransparent
;
5513 const nsStyleBackground
* bg
= bgSC
->StyleBackground();
5514 if (NS_GET_A(bg
->mBackgroundColor
) < 255 ||
5515 // bottom layer's clip is used for the color
5516 bg
->BottomLayer().mClip
!= NS_STYLE_BG_CLIP_BORDER
)
5517 return eTransparencyTransparent
;
5518 return eTransparencyOpaque
;
5521 static bool IsPopupFrame(nsIFrame
* aFrame
)
5523 // aFrame is a popup it's the list control frame dropdown for a combobox.
5524 nsIAtom
* frameType
= aFrame
->GetType();
5525 if (frameType
== nsGkAtoms::listControlFrame
) {
5526 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(aFrame
);
5527 return lcf
->IsInDropDownMode();
5530 // ... or if it's a XUL menupopup frame.
5531 return frameType
== nsGkAtoms::menuPopupFrame
;
5535 nsLayoutUtils::IsPopup(nsIFrame
* aFrame
)
5537 // Optimization: the frame can't possibly be a popup if it has no view.
5538 if (!aFrame
->HasView()) {
5539 NS_ASSERTION(!IsPopupFrame(aFrame
), "popup frame must have a view");
5542 return IsPopupFrame(aFrame
);
5545 /* static */ nsIFrame
*
5546 nsLayoutUtils::GetDisplayRootFrame(nsIFrame
* aFrame
)
5548 // We could use GetRootPresContext() here if the
5549 // NS_FRAME_IN_POPUP frame bit is set.
5550 nsIFrame
* f
= aFrame
;
5552 if (!f
->HasAnyStateBits(NS_FRAME_IN_POPUP
)) {
5553 f
= f
->PresContext()->FrameManager()->GetRootFrame();
5554 } else if (IsPopup(f
)) {
5557 nsIFrame
* parent
= GetCrossDocParentFrame(f
);
5564 /* static */ nsIFrame
*
5565 nsLayoutUtils::GetReferenceFrame(nsIFrame
* aFrame
)
5567 nsIFrame
*f
= aFrame
;
5569 if (f
->IsTransformed() || IsPopup(f
)) {
5572 nsIFrame
* parent
= GetCrossDocParentFrame(f
);
5580 /* static */ nsIFrame
*
5581 nsLayoutUtils::GetTransformRootFrame(nsIFrame
* aFrame
)
5583 nsIFrame
*parent
= nsLayoutUtils::GetCrossDocParentFrame(aFrame
);
5584 while (parent
&& parent
->Preserves3DChildren()) {
5585 parent
= nsLayoutUtils::GetCrossDocParentFrame(parent
);
5590 /* static */ uint32_t
5591 nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext
* aStyleContext
,
5592 const nsStyleFont
* aStyleFont
,
5593 const nsStyleText
* aStyleText
,
5594 nscoord aLetterSpacing
)
5596 uint32_t result
= 0;
5597 if (aLetterSpacing
!= 0) {
5598 result
|= gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES
;
5600 if (aStyleText
->mControlCharacterVisibility
== NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN
) {
5601 result
|= gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS
;
5603 switch (aStyleContext
->StyleSVG()->mTextRendering
) {
5604 case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED
:
5605 result
|= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED
;
5607 case NS_STYLE_TEXT_RENDERING_AUTO
:
5608 if (aStyleFont
->mFont
.size
<
5609 aStyleContext
->PresContext()->GetAutoQualityMinFontSize()) {
5610 result
|= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED
;
5620 nsLayoutUtils::GetRectDifferenceStrips(const nsRect
& aR1
, const nsRect
& aR2
,
5621 nsRect
* aHStrip
, nsRect
* aVStrip
) {
5622 NS_ASSERTION(aR1
.TopLeft() == aR2
.TopLeft(),
5623 "expected rects at the same position");
5624 nsRect
unionRect(aR1
.x
, aR1
.y
, std::max(aR1
.width
, aR2
.width
),
5625 std::max(aR1
.height
, aR2
.height
));
5626 nscoord VStripStart
= std::min(aR1
.width
, aR2
.width
);
5627 nscoord HStripStart
= std::min(aR1
.height
, aR2
.height
);
5628 *aVStrip
= unionRect
;
5629 aVStrip
->x
+= VStripStart
;
5630 aVStrip
->width
-= VStripStart
;
5631 *aHStrip
= unionRect
;
5632 aHStrip
->y
+= HStripStart
;
5633 aHStrip
->height
-= HStripStart
;
5637 nsLayoutUtils::GetDeviceContextForScreenInfo(nsPIDOMWindow
* aWindow
)
5643 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
5645 // Now make sure our size is up to date. That will mean that the device
5646 // context does the right thing on multi-monitor systems when we return it to
5647 // the caller. It will also make sure that our prescontext has been created,
5648 // if we're supposed to have one.
5649 nsCOMPtr
<nsPIDOMWindow
> win
= docShell
->GetWindow();
5651 // No reason to go on
5655 win
->EnsureSizeUpToDate();
5657 nsRefPtr
<nsPresContext
> presContext
;
5658 docShell
->GetPresContext(getter_AddRefs(presContext
));
5660 nsDeviceContext
* context
= presContext
->DeviceContext();
5666 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
5667 docShell
->GetParent(getter_AddRefs(parentItem
));
5668 docShell
= do_QueryInterface(parentItem
);
5675 nsLayoutUtils::IsReallyFixedPos(nsIFrame
* aFrame
)
5677 NS_PRECONDITION(aFrame
->GetParent(),
5678 "IsReallyFixedPos called on frame not in tree");
5679 NS_PRECONDITION(aFrame
->StyleDisplay()->mPosition
==
5680 NS_STYLE_POSITION_FIXED
,
5681 "IsReallyFixedPos called on non-'position:fixed' frame");
5683 nsIAtom
*parentType
= aFrame
->GetParent()->GetType();
5684 return parentType
== nsGkAtoms::viewportFrame
||
5685 parentType
== nsGkAtoms::pageContentFrame
;
5688 nsLayoutUtils::SurfaceFromElementResult
5689 nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent
* aElement
,
5690 uint32_t aSurfaceFlags
,
5691 DrawTarget
* aTarget
)
5693 SurfaceFromElementResult result
;
5696 nsCOMPtr
<imgIRequest
> imgRequest
;
5697 rv
= aElement
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
5698 getter_AddRefs(imgRequest
));
5699 if (NS_FAILED(rv
) || !imgRequest
)
5703 imgRequest
->GetImageStatus(&status
);
5704 if ((status
& imgIRequest::STATUS_LOAD_COMPLETE
) == 0) {
5705 // Spec says to use GetComplete, but that only works on
5706 // nsIDOMHTMLImageElement, and we support all sorts of other stuff
5707 // here. Do this for now pending spec clarification.
5708 result
.mIsStillLoading
= (status
& imgIRequest::STATUS_ERROR
) == 0;
5712 nsCOMPtr
<nsIPrincipal
> principal
;
5713 rv
= imgRequest
->GetImagePrincipal(getter_AddRefs(principal
));
5717 nsCOMPtr
<imgIContainer
> imgContainer
;
5718 rv
= imgRequest
->GetImage(getter_AddRefs(imgContainer
));
5722 uint32_t noRasterize
= aSurfaceFlags
& SFE_NO_RASTERIZING_VECTORS
;
5724 uint32_t whichFrame
= (aSurfaceFlags
& SFE_WANT_FIRST_FRAME
)
5725 ? (uint32_t) imgIContainer::FRAME_FIRST
5726 : (uint32_t) imgIContainer::FRAME_CURRENT
;
5727 uint32_t frameFlags
= imgIContainer::FLAG_SYNC_DECODE
;
5728 if (aSurfaceFlags
& SFE_NO_COLORSPACE_CONVERSION
)
5729 frameFlags
|= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION
;
5730 if (aSurfaceFlags
& SFE_PREFER_NO_PREMULTIPLY_ALPHA
) {
5731 frameFlags
|= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
;
5732 result
.mIsPremultiplied
= false;
5735 int32_t imgWidth
, imgHeight
;
5736 rv
= imgContainer
->GetWidth(&imgWidth
);
5737 nsresult rv2
= imgContainer
->GetHeight(&imgHeight
);
5738 if (NS_FAILED(rv
) || NS_FAILED(rv2
))
5741 if (!noRasterize
|| imgContainer
->GetType() == imgIContainer::TYPE_RASTER
) {
5742 if (aSurfaceFlags
& SFE_WANT_IMAGE_SURFACE
) {
5743 frameFlags
|= imgIContainer::FLAG_WANT_DATA_SURFACE
;
5745 result
.mSourceSurface
= imgContainer
->GetFrame(whichFrame
, frameFlags
);
5746 if (!result
.mSourceSurface
) {
5749 // The surface we return is likely to be cached. We don't want to have to
5750 // convert to a surface that's compatible with aTarget each time it's used
5751 // (that would result in terrible performance), so we convert once here
5752 // upfront if aTarget is specified.
5754 RefPtr
<SourceSurface
> optSurface
=
5755 aTarget
->OptimizeSourceSurface(result
.mSourceSurface
);
5757 result
.mSourceSurface
= optSurface
;
5761 result
.mDrawInfo
.mImgContainer
= imgContainer
;
5762 result
.mDrawInfo
.mWhichFrame
= whichFrame
;
5763 result
.mDrawInfo
.mDrawingFlags
= frameFlags
;
5767 if (NS_SUCCEEDED(imgRequest
->GetCORSMode(&corsmode
))) {
5768 result
.mCORSUsed
= (corsmode
!= imgIRequest::CORS_NONE
);
5771 result
.mSize
= gfxIntSize(imgWidth
, imgHeight
);
5772 result
.mPrincipal
= principal
.forget();
5773 // no images, including SVG images, can load content from another domain.
5774 result
.mIsWriteOnly
= false;
5775 result
.mImageRequest
= imgRequest
.forget();
5780 nsLayoutUtils::SurfaceFromElementResult
5781 nsLayoutUtils::SurfaceFromElement(HTMLImageElement
*aElement
,
5782 uint32_t aSurfaceFlags
,
5783 DrawTarget
* aTarget
)
5785 return SurfaceFromElement(static_cast<nsIImageLoadingContent
*>(aElement
),
5786 aSurfaceFlags
, aTarget
);
5789 nsLayoutUtils::SurfaceFromElementResult
5790 nsLayoutUtils::SurfaceFromElement(HTMLCanvasElement
* aElement
,
5791 uint32_t aSurfaceFlags
,
5792 DrawTarget
* aTarget
)
5794 SurfaceFromElementResult result
;
5796 bool* isPremultiplied
= nullptr;
5797 if (aSurfaceFlags
& SFE_PREFER_NO_PREMULTIPLY_ALPHA
) {
5798 isPremultiplied
= &result
.mIsPremultiplied
;
5801 gfxIntSize size
= aElement
->GetSize();
5803 result
.mSourceSurface
= aElement
->GetSurfaceSnapshot(isPremultiplied
);
5804 if (!result
.mSourceSurface
) {
5805 // 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
5806 // draw nothing, so return an empty surface.
5807 DrawTarget
*ref
= aTarget
? aTarget
: gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
5808 RefPtr
<DrawTarget
> dt
= ref
->CreateSimilarDrawTarget(IntSize(size
.width
, size
.height
),
5809 SurfaceFormat::B8G8R8A8
);
5811 result
.mSourceSurface
= dt
->Snapshot();
5813 } else if (aTarget
) {
5814 RefPtr
<SourceSurface
> opt
= aTarget
->OptimizeSourceSurface(result
.mSourceSurface
);
5816 result
.mSourceSurface
= opt
;
5820 // Ensure that any future changes to the canvas trigger proper invalidation,
5821 // in case this is being used by -moz-element()
5822 aElement
->MarkContextClean();
5824 result
.mSize
= size
;
5825 result
.mPrincipal
= aElement
->NodePrincipal();
5826 result
.mIsWriteOnly
= aElement
->IsWriteOnly();
5831 nsLayoutUtils::SurfaceFromElementResult
5832 nsLayoutUtils::SurfaceFromElement(HTMLVideoElement
* aElement
,
5833 uint32_t aSurfaceFlags
,
5834 DrawTarget
* aTarget
)
5836 SurfaceFromElementResult result
;
5838 NS_WARN_IF_FALSE((aSurfaceFlags
& SFE_PREFER_NO_PREMULTIPLY_ALPHA
) == 0, "We can't support non-premultiplied alpha for video!");
5840 uint16_t readyState
;
5841 if (NS_SUCCEEDED(aElement
->GetReadyState(&readyState
)) &&
5842 (readyState
== nsIDOMHTMLMediaElement::HAVE_NOTHING
||
5843 readyState
== nsIDOMHTMLMediaElement::HAVE_METADATA
)) {
5844 result
.mIsStillLoading
= true;
5848 // If it doesn't have a principal, just bail
5849 nsCOMPtr
<nsIPrincipal
> principal
= aElement
->GetCurrentPrincipal();
5853 ImageContainer
*container
= aElement
->GetImageContainer();
5857 mozilla::gfx::IntSize size
;
5858 result
.mSourceSurface
= container
->GetCurrentAsSourceSurface(&size
);
5859 if (!result
.mSourceSurface
)
5863 RefPtr
<SourceSurface
> opt
= aTarget
->OptimizeSourceSurface(result
.mSourceSurface
);
5865 result
.mSourceSurface
= opt
;
5869 result
.mCORSUsed
= aElement
->GetCORSMode() != CORS_NONE
;
5870 result
.mSize
= ThebesIntSize(size
);
5871 result
.mPrincipal
= principal
.forget();
5872 result
.mIsWriteOnly
= false;
5877 nsLayoutUtils::SurfaceFromElementResult
5878 nsLayoutUtils::SurfaceFromElement(dom::Element
* aElement
,
5879 uint32_t aSurfaceFlags
,
5880 DrawTarget
* aTarget
)
5882 // If it's a <canvas>, we may be able to just grab its internal surface
5883 if (HTMLCanvasElement
* canvas
=
5884 HTMLCanvasElement::FromContentOrNull(aElement
)) {
5885 return SurfaceFromElement(canvas
, aSurfaceFlags
, aTarget
);
5888 // Maybe it's <video>?
5889 if (HTMLVideoElement
* video
=
5890 HTMLVideoElement::FromContentOrNull(aElement
)) {
5891 return SurfaceFromElement(video
, aSurfaceFlags
, aTarget
);
5894 // Finally, check if it's a normal image
5895 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(aElement
);
5898 return SurfaceFromElementResult();
5901 return SurfaceFromElement(imageLoader
, aSurfaceFlags
, aTarget
);
5906 nsLayoutUtils::GetEditableRootContentByContentEditable(nsIDocument
* aDocument
)
5908 // If the document is in designMode we should return nullptr.
5909 if (!aDocument
|| aDocument
->HasFlag(NODE_IS_EDITABLE
)) {
5913 // contenteditable only works with HTML document.
5914 // Note: Use nsIDOMHTMLDocument rather than nsIHTMLDocument for getting the
5915 // body node because nsIDOMHTMLDocument::GetBody() does something
5916 // additional work for some cases and nsEditor uses them.
5917 nsCOMPtr
<nsIDOMHTMLDocument
> domHTMLDoc
= do_QueryInterface(aDocument
);
5922 Element
* rootElement
= aDocument
->GetRootElement();
5923 if (rootElement
&& rootElement
->IsEditable()) {
5927 // If there are no editable root element, check its <body> element.
5928 // Note that the body element could be <frameset> element.
5929 nsCOMPtr
<nsIDOMHTMLElement
> body
;
5930 nsresult rv
= domHTMLDoc
->GetBody(getter_AddRefs(body
));
5931 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(body
);
5932 if (NS_SUCCEEDED(rv
) && content
&& content
->IsEditable()) {
5940 nsLayoutUtils::AssertNoDuplicateContinuations(nsIFrame
* aContainer
,
5941 const nsFrameList
& aFrameList
)
5943 for (nsIFrame
* f
= aFrameList
.FirstChild(); f
; f
= f
->GetNextSibling()) {
5944 // Check only later continuations of f; we deal with checking the
5945 // earlier continuations when we hit those earlier continuations in
5947 for (nsIFrame
*c
= f
; (c
= c
->GetNextInFlow());) {
5948 NS_ASSERTION(c
->GetParent() != aContainer
||
5949 !aFrameList
.ContainsFrame(c
),
5950 "Two continuations of the same frame in the same "
5956 // Is one of aFrame's ancestors a letter frame?
5958 IsInLetterFrame(nsIFrame
*aFrame
)
5960 for (nsIFrame
*f
= aFrame
->GetParent(); f
; f
= f
->GetParent()) {
5961 if (f
->GetType() == nsGkAtoms::letterFrame
) {
5969 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame
*aSubtreeRoot
)
5971 NS_ASSERTION(aSubtreeRoot
->GetPrevInFlow(),
5972 "frame tree not empty, but caller reported complete status");
5974 // Also assert that text frames map no text.
5976 nsresult rv
= aSubtreeRoot
->GetOffsets(start
, end
);
5977 NS_ASSERTION(NS_SUCCEEDED(rv
), "GetOffsets failed");
5978 // In some cases involving :first-letter, we'll partially unlink a
5979 // continuation in the middle of a continuation chain from its
5980 // previous and next continuations before destroying it, presumably so
5981 // that we don't also destroy the later continuations. Once we've
5982 // done this, GetOffsets returns incorrect values.
5983 // For examples, see list of tests in
5984 // https://bugzilla.mozilla.org/show_bug.cgi?id=619021#c29
5985 NS_ASSERTION(start
== end
|| IsInLetterFrame(aSubtreeRoot
),
5986 "frame tree not empty, but caller reported complete status");
5988 nsIFrame::ChildListIterator
lists(aSubtreeRoot
);
5989 for (; !lists
.IsDone(); lists
.Next()) {
5990 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
5991 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
5992 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(childFrames
.get());
5999 GetFontFacesForFramesInner(nsIFrame
* aFrame
, nsFontFaceList
* aFontFaceList
)
6001 NS_PRECONDITION(aFrame
, "NULL frame pointer");
6003 if (aFrame
->GetType() == nsGkAtoms::textFrame
) {
6004 if (!aFrame
->GetPrevContinuation()) {
6005 nsLayoutUtils::GetFontFacesForText(aFrame
, 0, INT32_MAX
, true,
6011 nsIFrame::ChildListID childLists
[] = { nsIFrame::kPrincipalList
,
6012 nsIFrame::kPopupList
};
6013 for (size_t i
= 0; i
< ArrayLength(childLists
); ++i
) {
6014 nsFrameList
children(aFrame
->GetChildList(childLists
[i
]));
6015 for (nsFrameList::Enumerator
e(children
); !e
.AtEnd(); e
.Next()) {
6016 nsIFrame
* child
= e
.get();
6017 child
= nsPlaceholderFrame::GetRealFrameFor(child
);
6018 GetFontFacesForFramesInner(child
, aFontFaceList
);
6025 nsLayoutUtils::GetFontFacesForFrames(nsIFrame
* aFrame
,
6026 nsFontFaceList
* aFontFaceList
)
6028 NS_PRECONDITION(aFrame
, "NULL frame pointer");
6031 GetFontFacesForFramesInner(aFrame
, aFontFaceList
);
6032 aFrame
= GetNextContinuationOrIBSplitSibling(aFrame
);
6040 nsLayoutUtils::GetFontFacesForText(nsIFrame
* aFrame
,
6041 int32_t aStartOffset
, int32_t aEndOffset
,
6042 bool aFollowContinuations
,
6043 nsFontFaceList
* aFontFaceList
)
6045 NS_PRECONDITION(aFrame
, "NULL frame pointer");
6047 if (aFrame
->GetType() != nsGkAtoms::textFrame
) {
6051 nsTextFrame
* curr
= static_cast<nsTextFrame
*>(aFrame
);
6053 int32_t fstart
= std::max(curr
->GetContentOffset(), aStartOffset
);
6054 int32_t fend
= std::min(curr
->GetContentEnd(), aEndOffset
);
6055 if (fstart
>= fend
) {
6056 curr
= static_cast<nsTextFrame
*>(curr
->GetNextContinuation());
6060 // curr is overlapping with the offset we want
6061 gfxSkipCharsIterator iter
= curr
->EnsureTextRun(nsTextFrame::eInflated
);
6062 gfxTextRun
* textRun
= curr
->GetTextRun(nsTextFrame::eInflated
);
6063 NS_ENSURE_TRUE(textRun
, NS_ERROR_OUT_OF_MEMORY
);
6065 // include continuations in the range that share the same textrun
6066 nsTextFrame
* next
= nullptr;
6067 if (aFollowContinuations
&& fend
< aEndOffset
) {
6068 next
= static_cast<nsTextFrame
*>(curr
->GetNextContinuation());
6069 while (next
&& next
->GetTextRun(nsTextFrame::eInflated
) == textRun
) {
6070 fend
= std::min(next
->GetContentEnd(), aEndOffset
);
6071 next
= fend
< aEndOffset
?
6072 static_cast<nsTextFrame
*>(next
->GetNextContinuation()) : nullptr;
6076 uint32_t skipStart
= iter
.ConvertOriginalToSkipped(fstart
);
6077 uint32_t skipEnd
= iter
.ConvertOriginalToSkipped(fend
);
6078 aFontFaceList
->AddFontsFromTextRun(textRun
, skipStart
, skipEnd
- skipStart
);
6080 } while (aFollowContinuations
&& curr
);
6087 nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame
* aFrame
,
6088 MallocSizeOf aMallocSizeOf
,
6091 NS_PRECONDITION(aFrame
, "NULL frame pointer");
6095 if (aFrame
->GetType() == nsGkAtoms::textFrame
) {
6096 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(aFrame
);
6097 for (uint32_t i
= 0; i
< 2; ++i
) {
6098 gfxTextRun
*run
= textFrame
->GetTextRun(
6099 (i
!= 0) ? nsTextFrame::eInflated
: nsTextFrame::eNotInflated
);
6102 run
->ResetSizeOfAccountingFlags();
6104 total
+= run
->MaybeSizeOfIncludingThis(aMallocSizeOf
);
6111 nsAutoTArray
<nsIFrame::ChildList
,4> childListArray
;
6112 aFrame
->GetChildLists(&childListArray
);
6114 for (nsIFrame::ChildListArrayIterator
childLists(childListArray
);
6115 !childLists
.IsDone(); childLists
.Next()) {
6116 for (nsFrameList::Enumerator
e(childLists
.CurrentList());
6117 !e
.AtEnd(); e
.Next()) {
6118 total
+= SizeOfTextRunsForFrames(e
.get(), aMallocSizeOf
, clear
);
6126 nsLayoutUtils::Initialize()
6128 Preferences::AddUintVarCache(&sFontSizeInflationMaxRatio
,
6129 "font.size.inflation.maxRatio");
6130 Preferences::AddUintVarCache(&sFontSizeInflationEmPerLine
,
6131 "font.size.inflation.emPerLine");
6132 Preferences::AddUintVarCache(&sFontSizeInflationMinTwips
,
6133 "font.size.inflation.minTwips");
6134 Preferences::AddUintVarCache(&sFontSizeInflationLineThreshold
,
6135 "font.size.inflation.lineThreshold");
6136 Preferences::AddIntVarCache(&sFontSizeInflationMappingIntercept
,
6137 "font.size.inflation.mappingIntercept");
6138 Preferences::AddBoolVarCache(&sFontSizeInflationForceEnabled
,
6139 "font.size.inflation.forceEnabled");
6140 Preferences::AddBoolVarCache(&sFontSizeInflationDisabledInMasterProcess
,
6141 "font.size.inflation.disabledInMasterProcess");
6142 Preferences::AddBoolVarCache(&sInvalidationDebuggingIsEnabled
,
6143 "nglayout.debug.invalidation");
6144 Preferences::AddBoolVarCache(&sCSSVariablesEnabled
,
6145 "layout.css.variables.enabled");
6146 Preferences::AddBoolVarCache(&sInterruptibleReflowEnabled
,
6147 "layout.interruptible-reflow.enabled");
6149 Preferences::RegisterCallback(GridEnabledPrefChangeCallback
,
6150 GRID_ENABLED_PREF_NAME
);
6151 GridEnabledPrefChangeCallback(GRID_ENABLED_PREF_NAME
, nullptr);
6152 Preferences::RegisterCallback(RubyEnabledPrefChangeCallback
,
6153 RUBY_ENABLED_PREF_NAME
);
6154 RubyEnabledPrefChangeCallback(RUBY_ENABLED_PREF_NAME
, nullptr);
6155 Preferences::RegisterCallback(StickyEnabledPrefChangeCallback
,
6156 STICKY_ENABLED_PREF_NAME
);
6157 StickyEnabledPrefChangeCallback(STICKY_ENABLED_PREF_NAME
, nullptr);
6158 Preferences::RegisterCallback(TextAlignTrueEnabledPrefChangeCallback
,
6159 TEXT_ALIGN_TRUE_ENABLED_PREF_NAME
);
6160 TextAlignTrueEnabledPrefChangeCallback(TEXT_ALIGN_TRUE_ENABLED_PREF_NAME
,
6163 nsComputedDOMStyle::RegisterPrefChangeCallbacks();
6168 nsLayoutUtils::Shutdown()
6172 sContentMap
= nullptr;
6175 Preferences::UnregisterCallback(GridEnabledPrefChangeCallback
,
6176 GRID_ENABLED_PREF_NAME
);
6177 Preferences::UnregisterCallback(RubyEnabledPrefChangeCallback
,
6178 RUBY_ENABLED_PREF_NAME
);
6179 Preferences::UnregisterCallback(StickyEnabledPrefChangeCallback
,
6180 STICKY_ENABLED_PREF_NAME
);
6182 nsComputedDOMStyle::UnregisterPrefChangeCallbacks();
6187 nsLayoutUtils::RegisterImageRequest(nsPresContext
* aPresContext
,
6188 imgIRequest
* aRequest
,
6189 bool* aRequestRegistered
)
6191 if (!aPresContext
) {
6195 if (aRequestRegistered
&& *aRequestRegistered
) {
6196 // Our request is already registered with the refresh driver, so
6197 // no need to register it again.
6202 if (!aPresContext
->RefreshDriver()->AddImageRequest(aRequest
)) {
6203 NS_WARNING("Unable to add image request");
6207 if (aRequestRegistered
) {
6208 *aRequestRegistered
= true;
6215 nsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext
* aPresContext
,
6216 imgIRequest
* aRequest
,
6217 bool* aRequestRegistered
)
6219 if (!aPresContext
) {
6223 if (aRequestRegistered
&& *aRequestRegistered
) {
6224 // Our request is already registered with the refresh driver, so
6225 // no need to register it again.
6230 nsCOMPtr
<imgIContainer
> image
;
6231 if (NS_SUCCEEDED(aRequest
->GetImage(getter_AddRefs(image
)))) {
6232 // Check to verify that the image is animated. If so, then add it to the
6233 // list of images tracked by the refresh driver.
6234 bool isAnimated
= false;
6235 nsresult rv
= image
->GetAnimated(&isAnimated
);
6236 if (NS_SUCCEEDED(rv
) && isAnimated
) {
6237 if (!aPresContext
->RefreshDriver()->AddImageRequest(aRequest
)) {
6238 NS_WARNING("Unable to add image request");
6242 if (aRequestRegistered
) {
6243 *aRequestRegistered
= true;
6252 nsLayoutUtils::DeregisterImageRequest(nsPresContext
* aPresContext
,
6253 imgIRequest
* aRequest
,
6254 bool* aRequestRegistered
)
6256 if (!aPresContext
) {
6260 // Deregister our imgIRequest with the refresh driver to
6261 // complete tear-down, but only if it has been registered
6262 if (aRequestRegistered
&& !*aRequestRegistered
) {
6267 nsCOMPtr
<imgIContainer
> image
;
6268 if (NS_SUCCEEDED(aRequest
->GetImage(getter_AddRefs(image
)))) {
6269 aPresContext
->RefreshDriver()->RemoveImageRequest(aRequest
);
6271 if (aRequestRegistered
) {
6272 *aRequestRegistered
= false;
6280 nsLayoutUtils::PostRestyleEvent(Element
* aElement
,
6281 nsRestyleHint aRestyleHint
,
6282 nsChangeHint aMinChangeHint
)
6284 nsIDocument
* doc
= aElement
->GetComposedDoc();
6286 nsCOMPtr
<nsIPresShell
> presShell
= doc
->GetShell();
6288 presShell
->GetPresContext()->RestyleManager()->PostRestyleEvent(
6289 aElement
, aRestyleHint
, aMinChangeHint
);
6294 nsSetAttrRunnable::nsSetAttrRunnable(nsIContent
* aContent
, nsIAtom
* aAttrName
,
6295 const nsAString
& aValue
)
6296 : mContent(aContent
),
6297 mAttrName(aAttrName
),
6300 NS_ASSERTION(aContent
&& aAttrName
, "Missing stuff, prepare to crash");
6303 nsSetAttrRunnable::nsSetAttrRunnable(nsIContent
* aContent
, nsIAtom
* aAttrName
,
6305 : mContent(aContent
),
6306 mAttrName(aAttrName
)
6308 NS_ASSERTION(aContent
&& aAttrName
, "Missing stuff, prepare to crash");
6309 mValue
.AppendInt(aValue
);
6313 nsSetAttrRunnable::Run()
6315 return mContent
->SetAttr(kNameSpaceID_None
, mAttrName
, mValue
, true);
6318 nsUnsetAttrRunnable::nsUnsetAttrRunnable(nsIContent
* aContent
,
6320 : mContent(aContent
),
6321 mAttrName(aAttrName
)
6323 NS_ASSERTION(aContent
&& aAttrName
, "Missing stuff, prepare to crash");
6327 nsUnsetAttrRunnable::Run()
6329 return mContent
->UnsetAttr(kNameSpaceID_None
, mAttrName
, true);
6333 * Compute the minimum font size inside of a container with the given
6334 * width, such that **when the user zooms the container to fill the full
6335 * width of the device**, the fonts satisfy our minima.
6338 MinimumFontSizeFor(nsPresContext
* aPresContext
, nscoord aContainerWidth
)
6340 nsIPresShell
* presShell
= aPresContext
->PresShell();
6342 uint32_t emPerLine
= presShell
->FontSizeInflationEmPerLine();
6343 uint32_t minTwips
= presShell
->FontSizeInflationMinTwips();
6344 if (emPerLine
== 0 && minTwips
== 0) {
6348 // Clamp the container width to the device dimensions
6349 nscoord iFrameWidth
= aPresContext
->GetVisibleArea().width
;
6350 nscoord effectiveContainerWidth
= std::min(iFrameWidth
, aContainerWidth
);
6352 nscoord byLine
= 0, byInch
= 0;
6353 if (emPerLine
!= 0) {
6354 byLine
= effectiveContainerWidth
/ emPerLine
;
6356 if (minTwips
!= 0) {
6357 // REVIEW: Is this giving us app units and sizes *not* counting
6358 // viewport scaling?
6359 float deviceWidthInches
=
6360 aPresContext
->ScreenWidthInchesForFontInflation();
6361 byInch
= NSToCoordRound(effectiveContainerWidth
/
6362 (deviceWidthInches
* 1440 /
6365 return std::max(byLine
, byInch
);
6369 nsLayoutUtils::FontSizeInflationInner(const nsIFrame
*aFrame
,
6370 nscoord aMinFontSize
)
6372 // Note that line heights should be inflated by the same ratio as the
6373 // font size of the same text; thus we operate only on the font size
6374 // even when we're scaling a line height.
6375 nscoord styleFontSize
= aFrame
->StyleFont()->mFont
.size
;
6376 if (styleFontSize
<= 0) {
6377 // Never scale zero font size.
6381 if (aMinFontSize
<= 0) {
6382 // No need to scale.
6386 // If between this current frame and its font inflation container there is a
6387 // non-inline element with fixed width or height, then we should not inflate
6388 // fonts for this frame.
6389 for (const nsIFrame
* f
= aFrame
;
6390 f
&& !f
->IsContainerForFontSizeInflation();
6391 f
= f
->GetParent()) {
6392 nsIContent
* content
= f
->GetContent();
6393 nsIAtom
* fType
= f
->GetType();
6394 // Also, if there is more than one frame corresponding to a single
6395 // content node, we want the outermost one.
6396 if (!(f
->GetParent() && f
->GetParent()->GetContent() == content
) &&
6397 // ignore width/height on inlines since they don't apply
6398 fType
!= nsGkAtoms::inlineFrame
&&
6399 // ignore width on radios and checkboxes since we enlarge them and
6400 // they have width/height in ua.css
6401 fType
!= nsGkAtoms::formControlFrame
) {
6402 nsStyleCoord stylePosWidth
= f
->StylePosition()->mWidth
;
6403 nsStyleCoord stylePosHeight
= f
->StylePosition()->mHeight
;
6404 if (stylePosWidth
.GetUnit() != eStyleUnit_Auto
||
6405 stylePosHeight
.GetUnit() != eStyleUnit_Auto
) {
6412 int32_t interceptParam
= nsLayoutUtils::FontSizeInflationMappingIntercept();
6413 float maxRatio
= (float)nsLayoutUtils::FontSizeInflationMaxRatio() / 100.0f
;
6415 float ratio
= float(styleFontSize
) / float(aMinFontSize
);
6416 float inflationRatio
;
6418 // Given a minimum inflated font size m, a specified font size s, we want to
6419 // find the inflated font size i and then return the ratio of i to s (i/s).
6420 if (interceptParam
>= 0) {
6421 // Since the mapping intercept parameter P is greater than zero, we use it
6422 // to determine the point where our mapping function intersects the i=s
6423 // line. This means that we have an equation of the form:
6425 // i = m + s·(P/2)/(1 + P/2), if s <= (1 + P/2)·m
6426 // i = s, if s >= (1 + P/2)·m
6428 float intercept
= 1 + float(interceptParam
)/2.0f
;
6429 if (ratio
>= intercept
) {
6430 // If we're already at 1+P/2 or more times the minimum, don't scale.
6434 // The point (intercept, intercept) is where the part of the i vs. s graph
6435 // that's not slope 1 meets the i=s line. (This part of the
6436 // graph is a line from (0, m), to that point). We calculate the
6437 // intersection point to be ((1+P/2)m, (1+P/2)m), where P is the
6438 // intercept parameter above. We then need to return i/s.
6439 inflationRatio
= (1.0f
+ (ratio
* (intercept
- 1) / intercept
)) / ratio
;
6441 // This is the case where P is negative. We essentially want to implement
6442 // the case for P=infinity here, so we make i = s + m, which means that
6443 // i/s = s/s + m/s = 1 + 1/ratio
6444 inflationRatio
= 1 + 1.0f
/ ratio
;
6447 if (maxRatio
> 1.0 && inflationRatio
> maxRatio
) {
6450 return inflationRatio
;
6455 ShouldInflateFontsForContainer(const nsIFrame
*aFrame
)
6457 // We only want to inflate fonts for text that is in a place
6458 // with room to expand. The question is what the best heuristic for
6460 // For now, we're going to use NS_FRAME_IN_CONSTRAINED_HEIGHT, which
6461 // indicates whether the frame is inside something with a constrained
6462 // height (propagating down the tree), but the propagation stops when
6463 // we hit overflow-y: scroll or auto.
6464 const nsStyleText
* styleText
= aFrame
->StyleText();
6466 return styleText
->mTextSizeAdjust
!= NS_STYLE_TEXT_SIZE_ADJUST_NONE
&&
6467 !(aFrame
->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT
) &&
6468 // We also want to disable font inflation for containers that have
6469 // preformatted text.
6470 styleText
->WhiteSpaceCanWrap(aFrame
);
6474 nsLayoutUtils::InflationMinFontSizeFor(const nsIFrame
*aFrame
)
6476 nsPresContext
*presContext
= aFrame
->PresContext();
6477 if (!FontSizeInflationEnabled(presContext
) ||
6478 presContext
->mInflationDisabledForShrinkWrap
) {
6482 for (const nsIFrame
*f
= aFrame
; f
; f
= f
->GetParent()) {
6483 if (f
->IsContainerForFontSizeInflation()) {
6484 if (!ShouldInflateFontsForContainer(f
)) {
6488 nsFontInflationData
*data
=
6489 nsFontInflationData::FindFontInflationDataFor(aFrame
);
6490 // FIXME: The need to null-check here is sort of a bug, and might
6491 // lead to incorrect results.
6492 if (!data
|| !data
->InflationEnabled()) {
6496 return MinimumFontSizeFor(aFrame
->PresContext(),
6497 data
->EffectiveWidth());
6501 NS_ABORT_IF_FALSE(false, "root should always be container");
6507 nsLayoutUtils::FontSizeInflationFor(const nsIFrame
*aFrame
)
6509 if (aFrame
->IsSVGText()) {
6510 const nsIFrame
* container
= aFrame
;
6511 while (container
->GetType() != nsGkAtoms::svgTextFrame
) {
6512 container
= container
->GetParent();
6514 NS_ASSERTION(container
, "expected to find an ancestor SVGTextFrame");
6516 static_cast<const SVGTextFrame
*>(container
)->GetFontSizeScaleFactor();
6519 if (!FontSizeInflationEnabled(aFrame
->PresContext())) {
6523 return FontSizeInflationInner(aFrame
, InflationMinFontSizeFor(aFrame
));
6527 nsLayoutUtils::FontSizeInflationEnabled(nsPresContext
*aPresContext
)
6529 nsIPresShell
* presShell
= aPresContext
->GetPresShell();
6535 return presShell
->FontSizeInflationEnabled();
6539 nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame
* aFrame
,
6540 const nsSize
& aFrameSize
)
6542 nsCSSShadowArray
* boxShadows
= aFrame
->StyleBorder()->mBoxShadow
;
6548 int32_t A2D
= aFrame
->PresContext()->AppUnitsPerDevPixel();
6549 for (uint32_t i
= 0; i
< boxShadows
->Length(); ++i
) {
6550 nsRect
tmpRect(nsPoint(0, 0), aFrameSize
);
6551 nsCSSShadowItem
* shadow
= boxShadows
->ShadowAt(i
);
6553 // inset shadows are never painted outside the frame
6557 tmpRect
.MoveBy(nsPoint(shadow
->mXOffset
, shadow
->mYOffset
));
6558 tmpRect
.Inflate(shadow
->mSpread
);
6560 nsContextBoxBlur::GetBlurRadiusMargin(shadow
->mRadius
, A2D
));
6561 shadows
.UnionRect(shadows
, tmpRect
);
6567 nsLayoutUtils::UpdateImageVisibilityForFrame(nsIFrame
* aImageFrame
)
6570 nsIAtom
* type
= aImageFrame
->GetType();
6571 MOZ_ASSERT(type
== nsGkAtoms::imageFrame
||
6572 type
== nsGkAtoms::imageControlFrame
||
6573 type
== nsGkAtoms::svgImageFrame
, "wrong type of frame");
6576 nsCOMPtr
<nsIImageLoadingContent
> content
= do_QueryInterface(aImageFrame
->GetContent());
6581 nsIPresShell
* presShell
= aImageFrame
->PresContext()->PresShell();
6582 if (presShell
->AssumeAllImagesVisible()) {
6583 presShell
->EnsureImageInVisibleList(content
);
6587 bool visible
= true;
6588 nsIFrame
* f
= aImageFrame
->GetParent();
6589 nsRect rect
= aImageFrame
->GetContentRectRelativeToSelf();
6590 nsIFrame
* rectFrame
= aImageFrame
;
6592 nsIScrollableFrame
* sf
= do_QueryFrame(f
);
6594 nsRect transformedRect
=
6595 nsLayoutUtils::TransformFrameRectToAncestor(rectFrame
, rect
, f
);
6596 if (!sf
->IsRectNearlyVisible(transformedRect
)) {
6600 // Move transformedRect to be contained in the scrollport as best we can
6601 // (it might not fit) to pretend that it was scrolled into view.
6602 nsRect scrollPort
= sf
->GetScrollPortRect();
6603 if (transformedRect
.XMost() > scrollPort
.XMost()) {
6604 transformedRect
.x
-= transformedRect
.XMost() - scrollPort
.XMost();
6606 if (transformedRect
.x
< scrollPort
.x
) {
6607 transformedRect
.x
= scrollPort
.x
;
6609 if (transformedRect
.YMost() > scrollPort
.YMost()) {
6610 transformedRect
.y
-= transformedRect
.YMost() - scrollPort
.YMost();
6612 if (transformedRect
.y
< scrollPort
.y
) {
6613 transformedRect
.y
= scrollPort
.y
;
6615 transformedRect
.width
= std::min(transformedRect
.width
, scrollPort
.width
);
6616 transformedRect
.height
= std::min(transformedRect
.height
, scrollPort
.height
);
6617 rect
= transformedRect
;
6620 nsIFrame
* parent
= f
->GetParent();
6622 parent
= nsLayoutUtils::GetCrossDocParentFrame(f
);
6623 if (parent
&& parent
->PresContext()->IsChrome()) {
6631 presShell
->EnsureImageInVisibleList(content
);
6633 presShell
->RemoveImageFromVisibleList(content
);
6638 nsLayoutUtils::GetContentViewerBounds(nsPresContext
* aPresContext
,
6639 LayoutDeviceIntRect
& aOutRect
)
6641 nsCOMPtr
<nsIDocShell
> docShell
= aPresContext
->GetDocShell();
6646 nsCOMPtr
<nsIContentViewer
> cv
;
6647 docShell
->GetContentViewer(getter_AddRefs(cv
));
6653 cv
->GetBounds(bounds
);
6654 aOutRect
= LayoutDeviceIntRect::FromUntyped(bounds
);
6659 nsLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame
* aFrame
)
6661 nsSize
size(aFrame
->GetSize());
6663 nsPresContext
* presContext
= aFrame
->PresContext();
6664 nsIPresShell
* presShell
= presContext
->PresShell();
6666 // See the comments in the code that calculates the root
6667 // composition bounds in RecordFrameMetrics.
6668 // TODO: Reuse that code here.
6669 bool isRootContentDocRootScrollFrame
= presContext
->IsRootContentDocument()
6670 && aFrame
== presShell
->GetRootScrollFrame();
6671 if (isRootContentDocRootScrollFrame
) {
6672 if (nsIFrame
* rootFrame
= presShell
->GetRootFrame()) {
6673 #ifdef MOZ_WIDGET_ANDROID
6674 nsIWidget
* widget
= rootFrame
->GetNearestWidget();
6676 nsView
* view
= rootFrame
->GetView();
6677 nsIWidget
* widget
= view
? view
->GetWidget() : nullptr;
6679 int32_t auPerDevPixel
= presContext
->AppUnitsPerDevPixel();
6681 nsIntRect widgetBounds
;
6682 widget
->GetBounds(widgetBounds
);
6683 size
= nsSize(widgetBounds
.width
* auPerDevPixel
,
6684 widgetBounds
.height
* auPerDevPixel
);
6685 #ifdef MOZ_WIDGET_ANDROID
6686 nsSize frameSize
= aFrame
->GetSize();
6687 if (frameSize
.height
< size
.height
) {
6688 size
.height
= frameSize
.height
;
6692 LayoutDeviceIntRect contentBounds
;
6693 if (nsLayoutUtils::GetContentViewerBounds(presContext
, contentBounds
)) {
6694 size
= LayoutDevicePixel::ToAppUnits(contentBounds
.Size(), auPerDevPixel
);
6700 // Adjust composition bounds for the size of scroll bars.
6701 nsIScrollableFrame
* scrollableFrame
= aFrame
->GetScrollTargetFrame();
6702 if (scrollableFrame
&& !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars
)) {
6703 nsMargin margins
= scrollableFrame
->GetActualScrollbarSizes();
6704 size
.width
-= margins
.LeftRight();
6705 size
.height
-= margins
.TopBottom();
6711 /* static */ CSSSize
6712 nsLayoutUtils::CalculateRootCompositionSize(nsIFrame
* aFrame
,
6713 bool aIsRootContentDocRootScrollFrame
,
6714 const FrameMetrics
& aMetrics
)
6717 if (aIsRootContentDocRootScrollFrame
) {
6718 return ViewAs
<LayerPixel
>(aMetrics
.mCompositionBounds
.Size(),
6719 PixelCastJustification::ParentLayerToLayerForRootComposition
)
6720 / aMetrics
.LayersPixelsPerCSSPixel();
6722 nsPresContext
* presContext
= aFrame
->PresContext();
6723 LayerSize rootCompositionSize
;
6724 nsPresContext
* rootPresContext
=
6725 presContext
->GetToplevelContentDocumentPresContext();
6726 if (!rootPresContext
) {
6727 rootPresContext
= presContext
->GetRootPresContext();
6729 nsIPresShell
* rootPresShell
= nullptr;
6730 if (rootPresContext
) {
6731 // See the comments in the code that calculates the root
6732 // composition bounds in RecordFrameMetrics.
6733 // TODO: Reuse that code here.
6734 nsIPresShell
* rootPresShell
= rootPresContext
->PresShell();
6735 if (nsIFrame
* rootFrame
= rootPresShell
->GetRootFrame()) {
6736 LayoutDeviceToParentLayerScale
parentResolution(
6737 rootPresShell
->GetCumulativeResolution().width
6738 / rootPresShell
->GetResolution().width
);
6739 int32_t rootAUPerDevPixel
= rootPresContext
->AppUnitsPerDevPixel();
6740 LayerSize frameSize
= ViewAs
<LayerPixel
>(
6741 (LayoutDeviceRect::FromAppUnits(rootFrame
->GetRect(), rootAUPerDevPixel
)
6742 * parentResolution
).Size(), PixelCastJustification::ParentLayerToLayerForRootComposition
);
6743 rootCompositionSize
= frameSize
;
6744 #ifdef MOZ_WIDGET_ANDROID
6745 nsIWidget
* widget
= rootFrame
->GetNearestWidget();
6747 nsView
* view
= rootFrame
->GetView();
6748 nsIWidget
* widget
= view
? view
->GetWidget() : nullptr;
6751 nsIntRect widgetBounds
;
6752 widget
->GetBounds(widgetBounds
);
6753 rootCompositionSize
= LayerSize(ViewAs
<LayerPixel
>(widgetBounds
.Size()));
6754 #ifdef MOZ_WIDGET_ANDROID
6755 if (frameSize
.height
< rootCompositionSize
.height
) {
6756 rootCompositionSize
.height
= frameSize
.height
;
6760 LayoutDeviceIntRect contentBounds
;
6761 if (nsLayoutUtils::GetContentViewerBounds(rootPresContext
, contentBounds
)) {
6762 LayoutDeviceToLayerScale
scale(1.0f
);
6763 if (rootPresContext
->GetParentPresContext()) {
6764 gfxSize res
= rootPresContext
->GetParentPresContext()->PresShell()->GetCumulativeResolution();
6765 scale
= LayoutDeviceToLayerScale(res
.width
, res
.height
);
6767 rootCompositionSize
= contentBounds
.Size() * scale
;
6772 nsIWidget
* widget
= aFrame
->GetNearestWidget();
6773 nsIntRect widgetBounds
;
6774 widget
->GetBounds(widgetBounds
);
6775 rootCompositionSize
= LayerSize(ViewAs
<LayerPixel
>(widgetBounds
.Size()));
6778 // Adjust composition size for the size of scroll bars.
6779 nsIFrame
* rootRootScrollFrame
= rootPresShell
? rootPresShell
->GetRootScrollFrame() : nullptr;
6780 nsIScrollableFrame
* rootScrollableFrame
= nullptr;
6781 if (rootRootScrollFrame
) {
6782 rootScrollableFrame
= rootRootScrollFrame
->GetScrollTargetFrame();
6784 if (rootScrollableFrame
&& !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars
)) {
6785 CSSMargin margins
= CSSMargin::FromAppUnits(rootScrollableFrame
->GetActualScrollbarSizes());
6786 // Scrollbars are not subject to scaling, so CSS pixels = layer pixels for them.
6787 rootCompositionSize
.width
-= margins
.LeftRight();
6788 rootCompositionSize
.height
-= margins
.TopBottom();
6791 return rootCompositionSize
/ aMetrics
.LayersPixelsPerCSSPixel();
6795 nsLayoutUtils::CalculateScrollableRectForFrame(nsIScrollableFrame
* aScrollableFrame
, nsIFrame
* aRootFrame
)
6797 nsRect contentBounds
;
6798 if (aScrollableFrame
) {
6799 contentBounds
= aScrollableFrame
->GetScrollRange();
6801 // We ifndef the below code for Fennec because it requires special behaviour
6802 // on the APZC side. Because Fennec has it's own PZC implementation which doesn't
6803 // provide the special behaviour, this code will cause it to break. We can remove
6804 // the ifndef once Fennec switches over to APZ or if we add the special handling
6806 #if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ)
6807 nsPoint scrollPosition
= aScrollableFrame
->GetScrollPosition();
6808 if (aScrollableFrame
->GetScrollbarStyles().mVertical
== NS_STYLE_OVERFLOW_HIDDEN
) {
6809 contentBounds
.y
= scrollPosition
.y
;
6810 contentBounds
.height
= 0;
6812 if (aScrollableFrame
->GetScrollbarStyles().mHorizontal
== NS_STYLE_OVERFLOW_HIDDEN
) {
6813 contentBounds
.x
= scrollPosition
.x
;
6814 contentBounds
.width
= 0;
6818 contentBounds
.width
+= aScrollableFrame
->GetScrollPortRect().width
;
6819 contentBounds
.height
+= aScrollableFrame
->GetScrollPortRect().height
;
6821 contentBounds
= aRootFrame
->GetRect();
6823 return contentBounds
;
6827 nsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame
* aFrame
)
6829 nsRect scrollableRect
=
6830 CalculateScrollableRectForFrame(aFrame
->GetScrollTargetFrame(),
6831 aFrame
->PresContext()->PresShell()->GetRootFrame());
6832 nsSize compSize
= CalculateCompositionSizeForFrame(aFrame
);
6834 if (aFrame
== aFrame
->PresContext()->PresShell()->GetRootScrollFrame()) {
6835 // the composition size for the root scroll frame does not include the
6836 // local resolution, so we adjust.
6837 gfxSize res
= aFrame
->PresContext()->PresShell()->GetResolution();
6838 compSize
.width
= NSToCoordRound(compSize
.width
/ ((float) res
.width
));
6839 compSize
.height
= NSToCoordRound(compSize
.height
/ ((float) res
.height
));
6842 if (scrollableRect
.width
< compSize
.width
) {
6843 scrollableRect
.x
= std::max(0,
6844 scrollableRect
.x
- (compSize
.width
- scrollableRect
.width
));
6845 scrollableRect
.width
= compSize
.width
;
6848 if (scrollableRect
.height
< compSize
.height
) {
6849 scrollableRect
.y
= std::max(0,
6850 scrollableRect
.y
- (compSize
.height
- scrollableRect
.height
));
6851 scrollableRect
.height
= compSize
.height
;
6853 return scrollableRect
;
6857 nsLayoutUtils::WantSubAPZC()
6859 // TODO Turn this on for inprocess OMTC on all platforms
6860 bool wantSubAPZC
= gfxPrefs::AsyncPanZoomEnabled() &&
6861 gfxPrefs::APZSubframeEnabled();
6862 #ifdef MOZ_WIDGET_GONK
6863 if (XRE_GetProcessType() != GeckoProcessType_Content
) {
6864 wantSubAPZC
= false;
6871 nsLayoutUtils::UsesAsyncScrolling()
6873 #ifdef MOZ_WIDGET_ANDROID
6874 // We always have async scrolling for android
6878 return gfxPrefs::AsyncPanZoomEnabled();
6882 nsLayoutUtils::DoLogTestDataForPaint(LayerManager
* aManager
,
6884 const std::string
& aKey
,
6885 const std::string
& aValue
)
6887 if (aManager
->GetBackendType() == LayersBackend::LAYERS_CLIENT
) {
6888 static_cast<ClientLayerManager
*>(aManager
)->LogTestDataForCurrentPaint(aScrollId
, aKey
, aValue
);
6893 nsLayoutUtils::IsAPZTestLoggingEnabled()
6895 return gfxPrefs::APZTestLoggingEnabled();
6898 nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult()
6899 // Use safe default values here
6900 : mIsWriteOnly(true)
6901 , mIsStillLoading(false)
6903 , mIsPremultiplied(true)
6908 nsLayoutUtils::IsNonWrapperBlock(nsIFrame
* aFrame
)
6910 return GetAsBlock(aFrame
) && !aFrame
->IsBlockWrapper();
6914 nsLayoutUtils::NeedsPrintPreviewBackground(nsPresContext
* aPresContext
)
6916 return aPresContext
->IsRootPaginatedDocument() &&
6917 (aPresContext
->Type() == nsPresContext::eContext_PrintPreview
||
6918 aPresContext
->Type() == nsPresContext::eContext_PageLayout
);
6921 AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame
*aFrame
)
6923 // FIXME: Now that inflation calculations are based on the flow
6924 // root's NCA's (nearest common ancestor of its inflatable
6925 // descendants) width, we could probably disable inflation in
6926 // fewer cases than we currently do.
6927 if (aFrame
->IsContainerForFontSizeInflation()) {
6928 mPresContext
= aFrame
->PresContext();
6929 mOldValue
= mPresContext
->mInflationDisabledForShrinkWrap
;
6930 mPresContext
->mInflationDisabledForShrinkWrap
= true;
6932 // indicate we have nothing to restore
6933 mPresContext
= nullptr;
6937 AutoMaybeDisableFontInflation::~AutoMaybeDisableFontInflation()
6940 mPresContext
->mInflationDisabledForShrinkWrap
= mOldValue
;
6948 MaybeSetupTransactionIdAllocator(layers::LayerManager
* aManager
, nsView
* aView
)
6950 if (aManager
->GetBackendType() == layers::LayersBackend::LAYERS_CLIENT
) {
6951 layers::ClientLayerManager
*manager
= static_cast<layers::ClientLayerManager
*>(aManager
);
6952 nsRefreshDriver
*refresh
= aView
->GetViewManager()->GetPresShell()->GetPresContext()->RefreshDriver();
6953 manager
->SetTransactionIdAllocator(refresh
);
6961 nsLayoutUtils::IsOutlineStyleAutoEnabled()
6963 static bool sOutlineStyleAutoEnabled
;
6964 static bool sOutlineStyleAutoPrefCached
= false;
6966 if (!sOutlineStyleAutoPrefCached
) {
6967 sOutlineStyleAutoPrefCached
= true;
6968 Preferences::AddBoolVarCache(&sOutlineStyleAutoEnabled
,
6969 "layout.css.outline-style-auto.enabled",
6972 return sOutlineStyleAutoEnabled
;
6976 nsLayoutUtils::SetBSizeFromFontMetrics(const nsIFrame
* aFrame
,
6977 nsHTMLReflowMetrics
& aMetrics
,
6978 const nsHTMLReflowState
& aReflowState
,
6979 LogicalMargin aFramePadding
,
6980 WritingMode aLineWM
,
6981 WritingMode aFrameWM
)
6983 nsRefPtr
<nsFontMetrics
> fm
;
6984 float inflation
= nsLayoutUtils::FontSizeInflationFor(aFrame
);
6985 nsLayoutUtils::GetFontMetricsForFrame(aFrame
, getter_AddRefs(fm
), inflation
);
6986 aReflowState
.rendContext
->SetFont(fm
);
6989 // Compute final height of the frame.
6991 // Do things the standard css2 way -- though it's hard to find it
6992 // in the css2 spec! It's actually found in the css1 spec section
6993 // 4.4 (you will have to read between the lines to really see
6996 // The height of our box is the sum of our font size plus the top
6997 // and bottom border and padding. The height of children do not
6998 // affect our height.
6999 aMetrics
.SetBlockStartAscent(fm
->MaxAscent());
7000 aMetrics
.BSize(aLineWM
) = fm
->MaxHeight();
7002 NS_WARNING("Cannot get font metrics - defaulting sizes to 0");
7003 aMetrics
.SetBlockStartAscent(aMetrics
.BSize(aLineWM
) = 0);
7005 aMetrics
.SetBlockStartAscent(aMetrics
.BlockStartAscent() +
7006 aFramePadding
.BStart(aFrameWM
));
7007 aMetrics
.BSize(aLineWM
) +=
7008 aReflowState
.ComputedLogicalBorderPadding().BStartEnd(aFrameWM
);